- 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

View File

@ -7,8 +7,8 @@ android {
applicationId = 'com.tutpro.baresip'
minSdkVersion 21
targetSdkVersion 27
versionCode = 33
versionName = '4.0.0'
versionCode = 34
versionName = '4.1.0'
externalNativeBuild {
cmake {
cFlags '-DHAVE_INTTYPES_H'

View File

@ -700,7 +700,7 @@ Java_com_tutpro_baresip_AccountKt_account_1sipnat(JNIEnv *env, jobject thiz, jst
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1sipnat(JNIEnv *env, jobject thiz,
jstring javaAcc, jstring javaSipNat) {
jstring javaAcc, jstring javaSipNat) {
const char *native_acc = (*env)->GetStringUTFChars(env, javaAcc, 0);
struct account *acc = (struct account *)strtoul(native_acc, NULL, 10);
(*env)->ReleaseStringUTFChars(env, javaAcc, native_acc);
@ -716,7 +716,7 @@ Java_com_tutpro_baresip_AccountKt_account_1set_1sipnat(JNIEnv *env, jobject thiz
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_AccountKt_account_1set_1mwi(JNIEnv *env, jobject thiz,
jstring javaAcc, jstring javaValue) {
jstring javaAcc, jstring javaValue) {
const char *native_acc = (*env)->GetStringUTFChars(env, javaAcc, 0);
struct account *acc = (struct account *) strtoul(native_acc, NULL, 10);
(*env)->ReleaseStringUTFChars(env, javaAcc, native_acc);
@ -748,8 +748,7 @@ Java_com_tutpro_baresip_AccountKt_account_1vm_1uri(JNIEnv *env, jobject thiz, js
}
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1alloc(JNIEnv *env, jobject thiz,
jstring javaUri) {
Java_com_tutpro_baresip_Api_ua_1alloc(JNIEnv *env, jobject thiz, jstring javaUri) {
const char *uri = (*env)->GetStringUTFChars(env, javaUri, 0);
struct ua *ua;
LOGD("allocating UA '%s'\n", uri);
@ -762,8 +761,7 @@ Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1alloc(JNIEnv *env, jobject
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1register(JNIEnv *env, jobject thiz,
jstring javaUA)
Java_com_tutpro_baresip_Api_ua_1register(JNIEnv *env, jobject thiz, jstring javaUA)
{
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
@ -772,8 +770,7 @@ Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1register(JNIEnv *env, jobje
}
JNIEXPORT void JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1unregister(JNIEnv *env, jobject thiz,
jstring javaUA)
Java_com_tutpro_baresip_Api_ua_1unregister(JNIEnv *env, jobject thiz, jstring javaUA)
{
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
@ -782,8 +779,7 @@ Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1unregister(JNIEnv *env, job
}
JNIEXPORT jboolean JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1isregistered(JNIEnv *env, jobject thiz,
jstring javaUA)
Java_com_tutpro_baresip_Api_ua_1isregistered(JNIEnv *env, jobject thiz, jstring javaUA)
{
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
@ -792,8 +788,7 @@ Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1isregistered(JNIEnv *env, j
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1update_1account(JNIEnv *env, jobject thiz,
jstring javaUA)
Java_com_tutpro_baresip_Api_ua_1update_1account(JNIEnv *env, jobject thiz, jstring javaUA)
{
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
@ -802,8 +797,7 @@ Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1update_1account(JNIEnv *env
}
JNIEXPORT void JNICALL
Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1destroy(JNIEnv *env, jobject thiz,
jstring javaUA)
Java_com_tutpro_baresip_Api_ua_1destroy(JNIEnv *env, jobject thiz, jstring javaUA)
{
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
@ -812,7 +806,7 @@ Java_com_tutpro_baresip_UserAgent_00024Companion_ua_1destroy(JNIEnv *env, jobjec
}
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);
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
Java_com_tutpro_baresip_MainActivity_uag_1current_1set(JNIEnv *env, jobject thiz,
jstring javaUA)
Java_com_tutpro_baresip_Api_uag_1current_1set(JNIEnv *env, jobject thiz, jstring javaUA)
{
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
@ -849,9 +842,47 @@ Java_com_tutpro_baresip_Api_call_1peeruri(JNIEnv *env, jobject thiz, jstring jav
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
Java_com_tutpro_baresip_MainActivity_ua_1connect(JNIEnv *env, jobject thiz,
jstring javaUA, jstring javaURI) {
Java_com_tutpro_baresip_Api_ua_1connect(JNIEnv *env, jobject thiz, jstring javaUA,
jstring javaURI) {
struct call *call;
struct ua *ua;
int err;
@ -875,8 +906,8 @@ Java_com_tutpro_baresip_MainActivity_ua_1connect(JNIEnv *env, jobject thiz,
}
JNIEXPORT jstring JNICALL
Java_com_tutpro_baresip_MainActivity_ua_1call_1alloc(JNIEnv *env, jobject thiz,
jstring javaUA, jstring javaXCall) {
Java_com_tutpro_baresip_Api_ua_1call_1alloc(JNIEnv *env, jobject thiz, jstring javaUA,
jstring javaXCall) {
struct call *xcall, *call = NULL;
struct ua *ua;
int err;
@ -901,8 +932,8 @@ Java_com_tutpro_baresip_MainActivity_ua_1call_1alloc(JNIEnv *env, jobject thiz,
}
JNIEXPORT void JNICALL
Java_com_tutpro_baresip_MainActivity_ua_1answer(JNIEnv *env, jobject thiz,
jstring javaUA, jstring javaCall) {
Java_com_tutpro_baresip_Api_ua_1answer(JNIEnv *env, jobject thiz, jstring javaUA,
jstring javaCall) {
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
LOGD("answering call %s/%s\n", native_ua, native_call);
@ -918,8 +949,8 @@ Java_com_tutpro_baresip_MainActivity_ua_1answer(JNIEnv *env, jobject thiz,
}
JNIEXPORT jint JNICALL
Java_com_tutpro_baresip_MainActivity_call_1connect(JNIEnv *env, jobject thiz, jstring javaCall,
jstring javaPeer) {
Java_com_tutpro_baresip_Api_call_1connect(JNIEnv *env, jobject thiz, jstring javaCall,
jstring javaPeer) {
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
const char *native_peer = (*env)->GetStringUTFChars(env, javaPeer, 0);
struct call *call = (struct call *)strtoul(native_call, NULL, 10);
@ -936,7 +967,23 @@ Java_com_tutpro_baresip_MainActivity_call_1connect(JNIEnv *env, jobject thiz, js
}
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);
struct call *call = (struct call *)strtoul(native_call, NULL, 10);
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
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);
struct call *call = (struct call *)strtoul(native_call, NULL, 10);
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
Java_com_tutpro_baresip_MainActivity_call_1hold(JNIEnv *env, jobject thiz,
jstring javaCall) {
Java_com_tutpro_baresip_Api_call_1hold(JNIEnv *env, jobject thiz, jstring javaCall) {
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
struct call *call = (struct call *) strtoul(native_call, NULL, 10);
int ret;
@ -979,7 +1025,7 @@ Java_com_tutpro_baresip_MainActivity_call_1hold(JNIEnv *env, jobject thiz,
}
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);
struct call *call = (struct call *) strtoul(native_call, NULL, 10);
int ret;
@ -994,48 +1040,9 @@ Java_com_tutpro_baresip_MainActivity_call_1unhold(JNIEnv *env, jobject thiz, jst
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
Java_com_tutpro_baresip_MainActivity_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 jint JNICALL
Java_com_tutpro_baresip_ChatActivity_message_1send(JNIEnv *env, jobject thiz, jstring javaUA,
jstring javaPeer, jstring javaMsg,
jstring javaTime) {
Java_com_tutpro_baresip_Api_message_1send(JNIEnv *env, jobject thiz, jstring javaUA,
jstring javaPeer, jstring javaMsg, jstring javaTime) {
struct ua *ua;
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 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;
}
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
Java_com_tutpro_baresip_MainActivity_reload_1config(JNIEnv *env, jobject thiz) {
Java_com_tutpro_baresip_Api_reload_1config(JNIEnv *env, jobject thiz) {
int err;
err = conf_configure();
if (err) {

View File

@ -116,28 +116,28 @@ class Account(val accp: String) {
fun accounts(): ArrayList<Account> {
val res = ArrayList<Account>()
for (ua in MainActivity.uas) {
for (ua in UserAgent.uas()) {
res.add(ua.account)
}
return res
}
fun find(uas: ArrayList<UserAgent>, accp: String): Account? {
for (ua in uas) {
fun find(accp: String): Account? {
for (ua in UserAgent.uas()) {
if (ua.account.accp == accp) return ua.account
}
return null
}
fun findUa(aor: String): UserAgent? {
for (ua in MainActivity.uas) {
for (ua in UserAgent.uas()) {
if (ua.account.aor == aor) return ua
}
return null
}
fun exists(uas: ArrayList<UserAgent>, aor: String): Boolean {
for (ua in uas) {
fun exists(aor: String): Boolean {
for (ua in UserAgent.uas()) {
if (ua.account.aor == aor) return true
}
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_outbound(acc: String, ix: Int): String
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_audio_codec(acc: String, ix: Int): String
external fun account_regint(acc: String): Int

View File

@ -32,9 +32,9 @@ class AccountActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_account)
acc = Account.find(MainActivity.uas, intent.extras.getString("accp"))!!
acc = Account.find(intent.extras.getString("accp"))!!
aor = acc.aor
uaIndex = UserAgent.findAorIndex(MainActivity.uas, aor)!!
uaIndex = UserAgent.findAorIndex(UserAgent.uas(), aor)!!
setTitle(aor.replace("sip:", ""))
@ -220,7 +220,7 @@ class AccountActivity : AppCompatActivity() {
if (acc.regint != 3600) newRegint = 3600
} else {
if (acc.regint != 0) {
UserAgent.ua_unregister(MainActivity.uas[uaIndex].uap)
Api.ua_unregister(UserAgent.uas()[uaIndex].uap)
newRegint = 0
}
}
@ -285,20 +285,18 @@ class AccountActivity : AppCompatActivity() {
if (save) {
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")
}
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()
return true
} else if (item.itemId == android.R.id.home) {
Log.d("Baresip", "Back array was pressed at Account")
setResult(RESULT_CANCELED, intent)
finish()
return true

View File

@ -1,5 +1,6 @@
package com.tutpro.baresip
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
@ -28,22 +29,21 @@ class AccountListAdapter(private val cxt: Context, private val rows: ArrayList<A
aorView.setOnClickListener { _ ->
val i = Intent(cxt, AccountActivity::class.java)
val b = Bundle()
b.putString("accp", MainActivity.uas[position].account.accp)
b.putString("accp", UserAgent.uas()[position].account.accp)
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
actionView.setImageResource(row.action)
actionView.setOnClickListener { _ ->
Log.d("Baresip", "Delete button clicked")
val deleteDialog = AlertDialog.Builder(cxt)
deleteDialog.setMessage("Do you want to delete account ${MainActivity.uas[position].account.aor}?")
deleteDialog.setMessage("Do you want to delete account ${ua.account.aor}?")
deleteDialog.setPositiveButton("Delete") { dialog, _ ->
MainActivity.uas[position].destroy()
MainActivity.uas.remove(MainActivity.uas[position])
MainActivity.images.removeAt(position)
AccountsActivity.saveAccounts()
UserAgent.remove(ua)
AccountsActivity.generateAccounts()
AccountsActivity.saveAccounts()
this.notifyDataSetChanged()
dialog.dismiss()
}

View File

@ -13,17 +13,20 @@ import java.util.ArrayList
class AccountsActivity : AppCompatActivity() {
internal var aor: String = ""
internal lateinit var alAdapter: AccountListAdapter
internal var aor = ""
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
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()
alAdapter = AccountListAdapter(this, accounts)
listview.adapter = alAdapter
listView.adapter = alAdapter
val addAccountButton = findViewById(R.id.addAccount) as ImageButton
val newAorView = findViewById(R.id.newAor) as EditText
@ -34,7 +37,7 @@ class AccountsActivity : AppCompatActivity() {
Log.e("Baresip", "Invalid SIP Address of Record $aor")
Utils.alertView(this, "Notice",
"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")
} else {
val ua = UserAgent.uaAlloc("<$aor>;regq=0.5;pubint=0;regint=0")
@ -46,8 +49,7 @@ class AccountsActivity : AppCompatActivity() {
newAorView.setText("")
newAorView.hint = "SIP URI user@domain"
newAorView.clearFocus()
MainActivity.uas.add(ua)
MainActivity.images.add(R.drawable.dot_yellow)
UserAgent.add(ua, R.drawable.dot_yellow)
generateAccounts()
alAdapter.notifyDataSetChanged()
saveAccounts()
@ -56,10 +58,18 @@ class AccountsActivity : AppCompatActivity() {
b.putString("accp", ua.account.accp)
i.putExtras(b)
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 {
@ -76,11 +86,13 @@ class AccountsActivity : AppCompatActivity() {
companion object {
internal var filesPath = ""
var accounts = ArrayList<AccountRow>()
fun generateAccounts() {
accounts.clear()
for (ua in MainActivity.uas)
for (ua in UserAgent.uas())
accounts.add(AccountRow(ua.account.aor.replace("sip:", ""),
R.drawable.action_remove))
}
@ -88,9 +100,8 @@ class AccountsActivity : AppCompatActivity() {
fun saveAccounts() {
var accounts = ""
for (a in Account.accounts()) accounts = accounts + a.print() + "\n"
val path = MainActivity.filesPath + "/accounts"
Utils.putFileContents(File(path), accounts)
// Log.d("Baresip", "Saved accounts '${accounts}' to '$path")
Utils.putFileContents(File(filesPath + "/accounts"), accounts)
// Log.d("Baresip", "Saved accounts '${accounts}' to '${filesPath}/accounts")
}
}

View File

@ -3,7 +3,28 @@ package com.tutpro.baresip
object Api {
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_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 contact_add(contact: String)
external fun contacts_remove()

View File

@ -1,11 +1,10 @@
package com.tutpro.baresip
import android.annotation.TargetApi
import android.app.*
import android.app.Notification.VISIBILITY_PUBLIC
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.*
import android.media.*
import android.net.ConnectivityManager
import android.net.NetworkInfo
import android.net.wifi.WifiManager
@ -22,11 +21,14 @@ import android.os.Build
import android.support.v4.app.NotificationCompat.VISIBILITY_PRIVATE
import java.nio.charset.StandardCharsets
import java.io.InputStream
import java.util.*
class BaresipService: Service() {
private val LOG_TAG = "Baresip Service"
internal lateinit var intent: Intent
internal lateinit var am: AudioManager
internal lateinit var rt: Ringtone
internal lateinit var nm: NotificationManager
internal lateinit var snb: NotificationCompat.Builder
internal lateinit var npi: PendingIntent
@ -34,6 +36,10 @@ class BaresipService: Service() {
internal lateinit var wl: PowerManager.WakeLock
internal lateinit var fl: WifiManager.WifiLock
internal var rtTimer: Timer? = null
internal var filesPath = ""
internal var restarting = false
override fun onCreate() {
Log.d(LOG_TAG, "At onCreate")
@ -41,6 +47,13 @@ class BaresipService: Service() {
intent = Intent("com.tutpro.baresip.EVENT")
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
createNotificationChannels()
snb = NotificationCompat.Builder(this, DEFAULT_CHANNEL_ID)
@ -59,7 +72,7 @@ class BaresipService: Service() {
if (isConnected) {
Log.d(LOG_TAG, "Network is connected/connecting")
if (disconnected) {
UserAgent.register(MainActivity.uas)
UserAgent.register(uas)
disconnected = false
}
} else {
@ -103,22 +116,21 @@ class BaresipService: Service() {
val assets = arrayOf("accounts", "contacts", "config", "busy.wav", "callwaiting.wav",
"error.wav", "notfound.wav", "ring.wav", "ringback.wav")
val path = applicationContext.filesDir.path
var file = File(path)
var file = File(filesPath)
if (!file.exists()) {
Log.d(LOG_TAG, "Creating baresip directory")
try {
File(path).mkdirs()
File(filesPath).mkdirs()
} catch (e: Error) {
Log.e(LOG_TAG, "Failed to create directory: " + e.toString())
}
}
for (a in assets) {
file = File("$path/$a")
file = File("${filesPath}/$a")
if (!file.exists()) {
Log.d(LOG_TAG, "Copying asset $a")
Utils.copyAssetToFile(applicationContext, a, "$path/$a")
Utils.copyAssetToFile(applicationContext, a, "$filesPath/$a")
} else {
Log.d(LOG_TAG, "Asset $a already copied")
if (a == "config") {
@ -144,8 +156,8 @@ class BaresipService: Service() {
}
wl.acquire()
Thread(Runnable { baresipStart(path) }).start()
BaresipService.IS_SERVICE_RUNNING = true
Thread(Runnable { baresipStart(filesPath) }).start()
BaresipService.isServiceRunning = true
registerReceiver(nr, IntentFilter("android.net.conn.CONNECTIVITY_CHANGE"))
showStatusNotification()
}
@ -159,7 +171,7 @@ class BaresipService: Service() {
}
"Kill" -> {
RESTARTING = true
restarting = true
stop()
}
}
@ -175,7 +187,7 @@ class BaresipService: Service() {
Log.d(LOG_TAG, "In onDestroy")
super.onDestroy()
Log.i(LOG_TAG, "Restart baresip killed by Android")
RESTARTING = true
restarting = true
stop()
}
@ -208,14 +220,13 @@ class BaresipService: Service() {
fun uaAdd(uap: String) {
Log.d(LOG_TAG, "addUA at BaresipService")
val ua = UserAgent(uap)
MainActivity.uas.add(ua)
if (UserAgent.ua_isregistered(uap)) {
uas.add(ua)
if (Api.ua_isregistered(uap)) {
Log.d(LOG_TAG, "Ua ${ua.account.aor} is registered")
MainActivity.images.add(R.drawable.dot_green)
status.add(R.drawable.dot_green)
} else {
Log.d(LOG_TAG, "Ua ${ua.account.aor} is NOT registered")
MainActivity.images.add(R.drawable.dot_yellow)
// ua.register()
status.add(R.drawable.dot_yellow)
}
val intent = Intent("service event")
intent.putExtra("event", "ua added")
@ -226,54 +237,69 @@ class BaresipService: Service() {
@Keep
fun uaEvent(event: String, uap: String, callp: String) {
Log.d(LOG_TAG, "updateStatus got event $event/$uap/$callp")
if (!IS_SERVICE_RUNNING) return
Log.d(LOG_TAG, "uaEvent got event $event/$uap/$callp")
if (!isServiceRunning) return
if (event == "exit")
return
val ua = UserAgent.find(MainActivity.uas, uap)
val ua = UserAgent.find(uap)
if (ua == null) {
Log.w(LOG_TAG, "updateStatus did not find ua $uap")
Log.w(LOG_TAG, "uaEvent did not find ua $uap")
return
}
val aor = ua.account.aor
for (account_index in MainActivity.uas.indices) {
if (MainActivity.uas[account_index].account.aor == aor) {
when (event) {
var newEvent: String? = null
val ev = event.split(",")
for (account_index in uas.indices) {
if (uas[account_index].account.aor == aor) {
when (ev[0]) {
"registering" -> {
return
}
"registered" -> {
if (ua.account.regint == 0)
MainActivity.images[account_index] = R.drawable.dot_yellow
status[account_index] = R.drawable.dot_yellow
else
MainActivity.images[account_index] = R.drawable.dot_green
status[account_index] = R.drawable.dot_green
updateStatusNotification()
}
"registering failed" -> {
MainActivity.images[account_index] = R.drawable.dot_red
status[account_index] = R.drawable.dot_red
updateStatusNotification()
}
"unregistering" -> {
MainActivity.images[account_index] = R.drawable.dot_yellow
status[account_index] = R.drawable.dot_yellow
updateStatusNotification()
}
"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 caller = Utils.friendlyUri(ContactsActivity.contactName(peerUri),
Utils.aorDomain(aor))
val title = "Incoming call from $caller"
cnb.setSmallIcon(R.drawable.ic_stat)
.setColor(0x0ca1fd)
.setContentIntent(npi)
.setAutoCancel(true)
.setContentTitle("Call from ${ContactsActivity.contactName(
Api.call_peeruri(callp))}")
.setContentTitle(title)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
cnb.setVibrate(LongArray(0))
.setVisibility(VISIBILITY_PRIVATE)
.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)
answerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
@ -288,22 +314,114 @@ class BaresipService: Service() {
rejectIntent.putExtra("callp", callp)
val rejectPendingIntent = PendingIntent.getActivity(this,
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, "Reject", rejectPendingIntent)
nm.notify(CALL_NOTIFICATION_ID, cnb.build())
}
}
"call established", "call closed" -> {
"call established" -> {
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")
intent.putExtra("event", event)
intent.putExtra("event", newEvent)
intent.putExtra("params", arrayListOf(uap, callp))
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
@ -316,25 +434,29 @@ class BaresipService: Service() {
} catch (e: Exception) {
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()
if (!Utils.isVisible()) {
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)
.setColor(0x0ca1fd)
.setContentIntent(npi)
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true)
.setContentTitle("Message from ${ContactsActivity.contactName(peer)}")
.setContentTitle("Message from $sender")
.setContentText(s)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
cnb.setVibrate(LongArray(0))
.setVisibility(VISIBILITY_PRIVATE)
.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)
replyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
@ -359,9 +481,6 @@ class BaresipService: Service() {
deleteIntent.putExtra("time", timeStamp)
val deletePendingIntent = PendingIntent.getActivity(this,
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, "Save", savePendingIntent)
cnb.addAction(R.drawable.ic_stat, "Delete", deletePendingIntent)
@ -385,28 +504,28 @@ class BaresipService: Service() {
@Keep
fun stopped() {
Log.d(LOG_TAG, "got event 'stopped'")
IS_SERVICE_RUNNING = false
isServiceRunning = false
val intent = Intent("service event")
intent.putExtra("event", "stopped")
intent.putExtra("params", arrayListOf<String>())
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
stopForeground(true)
stopSelf()
if (RESTARTING) restart()
if (restarting) restart()
}
private fun updateStatusNotification() {
val contentView = RemoteViews(getPackageName(), R.layout.status_notification)
for (i: Int in 0 .. 5) {
val resID = resources.getIdentifier("status$i", "id", packageName)
if (i < MainActivity.images.size) {
contentView.setImageViewResource(resID, MainActivity.images[i])
if (i < status.size) {
contentView.setImageViewResource(resID, status[i])
contentView.setViewVisibility(resID, View.VISIBLE)
} else {
contentView.setViewVisibility(resID, View.INVISIBLE)
}
}
if (MainActivity.images.size > 4)
if (status.size > 4)
contentView.setViewVisibility(R.id.etc, View.VISIBLE)
else
contentView.setViewVisibility(R.id.etc, View.INVISIBLE)
@ -414,11 +533,78 @@ class BaresipService: Service() {
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() {
MainActivity.uas.clear()
MainActivity.images.clear()
MainActivity.history.clear()
MainActivity.messages.clear()
uas.clear()
status.clear()
history.clear()
messages.clear()
try {
unregisterReceiver(nr)
} catch (e: Exception) {
@ -427,15 +613,15 @@ class BaresipService: Service() {
if (this::nm.isInitialized) nm.cancelAll()
if (this::wl.isInitialized && wl.isHeld) wl.release()
if (this::fl.isInitialized && fl.isHeld) fl.release()
if (IS_SERVICE_RUNNING) {
if (isServiceRunning) {
baresipStop(forceStop)
if (!forceStop) forceStop = true
} else if (RESTARTING)
} else if (restarting)
restart()
}
private fun restart() {
RESTARTING = false
restarting = false
val broadcastIntent = Intent("com.tutpro.baresip.Restart")
sendBroadcast(broadcastIntent)
}
@ -445,16 +631,27 @@ class BaresipService: Service() {
companion object {
var IS_SERVICE_RUNNING = false
var RESTARTING = false
val STATUS_NOTIFICATION_ID = 101
val CALL_NOTIFICATION_ID = 102
val MESSAGE_NOTIFICATION_ID = 103
val DEFAULT_CHANNEL_ID = "com.tutpro.baresip.default"
val HIGH_CHANNEL_ID = "com.tutpro.baresip.high"
var isServiceRunning = false
var disconnected = 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 {

View File

@ -13,6 +13,10 @@ class Call(val callp: String, val ua: UserAgent, val peerURI: String, val dir: S
companion object {
fun calls(): ArrayList<Call> {
return BaresipService.calls
}
fun calls(calls: ArrayList<Call>, dir: String): ArrayList<Call> {
val result = ArrayList<Call>()
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
}
fun uaCalls(calls: ArrayList<Call>, ua: UserAgent, dir: String): ArrayList<Call> {
fun calls(dir: String): ArrayList<Call> {
val result = ArrayList<Call>()
for (i in calls.indices)
if ((calls[i].ua == ua) && ((dir == "") || calls[i].dir == dir)) result.add(calls[i])
for (c in BaresipService.calls) {
if (c.dir == dir) result.add(c)
}
return result
}
fun find(calls: ArrayList<Call>, callp: String): Call? {
for (c in calls) {
fun uaCalls(ua: UserAgent, dir: String): ArrayList<Call> {
val result = ArrayList<Call>()
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
}

View File

@ -1,6 +1,8 @@
package com.tutpro.baresip
import java.io.Serializable
import android.util.Log
import java.io.*
import java.util.ArrayList
import java.util.GregorianCalendar
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 {
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;
for (h in history) {
for (h in BaresipService.history) {
if (h.aor == aor) size++
}
return size
}
fun aorRemoveHistory(history: ArrayList<CallHistory>, aor: String) {
for (h in history) {
if (h.aor == aor) {
history.remove(h)
return
}
}
}
fun aorLatestHistory(history: ArrayList<CallHistory>, aor: String): CallHistory? {
for (h in history.reversed())
fun aorLatestHistory(aor: String): CallHistory? {
for (h in BaresipService.history.reversed())
if (h.aor == aor) return h
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())
}
}
}
}
}

View File

@ -11,12 +11,6 @@ import android.view.MenuItem
import android.widget.AdapterView
import android.widget.ListView
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.util.ArrayList
@ -120,7 +114,12 @@ class CallsActivity : AppCompatActivity() {
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 {
@ -137,8 +136,8 @@ class CallsActivity : AppCompatActivity() {
private fun aorGenerateHistory(aor: String) {
uaHistory.clear()
for (i in MainActivity.history.indices.reversed()) {
val h = MainActivity.history[i]
for (i in CallHistory.history().indices.reversed()) {
val h = CallHistory.history()[i]
if (h.aor == aor) {
var direction: Int
if (h.direction == "in")
@ -171,7 +170,7 @@ class CallsActivity : AppCompatActivity() {
private fun removeUaHistoryAt(i: Int) {
for (index in uaHistory[i].indexes)
MainActivity.history.removeAt(index)
CallHistory.history().removeAt(index)
uaHistory.removeAt(i)
}
@ -182,38 +181,4 @@ class CallsActivity : AppCompatActivity() {
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())
}
}
}
}
}

View File

@ -82,7 +82,7 @@ class ChatActivity : AppCompatActivity() {
startActivityForResult(i, MainActivity.CONTACT_CODE)
}
DialogInterface.BUTTON_NEGATIVE -> {
MainActivity.messages.remove(chatMessages[pos])
Message.messages().remove(chatMessages[pos])
chatMessages.removeAt(pos)
mlAdapter.notifyDataSetChanged()
if (chatMessages.size == 0) {
@ -118,10 +118,10 @@ class ChatActivity : AppCompatActivity() {
imm.hideSoftInputFromWindow(newMessage.windowToken, 0)
val time = System.currentTimeMillis()
val msg = Message(aor, peerUri, R.drawable.arrow_up_yellow, msgText, time,true)
MainActivity.messages.add(msg)
Message.add(msg)
chatMessages.add(msg)
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.LENGTH_SHORT).show()
else
@ -146,7 +146,7 @@ class ChatActivity : AppCompatActivity() {
}
override fun onPause() {
ChatsActivity.saveMessages()
ChatsActivity.saveMessages(applicationContext.filesDir.absolutePath)
super.onPause()
}
@ -169,7 +169,7 @@ class ChatActivity : AppCompatActivity() {
Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent.putExtra("action", "call")
intent.putExtra("uap", ua.uap)
intent.putExtra("peer", ContactsActivity.contactName(peerUri))
intent.putExtra("peer", peerUri)
startActivity(intent)
}
}
@ -185,7 +185,7 @@ class ChatActivity : AppCompatActivity() {
private fun uaPeerMessages(aor: String, peerUri: String): 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)
return res
}
@ -203,6 +203,4 @@ class ChatActivity : AppCompatActivity() {
}
}
external fun message_send(uap: String, peer_uri: String, message: String, time: String): Int
}

View File

@ -30,6 +30,8 @@ class ChatsActivity: AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chats)
filesPath = applicationContext.filesDir.absolutePath
listView = findViewById(R.id.chats) as ListView
plusButton = findViewById(R.id.plusButton) as ImageButton
@ -67,13 +69,13 @@ class ChatsActivity: AppCompatActivity() {
DialogInterface.BUTTON_NEGATIVE -> {
val peerUri = uaMessages[pos].peerUri
val msgs = ArrayList<Message>()
for (m in MainActivity.messages)
for (m in Message.messages())
if ((m.aor != aor) || (m.peerUri != peerUri))
msgs.add(m)
else
clAdapter.remove(m)
clAdapter.notifyDataSetChanged()
MainActivity.messages = msgs
Message.messages(msgs)
uaMessages = uaMessages(aor)
}
DialogInterface.BUTTON_POSITIVE -> {
@ -84,8 +86,8 @@ class ChatsActivity: AppCompatActivity() {
val builder = AlertDialog.Builder(this@ChatsActivity, R.style.Theme_AppCompat)
val peer = ContactsActivity.contactName(uaMessages[pos].peerUri)
if (peer.startsWith("sip:"))
builder.setMessage("Do you want to delete chat with peer '" +
"${Utils.friendlyUri(peer, Utils.aorDomain(aor))}' or add peer to contacts?")
builder.setMessage("Do you want to delete chat with peer " +
"${Utils.friendlyUri(peer, Utils.aorDomain(aor))} or add peer to contacts?")
.setPositiveButton("Cancel", dialogClickListener)
.setNegativeButton("Delete Chat", dialogClickListener)
.setNeutralButton("Add Contact", dialogClickListener)
@ -101,7 +103,7 @@ class ChatsActivity: AppCompatActivity() {
peerUri = findViewById(R.id.peerUri) as AutoCompleteTextView
peerUri.threshold = 2
peerUri.setAdapter(ArrayAdapter(this, android.R.layout.select_dialog_item,
ContactsActivity.contacts.map{Contact -> Contact.name}))
Contact.contacts().map{Contact -> Contact.name}))
plusButton.setOnClickListener {
val uriText = peerUri.text.toString().trim()
@ -143,7 +145,7 @@ class ChatsActivity: AppCompatActivity() {
}
override fun onPause() {
saveMessages()
saveMessages(applicationContext.filesDir.absolutePath)
super.onPause()
}
@ -171,7 +173,7 @@ class ChatsActivity: AppCompatActivity() {
private fun uaMessages(aor: String) : ArrayList<Message> {
val res = ArrayList<Message>()
for (m in MainActivity.messages.reversed()) {
for (m in Message.messages().reversed()) {
if (m.aor != aor) continue
var found = false
for (r in res)
@ -186,32 +188,34 @@ class ChatsActivity: AppCompatActivity() {
companion object {
fun saveUaMessage(aor: String, time: Long) {
for (i in MainActivity.messages.indices.reversed())
if ((MainActivity.messages[i].aor == aor) &&
(MainActivity.messages[i].timeStamp == time)) {
MainActivity.messages[i].new = false
saveMessages()
var filesPath = ""
fun saveUaMessage(aor: String, time: Long, path: String) {
for (i in Message.messages().indices.reversed())
if ((Message.messages()[i].aor == aor) &&
(Message.messages()[i].timeStamp == time)) {
Message.messages()[i].new = false
saveMessages(path)
return
}
}
fun deleteUaMessage(aor: String, time: Long) {
for (i in MainActivity.messages.indices.reversed())
if ((MainActivity.messages[i].aor == aor) &&
(MainActivity.messages[i].timeStamp == time)) {
MainActivity.messages.removeAt(i)
saveMessages()
fun deleteUaMessage(aor: String, time: Long, path: String) {
for (i in Message.messages().indices.reversed())
if ((Message.messages()[i].aor == aor) &&
(Message.messages()[i].timeStamp == time)) {
Message.messages().removeAt(i)
saveMessages(path)
return
}
}
fun saveMessages() {
val file = File(MainActivity.filesPath, "messages")
fun saveMessages(path: String = filesPath) {
val file = File(path, "messages")
try {
val fos = FileOutputStream(file)
val oos = ObjectOutputStream(fos)
oos.writeObject(MainActivity.messages)
oos.writeObject(Message.messages())
oos.close()
fos.close()
} catch (e: IOException) {
@ -220,15 +224,13 @@ class ChatsActivity: AppCompatActivity() {
}
}
fun restoreMessages(path: String) {
fun restoreMessages(path: String = filesPath) {
val file = File(path, "messages")
if (file.exists()) {
try {
val fis = FileInputStream(file)
val ois = ObjectInputStream(fis)
@SuppressWarnings("unchecked")
MainActivity.messages = ois.readObject() as ArrayList<Message>
Log.d("Baresip", "Restored ${MainActivity.messages.size} messages")
Message.messages(ois.readObject() as ArrayList<Message>)
ois.close()
fis.close()
} catch (e: Exception) {
@ -236,5 +238,6 @@ class ChatsActivity: AppCompatActivity() {
}
}
}
}
}

View File

@ -1,3 +1,16 @@
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
}
}
}

View File

@ -37,10 +37,10 @@ class ContactActivity : AppCompatActivity() {
}
} else {
index = intent.extras.getInt("index")
val name = ContactsActivity.contacts[index].name
val name = Contact.contacts()[index].name
setTitle(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")
return false
}
if ((new || (ContactsActivity.contacts[index].name != newName)) &&
if ((new || (Contact.contacts()[index].name != newName)) &&
ContactsActivity.nameExists(newName)) {
Utils.alertView(this, "Notice",
"Contact $newName already exists")
@ -73,20 +73,27 @@ class ContactActivity : AppCompatActivity() {
var newUri = uriView.text.toString().trim()
if (!newUri.startsWith("<")) {
if (!newUri.startsWith("sip:")) newUri = "sip:$newUri"
if (!Utils.checkSipUri(newUri)) {
if (!Utils.checkAorUri(newUri)) {
Utils.alertView(this, "Notice","Invalid contact URI: $newUri")
return false
}
}
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 {
Contact.contacts().add(Contact(newName, newUri))
}
} else {
ContactsActivity.contacts[index].uri = newUri
ContactsActivity.contacts[index].name = newName
Contact.contacts()[index].uri = newUri
Contact.contacts()[index].name = newName
}
ContactsActivity.contacts.sortBy { Contact -> Contact.name }
ContactsActivity.saveContacts()
Contact.contacts().sortBy { Contact -> Contact.name }
ContactsActivity.saveContacts(applicationContext.filesDir.absolutePath)
i.putExtra("name", newName)
setResult(Activity.RESULT_OK, i)
@ -104,8 +111,10 @@ class ContactActivity : AppCompatActivity() {
}
companion object {
internal var index = 0
internal var new = false
}
}

View File

@ -45,7 +45,7 @@ class ContactListAdapter(private val cxt: Context, private val rows: ArrayList<C
Log.e("Baresip", "onClickListener did not find AoR $aor")
} else {
i.putExtra("uap", ua.uap)
i.putExtra("peer", ContactsActivity.contacts[pos].uri)
i.putExtra("peer", Contact.contacts()[pos].uri)
(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)
builder.setMessage("Do you want to call or send message to '" +
"${ContactsActivity.contacts[pos].name}'?")
"${Contact.contacts()[pos].name}'?")
.setNeutralButton("Call", dialogClickListener)
.setNegativeButton("Send Message", dialogClickListener)
.setPositiveButton("Cancel", dialogClickListener)
@ -66,7 +66,7 @@ class ContactListAdapter(private val cxt: Context, private val rows: ArrayList<C
val dialogClickListener = DialogInterface.OnClickListener { _, which ->
when (which) {
DialogInterface.BUTTON_NEGATIVE -> {
ContactsActivity.contacts.removeAt(pos)
Contact.contacts().removeAt(pos)
ContactsActivity.saveContacts()
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)
builder.setMessage("Do you want to delete contact '" +
"${ContactsActivity.contacts[pos].name}'?")
"${Contact.contacts()[pos].name}'?")
.setPositiveButton("Cancel", dialogClickListener)
.setNegativeButton("Delete Contact", dialogClickListener)
.show()

View File

@ -10,7 +10,6 @@ import android.widget.ImageButton
import android.widget.ListView
import java.io.File
import java.util.ArrayList
class ContactsActivity : AppCompatActivity() {
@ -20,22 +19,29 @@ class ContactsActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_contacts)
filesPath = applicationContext.filesDir.absolutePath
val listView = findViewById(R.id.contacts) as ListView
Log.d("Baresip", "Got ${contacts.size} contacts")
val aor = intent.extras.getString("aor")
clAdapter = ContactListAdapter(this, contacts, aor)
clAdapter = ContactListAdapter(this, Contact.contacts(), aor)
listView.adapter = clAdapter
listView.isLongClickable = true
val plusButton = findViewById(R.id.plusButton) as ImageButton
plusButton.setOnClickListener {
val i = Intent(this, ContactActivity::class.java)
val b = Bundle()
b.putBoolean("new", true)
b.putString("uri", "")
i.putExtras(b)
startActivityForResult(i, MainActivity.CONTACT_CODE)
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 b = Bundle()
b.putBoolean("new", true)
b.putString("uri", "")
i.putExtras(b)
startActivityForResult(i, MainActivity.CONTACT_CODE)
}
}
}
@ -64,34 +70,32 @@ class ContactsActivity : AppCompatActivity() {
companion object {
var contacts = ArrayList<Contact>()
internal var filesPath = ""
fun saveContacts() {
fun saveContacts(path: String = filesPath) {
var contents = ""
for (c in contacts)
for (c in Contact.contacts())
contents += "\"${c.name}\" ${c.uri}\n"
val path = MainActivity.filesPath + "/contacts"
Utils.putFileContents(File(path), contents)
Log.d("Baresip", "Saved contacts '${contents}' to '$path")
Utils.putFileContents(File(path + "/contacts"), contents)
}
fun restoreContacts(path: String) {
fun restoreContacts(path: String = filesPath) {
val content = Utils.getFileContents(File(path + "/contacts"))
Api.contacts_remove()
contacts.clear()
Contact.contacts().clear()
content.lines().forEach {
val parts = it.split("\"")
if (parts.size == 3) {
val name = parts[1]
val uri = parts[2].trim()
Api.contact_add("\"$name\" $uri")
contacts.add(Contact(name, uri))
Contact.contacts().add(Contact(name, uri))
}
}
}
fun findContactURI(name: String): String {
for (c in contacts)
for (c in Contact.contacts())
if (c.name == name)
return c.uri.removePrefix("<")
.replaceAfter(">", "")
@ -100,13 +104,13 @@ class ContactsActivity : AppCompatActivity() {
}
fun nameExists(name: String): Boolean {
for (c in contacts)
for (c in Contact.contacts())
if (c.name.equals(name, ignoreCase = true)) return true
return false
}
fun contactName(uri: String): String {
for (c in contacts)
for (c in Contact.contacts())
if ((Utils.uriUserPart(c.uri) == Utils.uriUserPart(uri)) &&
(Utils.uriHostPart(c.uri) == Utils.uriHostPart(uri)))
return c.name

View File

@ -7,10 +7,10 @@ import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.util.TypedValue
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.widget.EditText
import android.widget.TextView
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
@ -27,8 +27,7 @@ class EditConfigActivity : AppCompatActivity() {
setContentView(R.layout.activity_edit_config)
editText = findViewById(R.id.editConfig) as EditText
path = applicationContext.filesDir.absolutePath + "/config"
// path = "/sdcard/baresip/config";
path = "${applicationContext.filesDir.absolutePath}/config"
file = File(path!!)
var content: String

View File

@ -1,7 +1,6 @@
package com.tutpro.baresip
import android.Manifest
import android.annotation.TargetApi
import android.app.NotificationManager
import android.content.*
import android.support.v4.app.ActivityCompat
@ -15,22 +14,18 @@ import android.view.MenuItem
import android.content.pm.PackageManager
import android.graphics.Color
import android.media.*
import android.os.Build
import android.support.v4.content.LocalBroadcastManager
import android.support.v7.view.menu.ActionMenuItemView
import android.view.inputmethod.InputMethodManager
import android.text.Editable
import android.text.InputType
import android.text.TextWatcher
import android.widget.RelativeLayout
import android.widget.*
import android.view.*
import java.util.*
import kotlin.collections.ArrayList
class MainActivity : AppCompatActivity() {
lateinit var appContext: Context
internal lateinit var layout: RelativeLayout
internal lateinit var baresipService: Intent
internal lateinit var callTitle: TextView
@ -51,7 +46,6 @@ class MainActivity : AppCompatActivity() {
internal lateinit var aorSpinner: Spinner
internal lateinit var am: AudioManager
internal lateinit var rt: Ringtone
internal var rtTimer: Timer? = null
internal lateinit var imm: InputMethodManager
internal lateinit var nm: NotificationManager
internal lateinit var serviceEventReceiver: BroadcastReceiver
@ -69,9 +63,15 @@ class MainActivity : AppCompatActivity() {
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
callTitle = findViewById(R.id.callTitle) as TextView
callUri = findViewById(R.id.callUri) as AutoCompleteTextView
@ -112,16 +112,16 @@ class MainActivity : AppCompatActivity() {
}
aorSpinner = findViewById(R.id.AoRList)
uaAdapter = UaSpinnerAdapter(applicationContext, uas, images)
uaAdapter = UaSpinnerAdapter(applicationContext, UserAgent.uas(), UserAgent.status())
aorSpinner.adapter = uaAdapter
aorSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
Log.d("Baresip", "aorSpinner selecting $position")
val acc = uas[position].account
val acc = UserAgent.uas()[position].account
val aor = acc.aor
val ua = uas[position]
val ua = UserAgent.uas()[position]
Log.d("Baresip", "Setting $aor current")
uag_current_set(uas[position].uap)
Api.uag_current_set(UserAgent.uas()[position].uap)
showCall(ua)
if (acc.vmUri != "") {
if (acc.vmNew > 0)
@ -151,11 +151,12 @@ class MainActivity : AppCompatActivity() {
true
} else {
if ((event.x - view.left) < 100) {
val i = Intent(this, AccountActivity::class.java)
val i = Intent(this, AccountsActivity::class.java)
val b = Bundle()
b.putString("accp", uas[aorSpinner.selectedItemPosition].account.accp)
b.putString("accp",
UserAgent.uas()[aorSpinner.selectedItemPosition].account.accp)
i.putExtras(b)
startActivityForResult(i, ACCOUNT_CODE)
startActivityForResult(i, ACCOUNTS_CODE)
true
} else {
false
@ -165,11 +166,12 @@ class MainActivity : AppCompatActivity() {
false
}
}
callUri.threshold = 2
ContactsActivity.restoreContacts(applicationContext.filesDir.path)
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 {
when (securityButton.tag) {
@ -186,7 +188,8 @@ class MainActivity : AppCompatActivity() {
unverifyDialog.setMessage("This call is SECURE and peer is VERIFIED! " +
"Do you want to unverify the peer?")
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 (Api.cmd_exec("zrtp_unverify " + calls[0].zid) != 0) {
Log.w("Baresip",
@ -207,9 +210,10 @@ class MainActivity : AppCompatActivity() {
}
callButton.setOnClickListener {
val ua = uas[aorSpinner.selectedItemPosition]
callUri.setAdapter(null)
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val aor = ua.account.aor
if (!ONE_CALL_ONLY || calls.isEmpty()) {
if (Call.calls().isEmpty()) {
val uriText = callUri.text.toString().trim()
if (uriText.length > 0) {
var uri = ContactsActivity.findContactURI(uriText)
@ -225,66 +229,66 @@ class MainActivity : AppCompatActivity() {
else
call(ua, uri)
} else {
val latest = CallHistory.aorLatestHistory(history, aor)
val latest = CallHistory.aorLatestHistory(aor)
if (latest != null)
if (Utils.uriHostPart(latest.peerURI) == Utils.uriHostPart(aor))
callUri.setText(Utils.uriUserPart(latest.peerURI))
else
callUri.setText(latest.peerURI)
callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(latest.peerURI),
Utils.aorDomain(ua.account.aor)))
}
}
}
hangupButton.setOnClickListener {
val ua = uas[aorSpinner.selectedItemPosition]
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val aor = ua.account.aor
val uaCalls = Call.uaCalls(calls, ua, "")
val uaCalls = Call.uaCalls(ua, "")
if (uaCalls.size > 0) {
val callp = uaCalls[uaCalls.size - 1].callp
Log.d("Baresip", "AoR $aor hanging up call $callp with ${callUri.text}")
hangupButton.isEnabled = false
ua_hangup(ua.uap, callp, 0, "")
Api.ua_hangup(ua.uap, callp, 0, "")
}
}
answerButton.setOnClickListener {
val ua = uas[aorSpinner.selectedItemPosition]
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
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}")
answerButton.isEnabled = false
ua_answer(ua.uap, callp)
Api.ua_answer(ua.uap, callp)
}
rejectButton.setOnClickListener {
val ua = uas[aorSpinner.selectedItemPosition]
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
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}")
rejectButton.isEnabled = false
ua_hangup(ua.uap, callp, 486, "Rejected")
Api.ua_hangup(ua.uap, callp, 486, "Rejected")
}
holdButton.setOnClickListener {
val ua = uas[aorSpinner.selectedItemPosition]
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val aor = ua.account.aor
val call = Call.uaCalls(calls, ua,"")[0]
val call = Call.uaCalls(ua,"")[0]
if (call.onhold) {
Log.d("Baresip", "AoR $aor resuming call ${call.callp} with ${callUri.text}")
call_unhold(call.callp)
Api.call_unhold(call.callp)
call.onhold = false
holdButton.setImageResource(R.drawable.pause)
} else {
Log.d("Baresip", "AoR $aor holding call ${call.callp} with ${callUri.text}")
call_hold(call.callp)
Api.call_hold(call.callp)
call.onhold = true
holdButton.setImageResource(R.drawable.play)
}
}
dtmf.tag = ArrayList<TextWatcher>()
voicemailButton.setOnClickListener {
if (aorSpinner.selectedItemPosition >= 0) {
val ua = uas[aorSpinner.selectedItemPosition]
val ua = UserAgent.uas()[aorSpinner.selectedItemPosition]
val acc = ua.account
if (acc.vmUri != "") {
val dialogClickListener = DialogInterface.OnClickListener { _, which ->
@ -317,19 +321,19 @@ class MainActivity : AppCompatActivity() {
val i = Intent(this@MainActivity, ContactsActivity::class.java)
val b = Bundle()
if (aorSpinner.selectedItemPosition >= 0)
b.putString("aor", uas[aorSpinner.selectedItemPosition].account.aor)
b.putString("aor", UserAgent.uas()[aorSpinner.selectedItemPosition].account.aor)
else
b.putString("aor", "")
i.putExtras(b)
startActivityForResult(i, CONTACTS_CODE)
}
ChatsActivity.restoreMessages(applicationContext.filesDir.path)
ChatsActivity.restoreMessages(applicationContext.filesDir.absolutePath)
messagesButton.setOnClickListener {
if (aorSpinner.selectedItemPosition >= 0) {
val i = Intent(this@MainActivity, ChatsActivity::class.java)
val b = Bundle()
b.putString("aor", uas[aorSpinner.selectedItemPosition].account.aor)
b.putString("aor", UserAgent.uas()[aorSpinner.selectedItemPosition].account.aor)
b.putString("peer", "")
b.putBoolean("focus", false)
i.putExtras(b)
@ -337,12 +341,12 @@ class MainActivity : AppCompatActivity() {
}
}
CallsActivity.restoreHistory(applicationContext.filesDir.path)
CallHistory.restore(applicationContext.filesDir.absolutePath)
callsButton.setOnClickListener {
if (aorSpinner.selectedItemPosition >= 0) {
val i = Intent(this@MainActivity, CallsActivity::class.java)
val b = Bundle()
b.putString("aor", uas[aorSpinner.selectedItemPosition].account.aor)
b.putString("aor", UserAgent.uas()[aorSpinner.selectedItemPosition].account.aor)
i.putExtras(b)
startActivityForResult(i, CALLS_CODE)
}
@ -363,7 +367,7 @@ class MainActivity : AppCompatActivity() {
}
baresipService = Intent(this@MainActivity, BaresipService::class.java)
if (!BaresipService.IS_SERVICE_RUNNING) {
if (!BaresipService.isServiceRunning) {
baresipService.setAction("Start")
startService(baresipService)
}
@ -372,7 +376,7 @@ class MainActivity : AppCompatActivity() {
moveTaskToBack(true)
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)
}
}
@ -385,7 +389,7 @@ class MainActivity : AppCompatActivity() {
return
}
val uap = params[0]
val ua = UserAgent.find(uas, uap)
val ua = UserAgent.find(uap)
if (ua == null) {
Log.w("Baresip", "handleServiceEvent '$event' did not find ua $uap")
return
@ -394,8 +398,8 @@ class MainActivity : AppCompatActivity() {
Log.d("Baresip", "Handling service event '${ev[0]}' for $uap")
val aor = ua.account.aor
val acc = ua.account
for (account_index in uas.indices) {
if (uas[account_index].account.aor == aor) {
for (account_index in UserAgent.uas().indices) {
if (UserAgent.uas()[account_index].account.aor == aor) {
when (ev[0]) {
"ua added" -> {
uaAdapter.notifyDataSetChanged()
@ -406,70 +410,49 @@ class MainActivity : AppCompatActivity() {
}
"call ringing" -> {
}
"call rejected" -> {
if (ua == UserAgent.uas()[aorSpinner.selectedItemPosition]) {
callsButton.setImageResource(R.drawable.calls_missed)
}
}
"call incoming" -> {
val callp = params[1]
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")
val call = Call.find(callp)
if (call == null) {
Log.e("Baresip", "Incoming call $callp not found")
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)
}
if (ua != UserAgent.uas()[aorSpinner.selectedItemPosition]) {
aorSpinner.setSelection(account_index)
} else {
Log.d("Baresip", "Incoming call $uap/$callp/$peer_uri")
calls.add(new_call)
if (ua != uas[aorSpinner.selectedItemPosition]) {
aorSpinner.setSelection(account_index)
} else {
callTitle.text = "Incoming call from ..."
callUri.setText(new_call.peerURI)
securityButton.visibility = View.INVISIBLE
callButton.visibility = View.INVISIBLE
hangupButton.visibility = View.INVISIBLE
answerButton.visibility = View.VISIBLE
answerButton.isEnabled = true
rejectButton.visibility = View.VISIBLE
rejectButton.isEnabled = true
holdButton.visibility = View.INVISIBLE
dtmf.visibility = View.INVISIBLE
}
startRinging()
if (Utils.isVisible()) {
callTitle.text = "Incoming call from ..."
callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(call.peerURI),
Utils.aorDomain(ua.account.aor)))
securityButton.visibility = View.INVISIBLE
callButton.visibility = View.INVISIBLE
hangupButton.visibility = View.INVISIBLE
answerButton.visibility = View.VISIBLE
answerButton.isEnabled = true
rejectButton.visibility = View.VISIBLE
rejectButton.isEnabled = true
holdButton.visibility = View.INVISIBLE
dtmf.visibility = View.INVISIBLE
}
if (Utils.isVisible()) {
Log.d("Baresip", "Baresip is visible")
val i = Intent(applicationContext, MainActivity::class.java)
i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
startActivity(i)
}
}
}
"call established" -> {
val callp = params[1]
val call = Call.find(calls, callp)
val call = Call.find(callp)
if (call == null) {
Log.e("Baresip", "Established call $callp not found")
return
}
Log.d("Baresip", "AoR $aor call $callp established")
call.status = "connected"
call.onhold = false
call.security = R.drawable.box_red
if (ua == uas[aorSpinner.selectedItemPosition]) {
if (ua == UserAgent.uas()[aorSpinner.selectedItemPosition]) {
if (call.dir == "in")
callTitle.text = "Incoming call from ..."
else
@ -492,28 +475,13 @@ class MainActivity : AppCompatActivity() {
dtmf.hint = "DTMF"
dtmf.visibility = View.VISIBLE
dtmf.requestFocus()
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)
(dtmf.tag as ArrayList<TextWatcher>).add(call.dtmfWatcher!!)
dtmf.addTextChangedListener(call.dtmfWatcher)
}
"call verify" -> {
val callp = params[1]
val call = Call.find(calls, callp)
val call = Call.find(callp)
if (call == null) {
Log.e("Baresip", "Call $callp to be verified is not found")
return
@ -531,7 +499,7 @@ class MainActivity : AppCompatActivity() {
}
call.security = security
call.zid = ev[3]
if (ua == uas[aorSpinner.selectedItemPosition]) {
if (ua == UserAgent.uas()[aorSpinner.selectedItemPosition]) {
securityButton.setImageResource(security)
setSecurityButtonTag(securityButton, security)
securityButton.visibility = View.VISIBLE
@ -541,7 +509,7 @@ class MainActivity : AppCompatActivity() {
verifyDialog.setNegativeButton("No") { dialog, _ ->
call.security = R.drawable.box_yellow
call.zid = ev[3]
if (ua == uas[aorSpinner.selectedItemPosition]) {
if (ua == UserAgent.uas()[aorSpinner.selectedItemPosition]) {
securityButton.setImageResource(R.drawable.box_yellow)
securityButton.tag = "yellow"
securityButton.visibility = View.VISIBLE
@ -552,7 +520,7 @@ class MainActivity : AppCompatActivity() {
}
"call verified", "call secure" -> {
val callp = params[1]
val call = Call.find(calls, callp)
val call = Call.find(callp)
if (call == null) {
Log.e("Baresip", "Call $callp that is verified is not found")
return
@ -566,7 +534,7 @@ class MainActivity : AppCompatActivity() {
tag = "green"
call.zid = ev[1]
}
if (ua == uas[aorSpinner.selectedItemPosition]) {
if (ua == UserAgent.uas()[aorSpinner.selectedItemPosition]) {
securityButton.setImageResource(call.security)
securityButton.tag = tag
securityButton.visibility = View.VISIBLE
@ -574,86 +542,53 @@ class MainActivity : AppCompatActivity() {
}
"call transfer" -> {
val callp = params[1]
val call = Call.find(calls, callp)
val call = Call.find(callp)
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
}
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, _ ->
if (call in calls)
if (call in Call.calls())
transfer(ua, call, ev[1])
else
call(ua, ev[1])
dialog.dismiss()
}
transferDialog.setNegativeButton("No") { dialog, _ ->
if (call in calls)
call_notify_sipfrag(callp, 603, "Decline")
if (call in Call.calls())
Api.call_notify_sipfrag(callp, 603, "Decline")
dialog.dismiss()
}
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" -> {
val callp = params[1]
val call = Call.find(calls, callp)
if (call == null) {
Log.d("Baresip", "Call $callp that is closed is not found")
return
val watchers = dtmf.tag as ArrayList<TextWatcher>
if (watchers.size > 0) {
dtmf.removeTextChangedListener(watchers[0])
watchers.removeAt(0)
}
Log.d("Baresip", "Removing call ${uap}/${callp}/" + call.peerURI)
stopRinging()
if (ua == uas[aorSpinner.selectedItemPosition]) {
dtmf.removeTextChangedListener(call.dtmfWatcher)
}
calls.remove(call)
if (ua == uas[aorSpinner.selectedItemPosition]) {
if (ua == UserAgent.uas()[aorSpinner.selectedItemPosition]) {
showCall(ua)
if (ua.account.missedCalls)
callsButton.setImageResource(R.drawable.calls_missed)
}
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)
}
}
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
speakerIcon.setBackgroundColor(ContextCompat.getColor(applicationContext,
val speakerIcon = findViewById(R.id.speakerIcon) as ActionMenuItemView
speakerIcon.setBackgroundColor(ContextCompat.getColor(applicationContext,
R.color.colorPrimary))
}
if (audioFocused) {
abandonAudioFocus()
}
}
"message" -> {
val peerUri = params[1]
val msgText = params[2]
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")
messages.add(newMsg)
ChatsActivity.saveMessages()
Message.add(Message(aor, peerUri, R.drawable.arrow_down_green, msgText,
time.toLong(), true))
if (Utils.isVisible()) {
if (ua != uas[aorSpinner.selectedItemPosition])
if (ua != UserAgent.uas()[aorSpinner.selectedItemPosition])
aorSpinner.setSelection(account_index)
val i = Intent(applicationContext, ChatsActivity::class.java)
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
@ -676,7 +611,7 @@ class MainActivity : AppCompatActivity() {
}
}
if ((aorSpinner.selectedItemPosition >= 0) &&
(ua == uas[aorSpinner.selectedItemPosition]))
(ua == UserAgent.uas()[aorSpinner.selectedItemPosition]))
if (acc.vmNew > 0)
voicemailButton.setImageResource(R.drawable.voicemail_new)
else
@ -702,26 +637,19 @@ class MainActivity : AppCompatActivity() {
Log.d("Baresip", "Handling intent '$action'")
when (action) {
"call" -> {
if (!calls.isEmpty()) {
if (!Call.calls().isEmpty()) {
Toast.makeText(applicationContext, "You already have an active call!",
Toast.LENGTH_SHORT).show()
return
}
val uap = intent.getStringExtra("uap")
val ua = UserAgent.find(MainActivity.uas, uap)
val ua = UserAgent.find(uap)
if (ua == null) {
Log.e("Baresip", "onNewIntent did not find ua $uap")
Log.e("Baresip", "handleIntent 'call' did not find ua $uap")
return
}
val aor = ua.account.aor
if (ua != uas[aorSpinner.selectedItemPosition]) {
for (account_index in uas.indices) {
if (uas[account_index].account.aor == aor) {
aorSpinner.setSelection(account_index)
break
}
}
}
if (ua != UserAgent.uas()[aorSpinner.selectedItemPosition])
spinToAor(ua.account.aor)
makeCall = intent.getStringExtra("peer")
}
"answer" -> {
@ -731,9 +659,33 @@ class MainActivity : AppCompatActivity() {
rejectCall = intent.getStringExtra("callp")
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" -> {
val uap = intent.getStringExtra("uap")
val ua = UserAgent.find(MainActivity.uas, uap)
val ua = UserAgent.find(uap)
if (ua == null) {
Log.e("Baresip", "onNewIntent did not find ua $uap")
return
@ -743,14 +695,8 @@ class MainActivity : AppCompatActivity() {
"reply" -> {
val i = Intent(this@MainActivity, ChatsActivity::class.java)
val b = Bundle()
if (ua != uas[aorSpinner.selectedItemPosition]) {
for (account_index in uas.indices) {
if (uas[account_index].account.aor == aor) {
aorSpinner.setSelection(account_index)
break
}
}
}
if (ua != UserAgent.uas()[aorSpinner.selectedItemPosition])
spinToAor(aor)
b.putString("aor", aor)
b.putString("peer", intent.getStringExtra("peer"))
b.putBoolean("focus", true)
@ -759,19 +705,21 @@ class MainActivity : AppCompatActivity() {
}
"save" -> {
ChatsActivity.saveUaMessage(ua.account.aor,
intent.getStringExtra("time").toLong())
intent.getStringExtra("time").toLong(),
applicationContext.filesDir.absolutePath)
moveTaskToBack(true)
}
"delete" -> {
ChatsActivity.deleteUaMessage(ua.account.aor,
intent.getStringExtra("time").toLong())
intent.getStringExtra("time").toLong(),
applicationContext.filesDir.absolutePath)
moveTaskToBack(true)
}
}
nm.cancel(BaresipService.MESSAGE_NOTIFICATION_ID)
}
"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 b = Bundle()
b.putString("aor", ua.account.aor)
@ -816,7 +764,7 @@ class MainActivity : AppCompatActivity() {
override fun onDestroy() {
Log.d("Baresip", "Destroyed")
LocalBroadcastManager.getInstance(this).unregisterReceiver(serviceEventReceiver)
if (BaresipService.IS_SERVICE_RUNNING) {
if (BaresipService.isServiceRunning) {
baresipService.setAction("Kill")
startService(baresipService)
}
@ -844,6 +792,9 @@ class MainActivity : AppCompatActivity() {
}
R.id.accounts -> {
i = Intent(this, AccountsActivity::class.java)
val b = Bundle()
b.putString("accp", "")
i.putExtras(b)
startActivityForResult(i, ACCOUNTS_CODE)
return true
}
@ -860,7 +811,7 @@ class MainActivity : AppCompatActivity() {
}
R.id.quit -> {
Log.d("Baresip", "Quiting")
if (BaresipService.IS_SERVICE_RUNNING) {
if (BaresipService.isServiceRunning) {
baresipService.setAction("Stop");
startService(baresipService)
}
@ -876,11 +827,11 @@ class MainActivity : AppCompatActivity() {
ACCOUNTS_CODE -> {
uaAdapter.notifyDataSetChanged()
if (uas.size > 0) {
if (UserAgent.uas().size > 0) {
if ((aorSpinner.selectedItemPosition == -1) ||
(aorSpinner.selectedItemPosition >= uas.size))
(aorSpinner.selectedItemPosition >= UserAgent.uas().size))
aorSpinner.setSelection(0)
val acc = uas[aorSpinner.selectedItemPosition].account
val acc = UserAgent.uas()[aorSpinner.selectedItemPosition].account
if (acc.vmUri != "") {
if (acc.vmNew > 0)
voicemailButton.setImageResource(R.drawable.voicemail_new)
@ -898,7 +849,7 @@ class MainActivity : AppCompatActivity() {
}
ACCOUNT_CODE -> {
val acc = uas[aorSpinner.selectedItemPosition].account
val acc = UserAgent.uas()[aorSpinner.selectedItemPosition].account
if (acc.vmUri != "") {
if (acc.vmNew > 0)
voicemailButton.setImageResource(R.drawable.voicemail_new)
@ -912,14 +863,14 @@ class MainActivity : AppCompatActivity() {
CONTACTS_CODE -> {
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 -> {
if (resultCode == RESULT_OK) {
Utils.alertView(this, "Notice",
"You need to restart baresip in order to activate saved config!")
reload_config()
Api.reload_config()
}
if (resultCode == RESULT_CANCELED) {
Log.d("Baresip", "Edit config canceled")
@ -927,14 +878,17 @@ class MainActivity : AppCompatActivity() {
}
CALLS_CODE -> {
val acc = uas[aorSpinner.selectedItemPosition].account
val acc = UserAgent.uas()[aorSpinner.selectedItemPosition].account
if (resultCode == RESULT_OK) {
if (data != null)
callUri.setText(data.getStringExtra("peer_uri"))
if (data != null) {
val peerUri = data.getStringExtra("peer_uri")
callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(peerUri),
Utils.aorDomain(Utils.aorDomain(acc.aor))))
}
}
if (resultCode == RESULT_CANCELED) {
Log.d("Baresip", "History canceled")
if (CallHistory.aorHistory(history, acc.aor) == 0) {
if (CallHistory.aorHistorySize(acc.aor) == 0) {
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) {
if (ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.RECORD_AUDIO)
== PackageManager.PERMISSION_DENIED) {
@ -953,12 +915,16 @@ class MainActivity : AppCompatActivity() {
"You have not granted microphone permission.", Toast.LENGTH_SHORT).show()
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
val callp = ua_connect(ua.uap, uri)
val callp = Api.ua_connect(ua.uap, uri)
if (callp != "") {
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)
securityButton.visibility = View.INVISIBLE
callButton.visibility = View.INVISIBLE
@ -975,44 +941,27 @@ class MainActivity : AppCompatActivity() {
}
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 != "") {
Log.d("Baresip", "Adding outgoing call ${ua.uap}/$newCallp/$uri")
val newCall = Call(newCallp, ua, uri, "out", "transferring",
dtmfWatcher(newCallp))
calls.add(newCall)
call_stop_audio(call.callp)
call_start_audio(newCallp)
val err = call_connect(newCallp, uri)
Utils.dtmfWatcher(newCallp))
Call.calls().add(newCall)
Api.call_stop_audio(call.callp)
Api.call_start_audio(newCallp)
val err = Api.call_connect(newCallp, uri)
if (err == 0) {
if (ua == uas[aorSpinner.selectedItemPosition]) showCall(ua)
if (ua != UserAgent.uas()[aorSpinner.selectedItemPosition])
spinToAor(ua.account.aor)
showCall(ua)
} else {
call_start_audio(call.callp)
Api.call_start_audio(call.callp)
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 {
Log.w("Baresip", "ua_call_alloc ${ua.uap}/${call.callp} failed")
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())
}
Api.call_notify_sipfrag(call.callp, 500, "Call Error")
}
}
@ -1031,10 +980,13 @@ class MainActivity : AppCompatActivity() {
}
private fun showCall(ua: UserAgent) {
if (Call.uaCalls(calls, ua, "").size == 0) {
if (Call.uaCalls(ua, "").size == 0) {
callTitle.text = "Outgoing call to ..."
callUri.text.clear()
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
callButton.visibility = View.VISIBLE
hangupButton.visibility = View.INVISIBLE
@ -1043,8 +995,8 @@ class MainActivity : AppCompatActivity() {
holdButton.visibility = View.INVISIBLE
dtmf.visibility = View.INVISIBLE
} else {
val callsOut = Call.uaCalls(calls, ua, "out")
val callsIn = Call.uaCalls(calls, ua, "in")
val callsOut = Call.uaCalls(ua, "out")
val callsIn = Call.uaCalls(ua, "in")
val call: Call
if (callsOut.size > 0) {
call = callsOut[callsOut.size - 1]
@ -1056,7 +1008,8 @@ class MainActivity : AppCompatActivity() {
callTitle.text = "Incoming call from ..."
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) {
"outgoing", "transferring" -> {
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 {
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 makeCall = ""
var answerCall = ""
@ -1217,11 +1081,7 @@ class MainActivity : AppCompatActivity() {
const val CONTACT_CODE = 7
const val MESSAGES_CODE = 8
const val MESSAGE_CODE = 9
const val RECORD_AUDIO_PERMISSION = 1
const val HISTORY_SIZE = 100
const val MESSAGE_HISTORY_SIZE = 100
const val ONE_CALL_ONLY = true
}

View File

@ -1,7 +1,38 @@
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,
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)
}
}
}

View File

@ -4,37 +4,51 @@ import android.util.Log
class UserAgent (val uap: String) {
val account = Account(ua_account(uap))
val account = Account(Api.ua_account(uap))
fun register() {
if (account.regint > 0) {
Log.d("Baresip", "Registering ${account.aor} UA ${uap}")
if (ua_register(uap) != 0)
if (Api.ua_register(uap) != 0)
Log.e("Baresip", "Registering failed")
}
}
fun destroy() {
ua_destroy(uap)
Api.ua_destroy(uap)
}
companion object {
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)
fun uas(): ArrayList<UserAgent> {
return BaresipService.uas
}
fun status(): ArrayList<Int> {
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? {
val uap = ua_alloc(uri)
val uap = Api.ua_alloc(uri)
if (uap != "") return UserAgent(uap)
return null
}
fun find(uas: ArrayList<UserAgent>, uap: String): UserAgent? {
for (ua in uas) {
fun find(uap: String): UserAgent? {
for (ua in BaresipService.uas) {
if (ua.uap == uap) return ua
}
return null
@ -50,12 +64,10 @@ class UserAgent (val uap: String) {
fun register(uas: ArrayList<UserAgent>) {
for (ua in uas) {
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}")
}
}
}
}
external fun ua_account(ua: String): String

View File

@ -6,6 +6,8 @@ import android.support.v7.app.AlertDialog
import android.util.Log
import android.os.PowerManager
import android.app.KeyguardManager
import android.text.Editable
import android.text.TextWatcher
import java.io.*
@ -95,9 +97,13 @@ object Utils {
}
fun friendlyUri(uri: String, domain: String): String {
val user = uriUserPart(uri)
val host = uriHostPart(uri)
if (host == domain) return user else return "$user@$host"
if (uri.contains("@")) {
val user = uriUserPart(uri)
val host = uriHostPart(uri)
if (host == domain) return user else return "$user@$host"
} else {
return uri
}
}
fun aorDomain(aor: String): String {
@ -233,4 +239,23 @@ object Utils {
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())
}
}
}
}

View File

@ -26,6 +26,7 @@
android:textAlignment="gravity"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:paddingEnd="5dp"
android:textSize="18sp"
android:maxLines="1"
android:text="" />