diff --git a/app/build.gradle b/app/build.gradle index d1978ab2..8c1c8b5b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' android { compileSdkVersion = 25 @@ -20,6 +21,9 @@ android { 'proguard-rules.pro' } } + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } externalNativeBuild { cmake { path 'src/main/cpp/CMakeLists.txt' @@ -31,4 +35,8 @@ dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') compile 'com.android.support:appcompat-v7:25.2.0' compile 'com.android.support.constraint:constraint-layout:1.0.1' + compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" +} +repositories { + mavenCentral() } diff --git a/app/src/main/cpp/baresip.c b/app/src/main/cpp/baresip.c index aae5acdd..50970e72 100644 --- a/app/src/main/cpp/baresip.c +++ b/app/src/main/cpp/baresip.c @@ -314,22 +314,7 @@ Java_com_tutpro_baresip_MainActivity_ua_1aor(JNIEnv *env, jobject thiz, jstring return (*env)->NewStringUTF(env, ""); } -JNIEXPORT jboolean JNICALL -Java_com_tutpro_baresip_MainActivity_ua_1isregistered(JNIEnv *env, jobject thiz, jlong ua_ptr) -{ - struct ua *ua = (struct ua *)ua_ptr; - bool result; - - result = ua_isregistered(ua); - if (ua == NULL) { - LOGD("ua_ptr is null\n"); - } else { - LOGD("ua_ptr is NOT null\n"); - } - return result; -} - -JNIEXPORT void JNICALL +JNIEXPORT void JNICALL /* currently not in use */ Java_com_tutpro_baresip_MainActivity_ua_1current_1set(JNIEnv *env, jobject thiz, jstring javaAoR) { @@ -343,7 +328,7 @@ Java_com_tutpro_baresip_MainActivity_ua_1current_1set(JNIEnv *env, jobject thiz, return; } -JNIEXPORT jstring JNICALL +JNIEXPORT jstring JNICALL /* currently not in use */ Java_com_tutpro_baresip_MainActivity_ua_1current(JNIEnv *env, jobject thiz) { struct ua *current_ua = uag_current(); @@ -357,32 +342,18 @@ Java_com_tutpro_baresip_MainActivity_ua_1current(JNIEnv *env, jobject thiz) } JNIEXPORT jstring JNICALL -Java_com_tutpro_baresip_MainActivity_ua_1prev_1call(JNIEnv *env, jobject thiz, jstring javaUA) +Java_com_tutpro_baresip_MainActivity_aor_1ua(JNIEnv *env, jobject thiz, + jstring javaAoR) { - const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0); - struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10); - (*env)->ReleaseStringUTFChars(env, javaUA, native_ua); + const char *native_aor = (*env)->GetStringUTFChars(env, javaAoR, 0); + struct ua *ua = uag_find_aor(native_aor); + char ua_buf[256]; - struct call *call = ua_prev_call(ua); - char call_buf[256]; - if (call == NULL) - call_buf[0] = '\0'; + if (ua == NULL) + ua_buf[0] = '\0'; else - sprintf(call_buf, "%lu", (unsigned long)call); - return (*env)->NewStringUTF(env, call_buf); -} - -JNIEXPORT jstring JNICALL -Java_com_tutpro_baresip_MainActivity_ua_1call(JNIEnv *env, jobject thiz, jstring javaUA) -{ - const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0); - struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10); - (*env)->ReleaseStringUTFChars(env, javaUA, native_ua); - - struct call *call = ua_call(ua); - char call_buf[256]; - sprintf(call_buf, "%lu", (unsigned long)call); - return (*env)->NewStringUTF(env, call_buf); + sprintf(ua_buf, "%lu", (unsigned long)ua); + return (*env)->NewStringUTF(env, ua_buf); } JNIEXPORT jstring JNICALL @@ -398,28 +369,26 @@ Java_com_tutpro_baresip_MainActivity_call_1peeruri(JNIEnv *env, jobject thiz, js JNIEXPORT jstring JNICALL Java_com_tutpro_baresip_MainActivity_ua_1connect(JNIEnv *env, jobject thiz, - jstring uri) { + jstring javaUA, jstring javaURI) { struct call *call; struct ua *ua; int err; - const char *native_uri = (*env)->GetStringUTFChars(env, uri, 0); + const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0); + const char *native_uri = (*env)->GetStringUTFChars(env, javaURI, 0); char call_buf[256]; - LOGD("connecting to %s\n", native_uri); - ua = uag_current(); - if (ua != NULL) { - err = ua_connect(ua, &call, NULL, native_uri, NULL, VIDMODE_ON); - if (err) { - LOGW("connecting to %s failed with error %d\n", native_uri, err); - call_buf[0] = '\0'; - } else { - sprintf(call_buf, "%lu", (unsigned long)call); - } - } else { - LOGE("no current ua\n"); + LOGD("connecting ua %s to %s\n", native_ua, native_uri); + ua = (struct ua *)strtoul(native_ua, NULL, 10); + err = ua_connect(ua, &call, NULL, native_uri, NULL, VIDMODE_ON); + if (err) { + LOGW("connecting to %s failed with error %d\n", native_uri, err); call_buf[0] = '\0'; + } else { + sprintf(call_buf, "%lu", (unsigned long)call); } - (*env)->ReleaseStringUTFChars(env, uri, native_uri); + + (*env)->ReleaseStringUTFChars(env, javaUA, native_ua); + (*env)->ReleaseStringUTFChars(env, javaURI, native_uri); return (*env)->NewStringUTF(env, call_buf); } @@ -440,7 +409,8 @@ Java_com_tutpro_baresip_MainActivity_ua_1answer(JNIEnv *env, jobject thiz, } -JNIEXPORT jint JNICALL Java_com_tutpro_baresip_MainActivity_call_1hold(JNIEnv *env, jobject thiz, +JNIEXPORT jint JNICALL +Java_com_tutpro_baresip_MainActivity_call_1hold(JNIEnv *env, jobject thiz, jstring javaCall) { const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0); LOGD("holding call %s\n", native_call); @@ -449,7 +419,8 @@ JNIEXPORT jint JNICALL Java_com_tutpro_baresip_MainActivity_call_1hold(JNIEnv * return res; } -JNIEXPORT jint JNICALL Java_com_tutpro_baresip_MainActivity_call_1unhold(JNIEnv *env, jobject thiz, +JNIEXPORT jint JNICALL +Java_com_tutpro_baresip_MainActivity_call_1unhold(JNIEnv *env, jobject thiz, jstring javaCall) { const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0); LOGD("holding call %s\n", native_call); @@ -477,7 +448,7 @@ Java_com_tutpro_baresip_MainActivity_ua_1hangup(JNIEnv *env, jobject thiz, } JNIEXPORT void JNICALL -Java_com_tutpro_baresip_MainActivity_contacts_1remove(JNIEnv *env, jobject thiz) { +Java_com_tutpro_baresip_MainActivity_00024Companion_contacts_1remove(JNIEnv *env, jobject thiz) { struct le *le; le = list_head(contact_list(baresip_contacts())); while ((le = list_head(contact_list(baresip_contacts())))) { @@ -488,8 +459,8 @@ Java_com_tutpro_baresip_MainActivity_contacts_1remove(JNIEnv *env, jobject thiz) } JNIEXPORT void JNICALL -Java_com_tutpro_baresip_MainActivity_contact_1add(JNIEnv *env, jobject thiz, - jstring javaContact) { +Java_com_tutpro_baresip_MainActivity_00024Companion_contact_1add(JNIEnv *env, jobject thiz, + jstring javaContact) { struct pl pl_addr; const struct list *lst; struct le *le; diff --git a/app/src/main/java/com/tutpro/baresip/AboutActivity.java b/app/src/main/java/com/tutpro/baresip/AboutActivity.java deleted file mode 100644 index a13d75f0..00000000 --- a/app/src/main/java/com/tutpro/baresip/AboutActivity.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.tutpro.baresip; - -import android.content.Intent; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.util.Log; -import android.view.MenuItem; -import android.widget.TextView; - -public class AboutActivity extends AppCompatActivity { - - private TextView aboutView; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_about); - aboutView = (TextView)findViewById(R.id.aboutText); - aboutView.setEnabled(false); - aboutView.setText(getString(R.string.aboutText)); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - Intent i = new Intent(); - setResult(RESULT_CANCELED, i); - finish(); - break; - } - return true; - } -} - diff --git a/app/src/main/java/com/tutpro/baresip/Account.java b/app/src/main/java/com/tutpro/baresip/Account.java deleted file mode 100644 index 4030da99..00000000 --- a/app/src/main/java/com/tutpro/baresip/Account.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.tutpro.baresip; - -public class Account { - - private String ua, aor, status; - private int status_image; - - public Account(String ua, String aor) { - this.ua = ua; - this.aor = aor; - this.status = ""; - this.status_image = 0; - } - - public void setStatusImage(int status_image) { - this.status_image = status_image; - } - public int getStatusImage() { - return status_image; - } - - public String getUA() { - return ua; - } - public String getAoR() { - return aor; - } - - public void setStatus(String status) { this.status = status; } - public String getStatus() { - return status; - } - -} diff --git a/app/src/main/java/com/tutpro/baresip/AccountSpinnerAdapter.java b/app/src/main/java/com/tutpro/baresip/AccountSpinnerAdapter.java deleted file mode 100644 index c5cf72bc..00000000 --- a/app/src/main/java/com/tutpro/baresip/AccountSpinnerAdapter.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.tutpro.baresip; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.TextView; - -import java.util.ArrayList; - -public class AccountSpinnerAdapter extends ArrayAdapter { - - private ArrayList images; - private ArrayList aors; - private Context context; - - public AccountSpinnerAdapter(Context context, ArrayList aors, ArrayList images) { - super(context, android.R.layout.simple_spinner_item, images); - this.images = images; - this.aors = aors; - this.context = context; - } - - @Override - public View getDropDownView(int position, View convertView, ViewGroup parent) { - return getImageForPosition(position, convertView, parent); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - return getImageForPosition(position, convertView, parent); - } - - private View getImageForPosition(int position, View convertView, ViewGroup parent) { - LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - View row = inflater.inflate(R.layout.account_spinner, parent, false); - TextView textView = (TextView) row.findViewById(R.id.spinnerText); - textView.setText(aors.get(position)); - textView.setTextSize(17); - ImageView imageView = (ImageView) row.findViewById(R.id.spinnerImage); - imageView.setImageResource(images.get(position)); - return row; - } -} - diff --git a/app/src/main/java/com/tutpro/baresip/Call.java b/app/src/main/java/com/tutpro/baresip/Call.java deleted file mode 100644 index ffdd9442..00000000 --- a/app/src/main/java/com/tutpro/baresip/Call.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.tutpro.baresip; - -public class Call { - - private String ua, call, peer_uri, status; - Boolean hold; - - public Call(String ua, String call, String peer_uri, String status) { - this.ua = ua; - this.call = call; - this.peer_uri = peer_uri; - this.status = status; - } - - public String getUA() { - return ua; - } - - public String getCall() { - return call; - } - - public String getPeerURI() { - return peer_uri; - } - - public void setStatus(String status) { - this.status = status; - } - public String getStatus() { - return status; - } - - public void setHold(Boolean hold) { - this.hold = hold; - } - public Boolean getHold() { - return hold; - } -} diff --git a/app/src/main/java/com/tutpro/baresip/Contact.java b/app/src/main/java/com/tutpro/baresip/Contact.java deleted file mode 100644 index c42d11b6..00000000 --- a/app/src/main/java/com/tutpro/baresip/Contact.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.tutpro.baresip; - -public class Contact { - - private String name, uri; - - public Contact(String name, String uri) { - this.name = name; - this.uri = uri; - } - - public String getName() { - return name; - } - - public String getURI() { - return uri; - } - -} diff --git a/app/src/main/java/com/tutpro/baresip/EditAccountsActivity.java b/app/src/main/java/com/tutpro/baresip/EditAccountsActivity.java deleted file mode 100644 index 348e8cac..00000000 --- a/app/src/main/java/com/tutpro/baresip/EditAccountsActivity.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.tutpro.baresip; - -import android.content.Intent; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.util.Log; -import android.util.TypedValue; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.widget.EditText; -import android.widget.TextView; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.OutputStreamWriter; - -public class EditAccountsActivity extends AppCompatActivity { - - private EditText editText; - private String path; - private File file; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_edit_accounts); - - editText = (EditText)findViewById(R.id.editAccounts); - path = getApplicationContext().getFilesDir().getAbsolutePath() + "/accounts"; - file = new File(path); - String content; - - if (!file.exists()) { - Log.e("Baresip", "Failed to find accounts file"); - content = "No accounts"; - } else { - Log.e("Baresip", "Found accounts file"); - int length = (int)file.length(); - byte[] bytes = new byte[length]; - try { - FileInputStream in = new FileInputStream(file); - try { - in.read(bytes); - } finally { - in.close(); - } - content = new String(bytes); - } catch (java.io.IOException e) { - Log.e("Baresip", "Failed to read accounts file: " + e.toString()); - content = "Failed to read account file"; - } - } - - Log.d("Baresip", "Content length is: " + content.length()); - - editText.setTextSize(TypedValue.COMPLEX_UNIT_PX, - getResources().getDimension(R.dimen.textsize)); - editText.setText(content, TextView.BufferType.EDITABLE); - } - - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.edit_menu, menu); - return true; - } - - public boolean onOptionsItemSelected(MenuItem item) { - Intent i = new Intent(); - switch (item.getItemId()) { - case R.id.save: - try { - FileOutputStream fOut = - new FileOutputStream(file.getAbsoluteFile(), false); - OutputStreamWriter fWriter = new OutputStreamWriter(fOut); - String res = editText.getText().toString(); - try { - fWriter.write(res); - fWriter.close(); - fOut.close(); - } catch (java.io.IOException e) { - Log.e("Baresip", "Failed to write accounts file: " + - e.toString()); - } - } catch (java.io.FileNotFoundException e) { - Log.e("Baresip", "Failed to find accounts file: " + - e.toString()); - } - Log.d("Baresip", "Updated accounts file"); - setResult(RESULT_OK, i); - finish(); - return true; - case R.id.cancel: - case android.R.id.home: - setResult(RESULT_CANCELED, i); - finish(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } -} - diff --git a/app/src/main/java/com/tutpro/baresip/EditConfigActivity.java b/app/src/main/java/com/tutpro/baresip/EditConfigActivity.java deleted file mode 100644 index 3832e0df..00000000 --- a/app/src/main/java/com/tutpro/baresip/EditConfigActivity.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.tutpro.baresip; - -import android.content.Intent; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.util.Log; -import android.util.TypedValue; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.widget.EditText; -import android.widget.TextView; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.OutputStreamWriter; - -public class EditConfigActivity extends AppCompatActivity { - - private EditText editText; - private String path; - private File file; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_edit_config); - - editText = (EditText)findViewById(R.id.editConfig); - path = getApplicationContext().getFilesDir().getAbsolutePath() + "/config"; - // path = "/sdcard/baresip/config"; - file = new File(path); - String content; - - if (!file.exists()) { - Log.e("Baresip", "Failed to find config file"); - content = "No config"; - } else { - Log.e("Baresip", "Found config file"); - int length = (int)file.length(); - byte[] bytes = new byte[length]; - try { - FileInputStream in = new FileInputStream(file); - try { - in.read(bytes); - } finally { - in.close(); - } - content = new String(bytes); - } catch (java.io.IOException e) { - Log.e("Baresip", "Failed to read config file: " + e.toString()); - content = "Failed to read account file"; - } - } - - Log.d("Baresip", "Content length is: " + content.length()); - - editText.setTextSize(TypedValue.COMPLEX_UNIT_PX, - getResources().getDimension(R.dimen.textsize)); - editText.setText(content, TextView.BufferType.EDITABLE); - } - - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.edit_menu, menu); - return true; - } - - public boolean onOptionsItemSelected(MenuItem item) { - Intent i = new Intent(this, MainActivity.class); - switch (item.getItemId()) { - case R.id.save: - try { - FileOutputStream fOut = - new FileOutputStream(file.getAbsoluteFile(), false); - OutputStreamWriter fWriter = new OutputStreamWriter(fOut); - String res = editText.getText().toString(); - try { - fWriter.write(res); - fWriter.close(); - fOut.close(); - } catch (java.io.IOException e) { - Log.e("Baresip", "Failed to write config file: " + - e.toString()); - } - } catch (java.io.FileNotFoundException e) { - Log.e("Baresip", "Failed to find config file: " + - e.toString()); - } - Log.d("Baresip", "Updated config file"); - setResult(RESULT_OK, i); - finish(); - return true; - case R.id.cancel: - case android.R.id.home: - setResult(RESULT_CANCELED, i); - finish(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } -} diff --git a/app/src/main/java/com/tutpro/baresip/EditContactsActivity.java b/app/src/main/java/com/tutpro/baresip/EditContactsActivity.java deleted file mode 100644 index a5cc3c34..00000000 --- a/app/src/main/java/com/tutpro/baresip/EditContactsActivity.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.tutpro.baresip; - -import android.content.Intent; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.util.Log; -import android.util.TypedValue; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.widget.ArrayAdapter; -import android.widget.EditText; -import android.widget.TextView; - -import java.io.File; -import java.util.ArrayList; - -public class EditContactsActivity extends AppCompatActivity { - - private EditText editText; - static public ArrayList Contacts = new java.util.ArrayList<>(); - static public ArrayList Names = new java.util.ArrayList<>(); - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_edit_contacts); - editText = (EditText)findViewById(R.id.editText); - String path = getApplicationContext().getFilesDir().getAbsolutePath() + "/contacts"; - File file = new File(path); - String content = Utils.getFileContents(file); - Log.d("Baresip", "Contacts length is: " + content.length()); - editText.setTextSize(TypedValue.COMPLEX_UNIT_PX, - getResources().getDimension(R.dimen.textsize)); - editText.setText(content, TextView.BufferType.EDITABLE); - } - - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.edit_menu, menu); - return true; - } - - public boolean onOptionsItemSelected(MenuItem item) { - Intent i = new Intent(this, MainActivity.class); - switch (item.getItemId()) { - case R.id.save: - String path = getApplicationContext().getFilesDir().getAbsolutePath() + "/contacts"; - File file = new File(path); - Utils.putFileContents(file, editText.getText().toString()); - Log.d("Baresip", "Updated contacts file"); - MainActivity.contacts_remove(); - updateContactsAndNames(); - // MainActivity.CalleeAdapter.notifyDataSetChanged(); - MainActivity.CalleeAdapter = new ArrayAdapter - (this,android.R.layout.select_dialog_item, Names); - MainActivity.callee.setThreshold(2); - MainActivity.callee.setAdapter(MainActivity.CalleeAdapter); - i.putExtra("action", "save"); - setResult(RESULT_OK, i); - finish(); - return true; - case R.id.cancel: - case android.R.id.home: - i.putExtra("action", "cancel"); - setResult(RESULT_OK, i); - finish(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - static public void updateContactsAndNames() { - String path = MainActivity.mainActivityContext.getFilesDir().getAbsolutePath() + "/contacts"; - File file = new File(path); - String[] lines = Utils.getFileContents(file).split("\n"); - String name, uri; - Contacts.clear(); - Names.clear(); - for (String line : lines) { - line.trim(); - if (line.startsWith("#") || line.length() == 0) continue; - String[] parts = line.split("\""); - if (parts.length != 3) { - Log.e("Baresip", "Invalid contacts line: " + line); - continue; - } - name = parts[1]; - if (name.length() < 2) { - Log.e("Baresip", "Too short contact display name: " + name); - continue; - } - uri = parts[2].trim(); - if (!uri.startsWith("<") || !uri.contains(">")) { - Log.e("Baresip", "Invalid contact uri: " + uri); - continue; - } - MainActivity.contact_add("\"" + name + "\" " + uri); - if (uri.indexOf(";access") > 0) continue; - uri = uri.substring(1, uri.indexOf(">")); - Log.d("Baresip", "Adding contact name/uri: " + name + "/" + uri); - Contacts.add(new Contact(name, uri)); - Names.add(name); - } - } - - static public String findContactURI(String name) { - for (Contact c : Contacts) { - if (c.getName().equals(name)) return c.getURI(); - } - return name; - } - -} - diff --git a/app/src/main/java/com/tutpro/baresip/History.java b/app/src/main/java/com/tutpro/baresip/History.java deleted file mode 100644 index 111126f9..00000000 --- a/app/src/main/java/com/tutpro/baresip/History.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.tutpro.baresip; - -import java.io.Serializable; -import java.util.GregorianCalendar; - -public class History implements Serializable { - - private static final long serialVersionUID = -299482035708790407L; - - private String ua, call, aor, peer_uri, direction; - private GregorianCalendar time; - private Boolean connected; - - public History(String ua, String call, String aor, String peer_uri, String direction, - Boolean connected) { - this.ua = ua; - this.call = call; - this.aor = aor; - this.peer_uri = peer_uri; - this.direction = direction; - this.time = new GregorianCalendar(); - this.connected = connected; - } - - public String getUA() { return ua; } - public String getCall() { return call; } - public String getAoR() { return aor; } - public String getPeerURI() { return peer_uri; } - public String getDirection() { return direction; } - public GregorianCalendar getTime() { return time; } - public Boolean getConnected() { return connected; } -} diff --git a/app/src/main/java/com/tutpro/baresip/HistoryActivity.java b/app/src/main/java/com/tutpro/baresip/HistoryActivity.java deleted file mode 100644 index 321342fa..00000000 --- a/app/src/main/java/com/tutpro/baresip/HistoryActivity.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.tutpro.baresip; - -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; -import android.util.Log; -import android.view.MenuItem; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ListView; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.GregorianCalendar; - -public class HistoryActivity extends AppCompatActivity { - - ArrayList uaHistory = new ArrayList<>(); - ArrayList posAtHistory = new ArrayList<>(); - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_history); - - final ListView listview = (ListView) findViewById(R.id.history); - - generate_ua_history(); - - final HistoryListAdapter adapter = new HistoryListAdapter(this, uaHistory); - listview.setAdapter(adapter); - listview.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, final View view, int position, long id) { - HistoryRow row = uaHistory.get(position); - Intent i = new Intent(); - i.putExtra("peer_uri", row.getPeerURI()); - setResult(RESULT_OK, i); - finish(); - } - }); - - listview.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { - @Override - public boolean onItemLongClick(AdapterView parent, View view, final int pos, long id) { - DialogInterface.OnClickListener dialogClickListener = - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which){ - case DialogInterface.BUTTON_POSITIVE: - MainActivity.History.remove((posAtHistory.get(pos)).intValue()); - generate_ua_history(); - if (uaHistory.size() == 0) { - Intent i = new Intent(); - setResult(RESULT_CANCELED, i); - finish(); - } - adapter.notifyDataSetChanged(); - break; - case DialogInterface.BUTTON_NEGATIVE: - break; - } - } - }; - AlertDialog.Builder builder = - new AlertDialog.Builder(HistoryActivity.this, - R.style.Theme_AppCompat); - builder.setMessage("Do you want to delete " + - MainActivity.History.get(pos).getPeerURI() + "?") - .setPositiveButton("Yes", dialogClickListener) - .setNegativeButton("No", dialogClickListener).show(); - return true; - } - }); - - listview.setLongClickable(true); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - Log.d("Baresip", "Back array was pressed"); - Intent i = new Intent(); - setResult(RESULT_CANCELED, i); - finish(); - break; - } - return true; - } - - private void generate_ua_history() { - uaHistory.clear(); - posAtHistory.clear(); - for (Integer i = MainActivity.History.size() - 1; i >= 0; i--) { - History h = MainActivity.History.get(i); - if (h.getAoR().equals(MainActivity.ua_aor(MainActivity.ua_current()))) { - String time; - if (isToday(h.getTime())) { - SimpleDateFormat fmt = new SimpleDateFormat("HH:mm"); - time = fmt.format(h.getTime().getTime()); - } else { - SimpleDateFormat fmt = new SimpleDateFormat("MMM dd"); - time = fmt.format(h.getTime().getTime()); - } - if (h.getDirection().equals("in")) { - if (h.getConnected()) { - uaHistory.add(new HistoryRow(h.getPeerURI(), R.drawable.arrow_down_green, time)); - } else { - uaHistory.add(new HistoryRow(h.getPeerURI(), R.drawable.arrow_down_red, time)); - } - } else { - if (h.getConnected()) { - uaHistory.add(new HistoryRow(h.getPeerURI(), R.drawable.arrow_up_green, time)); - } else { - uaHistory.add(new HistoryRow(h.getPeerURI(), R.drawable.arrow_up_red, time)); - } - } - posAtHistory.add(i); - } - } - } - - private Boolean isToday(GregorianCalendar time) { - GregorianCalendar now = new GregorianCalendar(); - return now.get(Calendar.YEAR) == time.get(Calendar.YEAR) && - now.get(Calendar.MONTH) == time.get(Calendar.MONTH) && - now.get(Calendar.DAY_OF_MONTH) == time.get(Calendar.DAY_OF_MONTH); - } - -} diff --git a/app/src/main/java/com/tutpro/baresip/HistoryListAdapter.java b/app/src/main/java/com/tutpro/baresip/HistoryListAdapter.java deleted file mode 100644 index 4940addf..00000000 --- a/app/src/main/java/com/tutpro/baresip/HistoryListAdapter.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.tutpro.baresip; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.TextView; - -import java.util.ArrayList; - -public class HistoryListAdapter extends ArrayAdapter { - - private Context context; - private ArrayList rows; - - public HistoryListAdapter(Context context, ArrayList rows) { - super(context, R.layout.history_row, rows); - this.context = context; - this.rows = rows; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - HistoryRow row = rows.get(position); - LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - View rowView = inflater.inflate(R.layout.history_row, parent, false); - ImageView directionView = (ImageView) rowView.findViewById(R.id.direction); - directionView.setImageResource(row.getDirection()); - TextView peerURIView = (TextView) rowView.findViewById(R.id.peer_uri); - peerURIView.setText(row.getPeerURI()); - TextView timeView = (TextView) rowView.findViewById(R.id.time); - timeView.setText(row.getTime()); - return rowView; - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/tutpro/baresip/HistoryRow.java b/app/src/main/java/com/tutpro/baresip/HistoryRow.java deleted file mode 100644 index a18daed4..00000000 --- a/app/src/main/java/com/tutpro/baresip/HistoryRow.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.tutpro.baresip; - -public class HistoryRow { - - private String peer_uri; - private Integer direction; - private String time; - - public HistoryRow(String peer_uri, Integer direction, String time) { - this.peer_uri = peer_uri; - this.direction = direction; - this.time = time; - } - - public String getPeerURI() { return peer_uri; } - public Integer getDirection() { return direction; } - public String getTime() { return time; } - -} diff --git a/app/src/main/java/com/tutpro/baresip/MainActivity.java b/app/src/main/java/com/tutpro/baresip/MainActivity.java deleted file mode 100644 index a3b4108f..00000000 --- a/app/src/main/java/com/tutpro/baresip/MainActivity.java +++ /dev/null @@ -1,737 +0,0 @@ -package com.tutpro.baresip; - -import android.Manifest; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.res.AssetManager; -import android.content.res.Configuration; -import android.graphics.Color; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; -import android.os.Bundle; -import android.widget.*; -import android.widget.RelativeLayout.LayoutParams; -import android.widget.AutoCompleteTextView; -import android.view.*; -import android.util.Log; -import android.content.Context; - -import java.util.*; -import java.io.*; - -public class MainActivity extends AppCompatActivity { - - static Context mainActivityContext; - static Boolean running = false; - static AutoCompleteTextView callee; - static RelativeLayout layout; - static Button callButton; - static Button holdButton; - static ArrayList Accounts = new ArrayList<>(); - static ArrayList AoRs = new ArrayList<>(); - static ArrayList Images = new ArrayList<>(); - static AccountSpinnerAdapter AccountAdapter = null; - static ArrayAdapter CalleeAdapter = null; - static ArrayList In = new ArrayList<>(); - static ArrayList Out = new ArrayList<>(); - static ArrayList History = new ArrayList<>(); - static String CurrentUA = null; - - private static final int RECORD_AUDIO_PERMISSION = 1; - private static final int EDIT_ACCOUNTS_CODE = 1; - private static final int EDIT_CONTACTS_CODE = 2; - private static final int EDIT_CONFIG_CODE = 3; - private static final int HISTORY_CODE = 4; - private static final int ABOUT_CODE = 5; - - @Override - protected void onCreate(Bundle savedInstanceState) { - - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - mainActivityContext = getApplicationContext(); - - Spinner AoRSpinner = (Spinner) findViewById(R.id.AoRList); - Log.i("Baresip", "Setting AccountAdapter"); - AccountAdapter = new AccountSpinnerAdapter(getApplicationContext(), AoRs, Images); - AoRSpinner.setAdapter(AccountAdapter); - AoRSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - String aor = AoRs.get(position); - Log.i("Baresip", "Setting " + aor + " current"); - ua_current_set(aor); - CurrentUA = ua_current(); - ArrayList out = uaCalls(Out, CurrentUA); - if (out.size() == 0) { - callee.setHint("Callee"); - callButton.setText("Call"); - if (aorHasHistory(History, aor)) { - holdButton.setText("History"); - holdButton.setVisibility(View.VISIBLE); - } else { - holdButton.setVisibility(View.INVISIBLE); - } - } else { - callee.setText(out.get(0).getPeerURI()); - callButton.setText(out.get(0).getStatus()); - if (out.get(0).getStatus().equals("Hangup")) { - if (out.get(0).getHold()) { - holdButton.setText("Unhold"); - } else { - holdButton.setText("Hold"); - } - holdButton.setVisibility(View.VISIBLE); - } else { - holdButton.setVisibility(View.INVISIBLE); - } - } - ArrayList in = uaCalls(In, CurrentUA); - int view_count = layout.getChildCount(); - Log.d("Baresip", "View count is " + view_count); - if (view_count > 5) { - layout.removeViews(5, view_count - 5); - } - for (Call c: in) { - for (int call_index = 0; call_index < In.size(); call_index++) { - addCallViews(c,(call_index + 1) * 10); - } - } - } - @Override - public void onNothingSelected(AdapterView parent) { - Log.i("Baresip", "Nothing selected"); - } - }); - - String[] assets = {"accounts", "contacts", "config", "busy.wav", "callwaiting.wav", - "error.wav", "message.wav", "notfound.wav", "ring.wav", "ringback.wav"}; - final String path = mainActivityContext.getFilesDir().getPath(); - Log.d("Baresip", "path is: " + path); - File file = new File(path); - if (!file.exists()) { - Log.d("Baresip", "Creating baresip directory"); - try { - new File(path).mkdirs(); - } catch (Error e) { - Log.e("Baresip", "Failed to create directory: " + - e.toString()); - } - } - for (String a : assets) { - file = new File(path + "/" + a); - if (!file.exists()) { - Log.d("Baresip", "Copying asset " + a); - copyAssetToFile(a, path + "/" + a); - } else { - Log.d("Baresip", "Asset " + a + " already copied"); - } - } - - file = new File(path, "history"); - try { - FileInputStream fis = new FileInputStream(file); - ObjectInputStream ois = new ObjectInputStream(fis); - History = (ArrayList)ois.readObject(); - Log.d("Baresip", "Restored History"); - ois.close(); - fis.close(); - } catch (Exception e) { - Log.w("Baresip", "InputStream exception: - " + e.toString()); - } - - if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) - != PackageManager.PERMISSION_GRANTED) { - Log.d("Baresip", "Baresip does not have RECORD_AUDIO permission"); - ActivityCompat.requestPermissions(this, - new String[]{Manifest.permission.RECORD_AUDIO}, RECORD_AUDIO_PERMISSION); - } - - if (!running) { - Log.i("Baresip", "Starting Baresip with path " + path); - new Thread(new Runnable() { - public void run() { - baresipStart(path); - } - }).start(); - running = true; - } - - layout = (RelativeLayout) findViewById(R.id.mainActivityLayout); - - callee = (AutoCompleteTextView)findViewById(R.id.callee); - EditContactsActivity.updateContactsAndNames(); - CalleeAdapter = new ArrayAdapter - (this,android.R.layout.select_dialog_item, EditContactsActivity.Names); - callee.setThreshold(2); - callee.setAdapter(CalleeAdapter); - - callButton = (Button)findViewById(R.id.callButton); - callButton.setText("Call"); - callButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (callButton.getText().toString().equals("Call")) { - call(((EditText) findViewById(R.id.callee)).getText().toString()); - } else { - Log.i("Baresip", "Canceling UA " + CurrentUA + " call " + - Out.get(0).getCall() + " to " + - ((EditText) findViewById(R.id.callee)).getText()); - ua_hangup(CurrentUA, Out.get(0).getCall(), 486, "Rejected"); - } - } - }); - - holdButton = (Button)findViewById(R.id.holdButton); - if (aorHasHistory(History, ua_aor(ua_current()))) { - holdButton.setText("History"); - } else { - holdButton.setVisibility(View.INVISIBLE); - } - holdButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - switch (holdButton.getText().toString()) { - case "Hold": - Log.i("Baresip", "Holding up " + - ((EditText) findViewById(R.id.callee)).getText()); - call_hold(Out.get(0).getCall()); - Out.get(0).setHold(true); - holdButton.setText("Unhold"); - break; - case "Unhold": - Log.i("Baresip", "Unholding " + - ((EditText) findViewById(R.id.callee)).getText()); - call_unhold(Out.get(0).getCall()); - Out.get(0).setHold(false); - holdButton.setText("Hold"); - break; - case "History": - Intent i = new Intent(MainActivity.this, HistoryActivity.class); - startActivityForResult(i, HISTORY_CODE); - break; - } - } - }); - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { - Log.d("Baresip", "Screen orientation change to landscape"); - } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){ - Log.d("Baresip", "Screen orientation change to portrait"); - } - } - - @Override - public void onRequestPermissionsResult(int requestCode, - String permissions[], int[] grantResults) { - switch (requestCode) { - case RECORD_AUDIO_PERMISSION: { - if (grantResults.length > 0 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - Log.d("Baresip", "RECORD_AUDIO permission granted"); - } else { - Log.d("Baresip", "RECORD_AUDIO permission NOT granted"); - } - return; - } - default: - Log.e("Baresip", "Unknown permissions request code: " + requestCode); - } - } - - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.main_menu, menu); - return true; - } - - public boolean onOptionsItemSelected(MenuItem item) { - Intent i; - switch (item.getItemId()) { - case R.id.accounts: - i = new Intent(this, EditAccountsActivity.class); - startActivityForResult(i, EDIT_ACCOUNTS_CODE); - return true; - case R.id.contacts: - i = new Intent(this, EditContactsActivity.class); - startActivityForResult(i, EDIT_CONTACTS_CODE); - return true; - case R.id.config: - i = new Intent(this, EditConfigActivity.class); - startActivityForResult(i, EDIT_CONFIG_CODE); - return true; - case R.id.about: - i = new Intent(this, AboutActivity.class); - startActivityForResult(i, ABOUT_CODE); - return true; - case R.id.quit: - if (running) { - Log.d("Baresip", "Stopping"); - final String path = mainActivityContext.getFilesDir().getPath(); - File file = new File(path,"history"); - try { - FileOutputStream fos = new FileOutputStream(file); - ObjectOutputStream oos = new ObjectOutputStream(fos); - oos.writeObject(History); - oos.close(); - fos.close(); - } catch (IOException e) { - Log.w("Baresip", "OutputStream exception: " + e.toString()); - e.printStackTrace(); - } - History.clear(); - Accounts.clear(); - AoRs.clear(); - Images.clear(); - baresipStop(); - running = false; - } - finish(); - System.exit(0); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == EDIT_ACCOUNTS_CODE) { - if(resultCode == RESULT_OK) { - AlertDialog alertDialog = new AlertDialog.Builder(this).create(); - alertDialog.setTitle("Alert"); - alertDialog.setMessage("You need to restart baresip in order to activate saved accounts!"); - alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); - alertDialog.show(); - } - if (resultCode == RESULT_CANCELED) { - Log.d("Baresip", "Edit accounts canceled"); - } - } - if (requestCode == EDIT_CONFIG_CODE) { - if(resultCode == RESULT_OK) { - Utils.alertView(this, - "You need to restart baresip in order to activate saved config!"); - reload_config(); - } - if (resultCode == RESULT_CANCELED) { - Log.d("Baresip", "Edit config canceled"); - } - } - if (requestCode == HISTORY_CODE) { - if(resultCode == RESULT_OK) { - callee.setText(data.getStringExtra("peer_uri")); - } - if(resultCode == RESULT_CANCELED) { - Log.d("Baresip", "History canceled"); - if (!aorHasHistory(History, ua_aor(ua_current()))) { - holdButton.setVisibility(View.INVISIBLE); - } - } - } - if ((requestCode == EDIT_CONTACTS_CODE) || (requestCode == ABOUT_CODE)) { - Log.d("Baresip", "Back arrow or Cancel pressed at request: " + requestCode); - } - } - - public void addAccount(String ua) { - String aor = ua_aor(ua); - Log.d("Baresip", "Adding account " + ua + " with AoR " + aor); - Accounts.add(new Account(ua, aor)); - AoRs.add(aor); - Images.add(R.drawable.yellow); - runOnUiThread(new Runnable() { - @Override - public void run() { - AccountAdapter.notifyDataSetChanged(); - } - }); - } - - private void call(String callee) { - if (callee.length() == 0) return; - String uri = EditContactsActivity.findContactURI(callee); - if (!uri.startsWith("sip:")) uri = "sip:" + uri; - if (!uri.contains("@")) { - String aor = ua_aor(CurrentUA); - String host = aor.substring(aor.indexOf("@") + 1); - uri = uri + "@" + host; - } - ((EditText) findViewById(R.id.callee)).setText(uri); - Log.i("Baresip", "Calling " + uri); - String call = ua_connect(uri); - if (!call.equals("")) { - Log.i("Baresip", "Adding outgoing call " + CurrentUA + "/" + call + - "/" + uri); - Out.add(new Call(CurrentUA, call, uri, "Cancel")); - // History.add(new History(ua_aor(CurrentUA), uri, "out")); - callButton.setText("Cancel"); - holdButton.setVisibility(View.INVISIBLE); - } - } - - private void copyAssetToFile(String asset, String path) { - try { - AssetManager assetManager = getAssets(); - InputStream is = assetManager.open(asset); - OutputStream os = new FileOutputStream(path); - byte [] buffer = new byte[512]; - int byteRead; - while ((byteRead = is.read(buffer)) != -1) { - os.write(buffer, 0, byteRead); - } - } catch (IOException e) { - Log.e("Baresip", "Failed to read asset " + asset + ": " + - e.toString()); - } - } - - private void addCallViews(final Call call, int id) { - Log.d("Baresip", "Creating new Incoming textview at " + id); - TextView caller_heading = new TextView(mainActivityContext); - caller_heading.setText("Incoming call from ..."); - caller_heading.setTextColor(Color.BLACK); - caller_heading.setTextSize(20); - caller_heading.setPadding(10, 20, 0, 0); - caller_heading.setId(id); - LayoutParams heading_params = new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT); - if (id == 10) - heading_params.addRule(RelativeLayout.BELOW, callButton.getId()); - else - heading_params.addRule(RelativeLayout.BELOW,id - 10 + 3); - caller_heading.setLayoutParams(heading_params); - layout.addView(caller_heading); - - TextView caller_uri = new TextView(mainActivityContext); - caller_uri.setText(In.get(In.size() - 1).getPeerURI()); - caller_uri.setTextColor(Color.GREEN); - caller_uri.setTextSize(20); - caller_uri.setPadding(10, 10, 0, 10); - Log.d("Baresip", "Creating new caller textview at " + (id + 1)); - caller_uri.setId(id + 1); - LayoutParams caller_uri_params = new LayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.WRAP_CONTENT); - caller_uri_params.addRule(RelativeLayout.BELOW, id); - caller_uri.setLayoutParams(caller_uri_params); - layout.addView(caller_uri); - - Button answer_button = new Button(mainActivityContext); - answer_button.setText(call.getStatus()); - answer_button.setBackgroundResource(android.R.drawable.btn_default); - answer_button.setTextColor(Color.BLACK); - Log.d("Baresip", "Creating new answer button at " + (id + 2)); - answer_button.setId(id + 2); - answer_button.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - String call_ua = call.getUA(); - String call_call = call.getCall(); - switch (((Button)v).getText().toString()) { - case "Answer": - Log.i("Baresip", "UA " + call_ua + " accepting incoming call " + - call_call); - ua_answer(call_ua, call_call); - final int final_in_index = callIndex(In, call_ua, call_call); - if (final_in_index >= 0) { - Log.d("Baresip", "Updating Hangup and Hold"); - In.get(final_in_index).setStatus("Hangup"); - In.get(final_in_index).setHold(false); - runOnUiThread(new Runnable() { - @Override - public void run() { - final int answer_id = (final_in_index + 1) * 10 + 2; - Button answer_button = (Button)layout.findViewById(answer_id); - answer_button.setText("Hangup"); - Button reject_button = (Button)layout.findViewById(answer_id + 1); - reject_button.setText("Hold"); - } - }); - } - break; - case "Hangup": - Log.i("Baresip", "UA " + call_ua + " hanging up call " + - call_call); - ua_hangup(call_ua, call_call,200, "OK"); - break; - default: - Log.e("Baresip", "Invalid answer button text: " + - ((Button)v).getText().toString()); - break; - } - } - }); - LayoutParams answer_button_params = new LayoutParams(200, - LayoutParams.WRAP_CONTENT); - answer_button_params.addRule(RelativeLayout.BELOW, id + 1); - answer_button_params.setMargins(3, 10, 0, 0); - answer_button.setLayoutParams(answer_button_params); - layout.addView(answer_button); - - Button reject_button = new Button(mainActivityContext); - if (call.getStatus().equals("Answer")) { - reject_button.setText("Reject"); - } else { - if (call.getHold()) { - reject_button.setText("Unhold"); - } else { - reject_button.setText("Hold"); - } - } - reject_button.setBackgroundResource(android.R.drawable.btn_default); - reject_button.setTextColor(Color.BLACK); - Log.d("Baresip", "Creating new reject button at " + (id + 3)); - reject_button.setId(id + 3); - reject_button.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - switch (((Button)v).getText().toString()) { - case "Reject": - Log.i("Baresip", "UA " + call.getUA() + - " rejecting incoming call " + call.getCall()); - ua_hangup(call.getUA(), call.getCall(), 486, "Rejected"); - break; - case "Hold": - call_hold(call.getCall()); - ((Button)v).setText("Unhold"); - call.setHold(true); - break; - case "Unhold": - call_unhold(call.getCall()); - ((Button)v).setText("Hold"); - call.setHold(false); - break; - } - } - }); - LayoutParams reject_button_params = new LayoutParams(200, - LayoutParams.WRAP_CONTENT); - reject_button_params.addRule(RelativeLayout.BELOW, id + 1); - reject_button_params.setMargins(225, 10, 0, 0); - reject_button.setLayoutParams(reject_button_params); - layout.addView(reject_button); - } - - private int callIndex(ArrayList calls, String ua, String call) { - for (int i = 0; i < calls.size(); i++) { - if (calls.get(i).getUA().equals(ua) && calls.get(i).getCall().equals(call)) - return i; - } - return -1; - } - - private ArrayList uaCalls(ArrayList calls, String ua) { - ArrayList result = new ArrayList<>(); - for (int i = 0; i < calls.size(); i++) { - if (calls.get(i).getUA().equals(ua)) result.add(calls.get(i)); - } - return result; - } - - private Boolean aorHasHistory(ArrayList history, String aor) { - for (History h : history) { - if (h.getAoR().equals(aor)) return true; - } - return false; - } - - private Boolean callHasHistory(ArrayList history, String ua, String call) { - for (History h : history) { - if (h.getUA().equals(ua) && h.getCall().equals(call)) return true; - } - return false; - } - - private void updateStatus(String event, final String ua, final String call) { - String aor = ua_aor(ua); - int call_index; - - Log.d("Baresip", "Handling event " + event + " for " + ua + "/" + call + "/" + - aor); - - for (int account_index = 0; account_index < Accounts.size(); account_index++) { - if (Accounts.get(account_index).getAoR().equals(aor)) { - Log.d("Baresip", "Found AoR at index " + account_index); - switch (event) { - case "registering": - case "unregistering": - // Log.d("Baresip", "Setting status to yellow"); - break; - case "registered": - Log.d("Baresip", "Setting status to green"); - Accounts.get(account_index).setStatus("OK"); - AoRs.set(account_index, aor); - Images.set(account_index, R.drawable.green); - runOnUiThread(new Runnable() { - @Override - public void run() { - AccountAdapter.notifyDataSetChanged(); - } - }); - break; - case "registering failed": - Log.d("Baresip", "Setting status to red"); - Accounts.get(account_index).setStatus("FAIL"); - AoRs.set(account_index, aor); - Images.set(account_index, R.drawable.red); - runOnUiThread(new Runnable() { - @Override - public void run() { - AccountAdapter.notifyDataSetChanged(); - } - }); - break; - case "call ringing": - break; - case "call established": - Log.d("Baresip", "Out call index is " + (Out.size() - 1)); - int out_index = callIndex(Out, ua, call); - if (out_index >= 0) { - Log.d("Baresip", "Update Hangup and Hold"); - Out.get(out_index).setStatus("Hangup"); - Out.get(out_index).setHold(false); - if (ua.equals(CurrentUA)) { - runOnUiThread(new Runnable() { - @Override - public void run() { - callButton.setText("Hangup"); - holdButton.setText("Hold"); - holdButton.setVisibility(View.VISIBLE); - } - }); - } - History.add(new History(ua, call, aor, call_peeruri(call), - "out", true)); - break; - } else { - History.add(new History(ua, call, aor, call_peeruri(call), - "in", true)); - } - Log.e("Baresip", "Unknown call " + ua + "/" + call + - " established"); - break; - case "call incoming": - final String peer_uri = call_peeruri(call); - Log.d("Baresip", "Incoming call " + ua + "/" + call + "/" + - peer_uri); - final Call new_call = new Call(ua, call, peer_uri, "Answer"); - In.add(new_call); - // History.add(new History(aor, call_peeruri(call), "in")); - Log.d("Baresip", "Current UA is " + CurrentUA); - if (ua.equals(CurrentUA)) { - runOnUiThread(new Runnable() { - @Override - public void run() { - addCallViews(new_call, In.size() * 10); - } - }); - } - break; - case "call closed": - call_index = callIndex(In, ua, call); - Log.d("Baresip", "Incoming call index is " + call_index); - if (call_index != -1) { - Log.d("Baresip", "Removing inbound call " + ua + "/" + - call + "/" + In.get(call_index).getPeerURI()); - final int view_id = (call_index + 1) * 10; - final int remove_count = In.size() - call_index; - final int final_call_index = call_index; - In.remove(call_index); - Log.d("Baresip", "Current UA is " + CurrentUA); - if (ua.equals(CurrentUA)) { - runOnUiThread(new Runnable() { - @Override - public void run() { - if (callButton.getText().equals("Call")) { - holdButton.setText("History"); - holdButton.setVisibility(View.VISIBLE); - } - View caller_heading = layout.findViewById(view_id); - int view_index = layout.indexOfChild(caller_heading); - Log.d("Baresip", "Index of caller heading is " + - view_index); - layout.removeViews(view_index, 4 * remove_count); - for (int i = final_call_index; i < In.size(); i++) { - addCallViews(In.get(i), (i + 1) * 10); - } - } - }); - } - if (!callHasHistory(History, ua, call)) { - History.add(new History(ua, call, aor, call_peeruri(call), - "in", false)); - } - break; - } - call_index = callIndex(Out, ua, call); - Log.d("Baresip", "Outgoing call index is " + call_index); - if (call_index != -1) { - Log.d("Baresip", "Removing outgoing call " + ua + "/" + - call + "/" + Out.get(call_index).getPeerURI()); - Out.remove(call_index); - if (ua.equals(CurrentUA)) { - runOnUiThread(new Runnable() { - @Override - public void run() { - callButton.setText("Call"); - callButton.setEnabled(true); - callee.setText(""); - callee.setHint("Callee"); - holdButton.setText("History"); - holdButton.setVisibility(View.VISIBLE); - } - }); - } - if (!callHasHistory(History, ua, call)) { - History.add(new History(ua, call, aor, call_peeruri(call), - "out", false)); - } - break; - } - Log.e("Baresip", "Unknown call " + ua + "/" + call + - " closed"); - break; - default: - Log.d("Baresip", "Unknown event '" + event + "'"); - break; - } - } - } - } - - public native void baresipStart(String path); - public native void baresipStop(); - public static native String ua_current(); - public static native String ua_aor(String ua); - public native Boolean ua_isregistered(long ua_ptr); - public native String ua_call(String ua); - public native String call_peeruri(String call); - public static native void ua_current_set(String s); - public native String ua_connect(String s); - public native void ua_answer(String ua, String call); - public native Integer call_hold(String call); - public native Integer call_unhold(String call); - public native void ua_hangup(String ua, String call, int code, String reason); - static public native void contacts_remove(); - static public native void contact_add(String contact); - static public native int reload_config(); - - static { - System.loadLibrary("baresip"); - } - -} diff --git a/app/src/main/java/com/tutpro/baresip/Utils.java b/app/src/main/java/com/tutpro/baresip/Utils.java deleted file mode 100644 index a4fc6cec..00000000 --- a/app/src/main/java/com/tutpro/baresip/Utils.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.tutpro.baresip; - -import android.content.Context; -import android.content.DialogInterface; -import android.support.v7.app.AlertDialog; -import android.util.Log; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.OutputStreamWriter; - -public class Utils { - - static public String getFileContents(File file) { - if (!file.exists()) { - Log.e("Baresip", "Failed to find file: " + file.getPath()); - return ""; - } else { - Log.e("Baresip", "Found file: " + file.getPath()); - int length = (int) file.length(); - byte[] bytes = new byte[length]; - try { - FileInputStream in = new FileInputStream(file); - try { - in.read(bytes); - } finally { - in.close(); - } - return new String(bytes); - } catch (java.io.IOException e) { - Log.e("Baresip", "Failed to read file: " + file.getPath() + ": " + - e.toString()); - return ""; - } - } - } - - static public void putFileContents(File file, String contents) { - try { - FileOutputStream fOut = - new FileOutputStream(file.getAbsoluteFile(), false); - OutputStreamWriter fWriter = new OutputStreamWriter(fOut); - try { - fWriter.write(contents); - fWriter.close(); - fOut.close(); - } catch (java.io.IOException e) { - Log.e("Baresip", "Failed to put contents to file: " + - e.toString()); - } - } catch (java.io.FileNotFoundException e) { - Log.e("Baresip", "Failed to find contents file: " + - e.toString()); - } - } - - static public void alertView( Context context, String message ) { - AlertDialog alertDialog = new AlertDialog.Builder(context).create(); - alertDialog.setTitle("Alert"); - alertDialog.setMessage(message); - alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }); - alertDialog.show(); - } - -} diff --git a/app/src/main/kotlin/com/tutpro/baresip/AboutActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/AboutActivity.kt new file mode 100644 index 00000000..3d4a088e --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/AboutActivity.kt @@ -0,0 +1,31 @@ +package com.tutpro.baresip + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.view.MenuItem +import android.widget.TextView + +class AboutActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_about) + val aboutView = findViewById(R.id.aboutText) as TextView + aboutView.isEnabled = false + aboutView.text = getString(R.string.aboutText) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + android.R.id.home -> { + val i = Intent() + setResult(Activity.RESULT_CANCELED, i) + finish() + } + } + return true + } +} + diff --git a/app/src/main/kotlin/com/tutpro/baresip/Account.kt b/app/src/main/kotlin/com/tutpro/baresip/Account.kt new file mode 100644 index 00000000..d47a79ec --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/Account.kt @@ -0,0 +1,10 @@ +package com.tutpro.baresip + +class Account(val ua: String, val aoR: String, var status: String) { + private var statusImage: Int = 0 + + init { + this.statusImage = 0 + } + +} diff --git a/app/src/main/kotlin/com/tutpro/baresip/AccountSpinnerAdapter.kt b/app/src/main/kotlin/com/tutpro/baresip/AccountSpinnerAdapter.kt new file mode 100644 index 00000000..aee2ddd2 --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/AccountSpinnerAdapter.kt @@ -0,0 +1,36 @@ +package com.tutpro.baresip + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.ImageView +import android.widget.TextView + +import java.util.ArrayList + +class AccountSpinnerAdapter(private val cxt: Context, private val aors: ArrayList, + private val images: ArrayList) : + ArrayAdapter(cxt, android.R.layout.simple_spinner_item, images) { + + override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { + return getImageForPosition(position, parent) + } + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + return getImageForPosition(position, parent) + } + + private fun getImageForPosition(position: Int, parent: ViewGroup): View { + val inflater = cxt.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + val row = inflater.inflate(R.layout.account_spinner, parent, false) + val textView = row.findViewById(R.id.spinnerText) as TextView + textView.text = aors[position] + textView.textSize = 17f + val imageView = row.findViewById(R.id.spinnerImage) as ImageView + imageView.setImageResource(images[position]) + return row + } + +} diff --git a/app/src/main/kotlin/com/tutpro/baresip/Call.kt b/app/src/main/kotlin/com/tutpro/baresip/Call.kt new file mode 100644 index 00000000..17f0e5ca --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/Call.kt @@ -0,0 +1,5 @@ +package com.tutpro.baresip + +class Call(val ua: String, val call: String, val peerURI: String, var status: String) { + var hold: Boolean = false +} diff --git a/app/src/main/kotlin/com/tutpro/baresip/Contact.kt b/app/src/main/kotlin/com/tutpro/baresip/Contact.kt new file mode 100644 index 00000000..14202668 --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/Contact.kt @@ -0,0 +1,3 @@ +package com.tutpro.baresip + +class Contact(val name: String, val uri: String) diff --git a/app/src/main/kotlin/com/tutpro/baresip/EditAccountsActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/EditAccountsActivity.kt new file mode 100644 index 00000000..7fb58473 --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/EditAccountsActivity.kt @@ -0,0 +1,103 @@ +package com.tutpro.baresip + +import android.content.Intent +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.util.Log +import android.util.TypedValue +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.widget.EditText +import android.widget.TextView +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.io.OutputStreamWriter + +class EditAccountsActivity : AppCompatActivity() { + + private var editText: EditText? = null + private var path: String? = null + private var file: File? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_edit_accounts) + + editText = findViewById(R.id.editAccounts) as EditText + path = applicationContext.filesDir.absolutePath + "/accounts" + file = File(path!!) + var content: String + + if (!file!!.exists()) { + Log.e("Baresip", "Failed to find accounts file") + content = "No accounts" + } else { + Log.e("Baresip", "Found accounts file") + val length = file!!.length().toInt() + val bytes = ByteArray(length) + try { + val `in` = FileInputStream(file!!) + try { + `in`.read(bytes) + } finally { + `in`.close() + } + content = String(bytes) + } catch (e: java.io.IOException) { + Log.e("Baresip", "Failed to read accounts file: " + e.toString()) + content = "Failed to read account file" + } + + } + + Log.d("Baresip", "Content length is: " + content.length) + + editText!!.setTextSize(TypedValue.COMPLEX_UNIT_PX, + resources.getDimension(R.dimen.textsize)) + editText!!.setText(content, TextView.BufferType.EDITABLE) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + super.onCreateOptionsMenu(menu) + val inflater = menuInflater + inflater.inflate(R.menu.edit_menu, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val i = Intent() + when (item.itemId) { + R.id.save -> { + try { + val fOut = FileOutputStream(file!!.absoluteFile, false) + val fWriter = OutputStreamWriter(fOut) + val res = editText!!.text.toString() + try { + fWriter.write(res) + fWriter.close() + fOut.close() + } catch (e: java.io.IOException) { + Log.e("Baresip", "Failed to write accounts file: " + e.toString()) + } + + } catch (e: java.io.FileNotFoundException) { + Log.e("Baresip", "Failed to find accounts file: " + e.toString()) + } + + Log.d("Baresip", "Updated accounts file") + setResult(RESULT_OK, i) + finish() + return true + } + R.id.cancel, android.R.id.home -> { + setResult(RESULT_CANCELED, i) + finish() + return true + } + else -> return super.onOptionsItemSelected(item) + } + } +} + diff --git a/app/src/main/kotlin/com/tutpro/baresip/EditConfigActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/EditConfigActivity.kt new file mode 100644 index 00000000..42587a50 --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/EditConfigActivity.kt @@ -0,0 +1,103 @@ +package com.tutpro.baresip + +import android.content.Intent +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.util.Log +import android.util.TypedValue +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.widget.EditText +import android.widget.TextView +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.io.OutputStreamWriter + +class EditConfigActivity : AppCompatActivity() { + + private var editText: EditText? = null + private var path: String? = null + private var file: File? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_edit_config) + + editText = findViewById(R.id.editConfig) as EditText + path = applicationContext.filesDir.absolutePath + "/config" + // path = "/sdcard/baresip/config"; + file = File(path!!) + var content: String + + if (!file!!.exists()) { + Log.e("Baresip", "Failed to find config file") + content = "No config" + } else { + Log.e("Baresip", "Found config file") + val length = file!!.length().toInt() + val bytes = ByteArray(length) + try { + val `in` = FileInputStream(file!!) + try { + `in`.read(bytes) + } finally { + `in`.close() + } + content = String(bytes) + } catch (e: java.io.IOException) { + Log.e("Baresip", "Failed to read config file: " + e.toString()) + content = "Failed to read account file" + } + + } + + Log.d("Baresip", "Content length is: " + content.length) + + editText!!.setTextSize(TypedValue.COMPLEX_UNIT_PX, + resources.getDimension(R.dimen.textsize)) + editText!!.setText(content, TextView.BufferType.EDITABLE) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + super.onCreateOptionsMenu(menu) + val inflater = menuInflater + inflater.inflate(R.menu.edit_menu, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val i = Intent(this, MainActivity::class.java) + when (item.itemId) { + R.id.save -> { + try { + val fOut = FileOutputStream(file!!.absoluteFile, false) + val fWriter = OutputStreamWriter(fOut) + val res = editText!!.text.toString() + try { + fWriter.write(res) + fWriter.close() + fOut.close() + } catch (e: java.io.IOException) { + Log.e("Baresip", "Failed to write config file: " + e.toString()) + } + + } catch (e: java.io.FileNotFoundException) { + Log.e("Baresip", "Failed to find config file: " + e.toString()) + } + + Log.d("Baresip", "Updated config file") + setResult(RESULT_OK, i) + finish() + return true + } + R.id.cancel, android.R.id.home -> { + setResult(RESULT_CANCELED, i) + finish() + return true + } + else -> return super.onOptionsItemSelected(item) + } + } +} diff --git a/app/src/main/kotlin/com/tutpro/baresip/EditContactsActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/EditContactsActivity.kt new file mode 100644 index 00000000..453d602d --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/EditContactsActivity.kt @@ -0,0 +1,113 @@ +package com.tutpro.baresip + +import android.content.Intent +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.util.Log +import android.util.TypedValue +import android.view.Menu +import android.view.MenuItem +import android.widget.ArrayAdapter +import android.widget.EditText +import android.widget.TextView + +import java.io.File +import java.util.ArrayList + +class EditContactsActivity : AppCompatActivity() { + + private var editText: EditText? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_edit_contacts) + editText = findViewById(R.id.editText) as EditText + val path = applicationContext.filesDir.absolutePath + "/contacts" + val file = File(path) + val content = Utils.getFileContents(file) + Log.d("Baresip", "Contacts length is: " + content.length) + editText!!.setTextSize(TypedValue.COMPLEX_UNIT_PX, + resources.getDimension(R.dimen.textsize)) + editText!!.setText(content, TextView.BufferType.EDITABLE) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + super.onCreateOptionsMenu(menu) + val inflater = menuInflater + inflater.inflate(R.menu.edit_menu, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val i = Intent(this, MainActivity::class.java) + when (item.itemId) { + R.id.save -> { + val path = applicationContext.filesDir.absolutePath + "/contacts" + val file = File(path) + Utils.putFileContents(file, editText!!.text.toString()) + Log.d("Baresip", "Updated contacts file") + MainActivity.contacts_remove() + updateContactsAndNames(path) + // MainActivity.CalleeAdapter.notifyDataSetChanged(); does not work + setResult(RESULT_OK, i) + finish() + return true + } + R.id.cancel, android.R.id.home -> { + i.putExtra("action", "cancel") + setResult(RESULT_OK, i) + finish() + return true + } + else -> return super.onOptionsItemSelected(item) + } + } + + companion object { + var Contacts: ArrayList = java.util.ArrayList() + var Names: ArrayList = java.util.ArrayList() + + fun updateContactsAndNames(path: String) { + val file = File(path) + val lines = Utils.getFileContents(file).split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + var name: String + var uri: String + Contacts.clear() + Names.clear() + for (line in lines) { + line.trim { it <= ' ' } + if (line.startsWith("#") || line.length == 0) continue + val parts = line.split("\"".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + if (parts.size != 3) { + Log.e("Baresip", "Invalid contacts line: $line") + continue + } + name = parts[1] + if (name.length < 2) { + Log.e("Baresip", "Too short contact display name: $name") + continue + } + uri = parts[2].trim { it <= ' ' } + if (!uri.startsWith("<") || !uri.contains(">")) { + Log.e("Baresip", "Invalid contact uri: $uri") + continue + } + MainActivity.contact_add("\"$name\" $uri") + if (uri.indexOf(";access") > 0) continue + uri = uri.substring(1, uri.indexOf(">")) + Log.d("Baresip", "Adding contact name/uri: $name/$uri") + Contacts.add(Contact(name, uri)) + Names.add(name) + } + } + + fun findContactURI(name: String): String { + for (c in Contacts) { + if (c.name == name) return c.uri + } + return name + } + } + +} + diff --git a/app/src/main/kotlin/com/tutpro/baresip/History.kt b/app/src/main/kotlin/com/tutpro/baresip/History.kt new file mode 100644 index 00000000..f1fc8c49 --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/History.kt @@ -0,0 +1,11 @@ +package com.tutpro.baresip + +import java.io.Serializable +import java.util.GregorianCalendar + +class History(val ua: String, val call: String, val aor: String, val peerURI: String, + val direction: String, val connected: Boolean) : Serializable { + + val time: GregorianCalendar = GregorianCalendar() + +} diff --git a/app/src/main/kotlin/com/tutpro/baresip/HistoryActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/HistoryActivity.kt new file mode 100644 index 00000000..29d2f6dd --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/HistoryActivity.kt @@ -0,0 +1,129 @@ +package com.tutpro.baresip + +import android.app.Activity +import android.content.DialogInterface +import android.content.Intent +import android.os.Bundle +import android.support.v7.app.AlertDialog +import android.support.v7.app.AppCompatActivity +import android.util.Log +import android.view.MenuItem +import android.widget.AdapterView +import android.widget.ListView + +import java.text.SimpleDateFormat +import java.util.ArrayList +import java.util.Calendar +import java.util.GregorianCalendar + +class HistoryActivity : AppCompatActivity() { + + internal var uaHistory = ArrayList() + internal var posAtHistory = ArrayList() + internal var aor: String = "" + + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_history) + + val listview = findViewById(R.id.history) as ListView + + val b = intent.extras + aor = b.getString("aor") + generate_ua_history(aor) + + val adapter = HistoryListAdapter(this, uaHistory) + listview.adapter = adapter + listview.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> + val row = uaHistory[position] + val i = Intent() + i.putExtra("peer_uri", row.peerURI) + setResult(Activity.RESULT_OK, i) + finish() + } + + listview.onItemLongClickListener = AdapterView.OnItemLongClickListener { _, _, pos, _ -> + val dialogClickListener = DialogInterface.OnClickListener { _, which -> + when (which) { + DialogInterface.BUTTON_POSITIVE -> { + History.removeAt(posAtHistory[pos]) + generate_ua_history(aor) + if (uaHistory.size == 0) { + val i = Intent() + setResult(Activity.RESULT_CANCELED, i) + finish() + } + adapter.notifyDataSetChanged() + } + DialogInterface.BUTTON_NEGATIVE -> { + } + } + } + val builder = AlertDialog.Builder(this@HistoryActivity, + R.style.Theme_AppCompat) + builder.setMessage("Do you want to delete " + + History[pos].peerURI + "?") + .setPositiveButton("Yes", dialogClickListener) + .setNegativeButton("No", dialogClickListener).show() + true + } + + listview.isLongClickable = true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + android.R.id.home -> { + Log.d("Baresip", "Back array was pressed") + val i = Intent() + setResult(Activity.RESULT_CANCELED, i) + finish() + } + } + return true + } + + private fun generate_ua_history(aor: String) { + uaHistory.clear() + posAtHistory.clear() + for (i in History.indices.reversed()) { + val h = History[i] + if (h.aor == aor) { + val time: String + if (isToday(h.time)) { + val fmt = SimpleDateFormat("HH:mm") + time = fmt.format(h.time.time) + } else { + val fmt = SimpleDateFormat("MMM dd") + time = fmt.format(h.time.time) + } + if (h.direction == "in") { + if (h.connected) { + uaHistory.add(HistoryRow(h.peerURI, R.drawable.arrow_down_green, time)) + } else { + uaHistory.add(HistoryRow(h.peerURI, R.drawable.arrow_down_red, time)) + } + } else { + if (h.connected) { + uaHistory.add(HistoryRow(h.peerURI, R.drawable.arrow_up_green, time)) + } else { + uaHistory.add(HistoryRow(h.peerURI, R.drawable.arrow_up_red, time)) + } + } + posAtHistory.add(i) + } + } + } + + private fun isToday(time: GregorianCalendar): Boolean { + val now = GregorianCalendar() + return now.get(Calendar.YEAR) == time.get(Calendar.YEAR) && + now.get(Calendar.MONTH) == time.get(Calendar.MONTH) && + now.get(Calendar.DAY_OF_MONTH) == time.get(Calendar.DAY_OF_MONTH) + } + + companion object { + var History: ArrayList = ArrayList() + } + +} diff --git a/app/src/main/kotlin/com/tutpro/baresip/HistoryListAdapter.kt b/app/src/main/kotlin/com/tutpro/baresip/HistoryListAdapter.kt new file mode 100644 index 00000000..ddfd3f7a --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/HistoryListAdapter.kt @@ -0,0 +1,29 @@ +package com.tutpro.baresip + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ArrayAdapter +import android.widget.ImageView +import android.widget.TextView + +import java.util.ArrayList + +class HistoryListAdapter(private val cxt: Context, private val rows: ArrayList) : + ArrayAdapter(cxt, R.layout.history_row, rows) { + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val row = rows[position] + val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + val rowView = inflater.inflate(R.layout.history_row, parent, false) + val directionView = rowView.findViewById(R.id.direction) as ImageView + directionView.setImageResource(row.direction) + val peerURIView = rowView.findViewById(R.id.peer_uri) as TextView + peerURIView.text = row.peerURI + val timeView = rowView.findViewById(R.id.time) as TextView + timeView.text = row.time + return rowView + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/tutpro/baresip/HistoryRow.kt b/app/src/main/kotlin/com/tutpro/baresip/HistoryRow.kt new file mode 100644 index 00000000..346458c4 --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/HistoryRow.kt @@ -0,0 +1,3 @@ +package com.tutpro.baresip + +class HistoryRow(val peerURI: String, val direction: Int, val time: String) diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt new file mode 100644 index 00000000..752b93b9 --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt @@ -0,0 +1,668 @@ +package com.tutpro.baresip + +import android.Manifest +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.content.res.Configuration +import android.graphics.Color +import android.support.v4.app.ActivityCompat +import android.support.v4.content.ContextCompat +import android.support.v7.app.AlertDialog +import android.support.v7.app.AppCompatActivity +import android.os.Bundle +import android.widget.* +import android.widget.RelativeLayout.LayoutParams +import android.widget.AutoCompleteTextView +import android.view.* +import android.util.Log + +import java.util.* +import java.io.* +import android.widget.RelativeLayout + +class MainActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + mainActivityContext = applicationContext + layout = findViewById(R.id.mainActivityLayout) as RelativeLayout + callee = findViewById(R.id.callee) as AutoCompleteTextView + callButton = findViewById(R.id.callButton) as Button + holdButton = findViewById(R.id.holdButton) as Button + + AoRSpinner = findViewById(R.id.AoRList) as Spinner + + Log.i("Baresip", "Setting AccountAdapter") + AccountAdapter = AccountSpinnerAdapter(applicationContext, AoRs, Images) + AoRSpinner.adapter = AccountAdapter + AoRSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { + val aor = AoRs[position] + val callee = findViewById(R.id.callee) as AutoCompleteTextView + val callButton = findViewById(R.id.callButton) as Button + val holdButton = findViewById(R.id.holdButton) as Button + Log.i("Baresip", "Setting $aor current") + val out = uaCalls(Out, aor_ua(aor)) + if (out.size == 0) { + callee.text.clear() + callee.hint = "Callee" + callButton.text = "Call" + if (aorHasHistory(aor)) { + holdButton.text = "History" + holdButton.visibility = View.VISIBLE + } else { + holdButton.visibility = View.INVISIBLE + } + } else { + callee.setText(out[0].peerURI) + callButton.text = out[0].status + if (out[0].status == "Hangup") { + if (out[0].hold) { + holdButton.text = "Unhold" + } else { + holdButton.text = "Hold" + } + holdButton.visibility = View.VISIBLE + } else { + holdButton.visibility = View.INVISIBLE + } + } + val `in` = uaCalls(In, aor_ua(aor)) + val view_count = layout.childCount + Log.d("Baresip", "View count is $view_count") + if (view_count > 5) { + layout.removeViews(5, view_count - 5) + } + for (c in `in`) { + for (call_index in In.indices) { + addCallViews(c, (call_index + 1) * 10) + } + } + } + + override fun onNothingSelected(parent: AdapterView<*>) { + Log.i("Baresip", "Nothing selected") + } + } + + val assets = arrayOf("accounts", "contacts", "config", "busy.wav", "callwaiting.wav", "error.wav", "message.wav", "notfound.wav", "ring.wav", "ringback.wav") + val path = applicationContext.filesDir.path + Log.d("Baresip", "path is: $path") + var file = File(path) + if (!file.exists()) { + Log.d("Baresip", "Creating baresip directory") + try { + File(path).mkdirs() + } catch (e: Error) { + Log.e("Baresip", "Failed to create directory: " + e.toString()) + } + + } + for (a in assets) { + file = File("$path/$a") + if (!file.exists()) { + Log.d("Baresip", "Copying asset $a") + copyAssetToFile(a, "$path/$a") + } else { + Log.d("Baresip", "Asset $a already copied") + } + } + + file = File(path, "history") + try { + val fis = FileInputStream(file) + val ois = ObjectInputStream(fis) + @SuppressWarnings("unchecked") + HistoryActivity.History = ois.readObject() as ArrayList + Log.d("Baresip", "Restored History of " + HistoryActivity.History.size + " entries") + ois.close() + fis.close() + } catch (e: Exception) { + Log.w("Baresip", "InputStream exception: - " + e.toString()) + } + + if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + Log.d("Baresip", "Baresip does not have RECORD_AUDIO permission") + ActivityCompat.requestPermissions(this, + arrayOf(Manifest.permission.RECORD_AUDIO), RECORD_AUDIO_PERMISSION) + } + + if (!running) { + Log.i("Baresip", "Starting Baresip with path $path") + Thread(Runnable { baresipStart(path) }).start() + running = true + } + + EditContactsActivity.updateContactsAndNames(applicationContext.filesDir.absolutePath + "/contacts") + CalleeAdapter = ArrayAdapter(this, android.R.layout.select_dialog_item, EditContactsActivity.Names) + callee.threshold = 2 + callee.setAdapter>(CalleeAdapter) + + callButton.text = "Call" + callButton.setOnClickListener { + Log.d("Baresip", "AoR at position is " + AoRs[AoRSpinner.selectedItemPosition]) + val aor = AoRs[AoRSpinner.selectedItemPosition] + if (callButton.text.toString() == "Call") { + call(aor, (findViewById(R.id.callee) as EditText).text.toString()) + } else { + Log.i("Baresip", "Canceling UA " + aor_ua(aor) + " call " + + Out[0].call + " to " + + (findViewById(R.id.callee) as EditText).text) + ua_hangup(aor_ua(aor), Out[0].call, 486, "Rejected") + } + } + + val holdButton = findViewById(R.id.holdButton) as Button + holdButton.setOnClickListener { + when (holdButton.text.toString()) { + "Hold" -> { + Log.i("Baresip", "Holding up " + (findViewById(R.id.callee) as EditText).text) + call_hold(Out[0].call) + Out[0].hold = true + holdButton.text = "Unhold" + } + "Unhold" -> { + Log.i("Baresip", "Unholding " + (findViewById(R.id.callee) as EditText).text) + call_unhold(Out[0].call) + Out[0].hold = false + holdButton.text = "Hold" + } + "History" -> { + val i = Intent(this@MainActivity, HistoryActivity::class.java) + val b = Bundle() + b.putString("aor", AoRs[AoRSpinner.selectedItemPosition]) + i.putExtras(b) + startActivityForResult(i, HISTORY_CODE) + } + } + } + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { + Log.d("Baresip", "Screen orientation change to landscape") + } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { + Log.d("Baresip", "Screen orientation change to portrait") + } + } + + override fun onRequestPermissionsResult(requestCode: Int, + permissions: Array, grantResults: IntArray) { + when (requestCode) { + RECORD_AUDIO_PERMISSION -> { + if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + Log.d("Baresip", "RECORD_AUDIO permission granted") + } else { + Log.d("Baresip", "RECORD_AUDIO permission NOT granted") + } + return + } + else -> Log.e("Baresip", "Unknown permissions request code: $requestCode") + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + super.onCreateOptionsMenu(menu) + val inflater = menuInflater + inflater.inflate(R.menu.main_menu, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val i: Intent + when (item.itemId) { + R.id.accounts -> { + i = Intent(this, EditAccountsActivity::class.java) + startActivityForResult(i, EDIT_ACCOUNTS_CODE) + return true + } + R.id.contacts -> { + i = Intent(this, EditContactsActivity::class.java) + startActivityForResult(i, EDIT_CONTACTS_CODE) + return true + } + R.id.config -> { + i = Intent(this, EditConfigActivity::class.java) + startActivityForResult(i, EDIT_CONFIG_CODE) + return true + } + R.id.about -> { + i = Intent(this, AboutActivity::class.java) + startActivityForResult(i, ABOUT_CODE) + return true + } + R.id.quit -> { + if (running) { + Log.d("Baresip", "Stopping") + val path = applicationContext.filesDir.path + Log.d("Baresip", "Saving history to $path") + val file = File(path, "history") + try { + val fos = FileOutputStream(file) + val oos = ObjectOutputStream(fos) + oos.writeObject(HistoryActivity.History) + oos.close() + fos.close() + } catch (e: IOException) { + Log.w("Baresip", "OutputStream exception: " + e.toString()) + e.printStackTrace() + } + + baresipStop() + running = false + } + finish() + System.exit(0) + return true + } + else -> return super.onOptionsItemSelected(item) + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode == EDIT_ACCOUNTS_CODE) { + if (resultCode == RESULT_OK) { + val alertDialog = AlertDialog.Builder(this).create() + alertDialog.setTitle("Alert") + alertDialog.setMessage("You need to restart baresip in order to activate saved accounts!") + alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK" + ) { dialog, _ -> dialog.dismiss() } + alertDialog.show() + } + if (resultCode == RESULT_CANCELED) { + Log.d("Baresip", "Edit accounts canceled") + } + } + if (requestCode == EDIT_CONFIG_CODE) { + if (resultCode == RESULT_OK) { + Utils.alertView(this, + "You need to restart baresip in order to activate saved config!") + reload_config() + } + if (resultCode == RESULT_CANCELED) { + Log.d("Baresip", "Edit config canceled") + } + } + if (requestCode == HISTORY_CODE) { + if (resultCode == RESULT_OK) { + if (data != null) { + (findViewById(R.id.callee) as EditText).setText(data.getStringExtra("peer_uri")) + } + } + if (resultCode == RESULT_CANCELED) { + Log.d("Baresip", "History canceled") + if (!aorHasHistory(AoRs[AoRSpinner.selectedItemPosition])) { + (findViewById(R.id.holdButton) as Button).visibility = View.INVISIBLE + } + } + } + if (requestCode == EDIT_CONTACTS_CODE || requestCode == ABOUT_CODE) { + Log.d("Baresip", "Back arrow or Cancel pressed at request: $requestCode") + } + } + + fun addAccount(ua: String) { + val aor = ua_aor(ua) + Log.d("Baresip", "Adding account $ua with AoR $aor") + Accounts.add(Account(ua, aor, "")) + AoRs.add(aor) + Images.add(R.drawable.yellow) + runOnUiThread { AccountAdapter.notifyDataSetChanged() } + } + + private fun call(aor: String, callee: String) { + if (callee.length == 0) return + var uri = EditContactsActivity.findContactURI(callee) + if (!uri.startsWith("sip:")) uri = "sip:$uri" + if (!uri.contains("@")) { + val host = aor.substring(aor.indexOf("@") + 1) + uri = "$uri@$host" + } + (findViewById(R.id.callee) as EditText).setText(uri) + val ua = aor_ua(aor) + Log.i("Baresip", "Calling $ua / $uri") + val call = ua_connect(ua, uri) + if (call != "") { + Log.i("Baresip", "Adding outgoing call $ua / $call / $uri") + Out.add(Call(ua, call, uri, "Cancel")) + (findViewById(R.id.callButton) as Button).text = "Cancel" + (findViewById(R.id.holdButton) as Button).visibility = View.INVISIBLE + } + } + + private fun copyAssetToFile(asset: String, path: String) { + try { + val assetManager = assets + val `is` = assetManager.open(asset) + val os = FileOutputStream(path) + val buffer = ByteArray(512) + var byteRead: Int = `is`.read(buffer) + while (byteRead != -1) { + os.write(buffer, 0, byteRead) + byteRead = `is`.read(buffer) + } + } catch (e: IOException) { + Log.e("Baresip", "Failed to read asset " + asset + ": " + + e.toString()) + } + + } + + private fun addCallViews(call: Call, id: Int) { + Log.d("Baresip", "Creating new Incoming textview at $id") + + val caller_heading = TextView(mainActivityContext) + + caller_heading.text = "Incoming call from ..." + caller_heading.setTextColor(Color.BLACK) + caller_heading.textSize = 20f + caller_heading.setPadding(10, 20, 0, 0) + caller_heading.id = id + val heading_params = LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT) + if (id == 10) + heading_params.addRule(RelativeLayout.BELOW, callButton.id) + else + heading_params.addRule(RelativeLayout.BELOW, id - 10 + 3) + caller_heading.layoutParams = heading_params + layout.addView(caller_heading) + + val caller_uri = TextView(mainActivityContext) + caller_uri.text = In[In.size - 1].peerURI + caller_uri.setTextColor(Color.GREEN) + caller_uri.textSize = 20f + caller_uri.setPadding(10, 10, 0, 10) + Log.d("Baresip", "Creating new caller textview at " + (id + 1)) + caller_uri.id = id + 1 + val caller_uri_params = LayoutParams(LayoutParams.WRAP_CONTENT, + LayoutParams.WRAP_CONTENT) + caller_uri_params.addRule(RelativeLayout.BELOW, id) + caller_uri.layoutParams = caller_uri_params + layout.addView(caller_uri) + + val answer_button = Button(mainActivityContext) + answer_button.text = call.status + answer_button.setBackgroundResource(android.R.drawable.btn_default) + answer_button.setTextColor(Color.BLACK) + Log.d("Baresip", "Creating new answer button at " + (id + 2)) + answer_button.id = id + 2 + answer_button.setOnClickListener { v -> + val call_ua = call.ua + val call_call = call.call + when ((v as Button).text.toString()) { + "Answer" -> { + Log.i("Baresip", "UA " + call_ua + " accepting incoming call " + + call_call) + ua_answer(call_ua, call_call) + val final_in_index = callIndex(In, call_ua, call_call) + if (final_in_index >= 0) { + Log.d("Baresip", "Updating Hangup and Hold") + In[final_in_index].status = "Hangup" + In[final_in_index].hold = false + runOnUiThread { + val answer_id = (final_in_index + 1) * 10 + 2 + val answer_but = layout.findViewById(answer_id) as Button + answer_but.text = "Hangup" + val reject_button = layout.findViewById(answer_id + 1) as Button + reject_button.text = "Hold" + } + } + } + "Hangup" -> { + Log.i("Baresip", "UA " + call_ua + " hanging up call " + + call_call) + ua_hangup(call_ua, call_call, 200, "OK") + } + else -> Log.e("Baresip", "Invalid answer button text: " + v.text.toString()) + } + } + val answer_button_params = LayoutParams(200, + LayoutParams.WRAP_CONTENT) + answer_button_params.addRule(RelativeLayout.BELOW, id + 1) + answer_button_params.setMargins(3, 10, 0, 0) + answer_button.layoutParams = answer_button_params + layout.addView(answer_button) + + val reject_button = Button(mainActivityContext) + if (call.status == "Answer") { + reject_button.text = "Reject" + } else { + if (call.hold) { + reject_button.text = "Unhold" + } else { + reject_button.text = "Hold" + } + } + reject_button.setBackgroundResource(android.R.drawable.btn_default) + reject_button.setTextColor(Color.BLACK) + Log.d("Baresip", "Creating new reject button at " + (id + 3)) + reject_button.id = id + 3 + reject_button.setOnClickListener { v -> + when ((v as Button).text.toString()) { + "Reject" -> { + Log.i("Baresip", "UA " + call.ua + + " rejecting incoming call " + call.call) + ua_hangup(call.ua, call.call, 486, "Rejected") + } + "Hold" -> { + call_hold(call.call) + v.text = "Unhold" + call.hold = true + } + "Unhold" -> { + call_unhold(call.call) + v.text = "Hold" + call.hold = false + } + } + } + val reject_button_params = LayoutParams(200, + LayoutParams.WRAP_CONTENT) + reject_button_params.addRule(RelativeLayout.BELOW, id + 1) + reject_button_params.setMargins(225, 10, 0, 0) + reject_button.layoutParams = reject_button_params + layout.addView(reject_button) + } + + private fun callIndex(calls: ArrayList, ua: String, call: String): Int { + for (i in calls.indices) { + if (calls[i].ua.equals(ua) && calls[i].call == call) + return i + } + return -1 + } + + private fun uaCalls(calls: ArrayList, ua: String): ArrayList { + val result = ArrayList() + for (i in calls.indices) { + if (calls[i].ua == ua) result.add(calls[i]) + } + return result + } + + private fun aorHasHistory(aor: String): Boolean { + for (h in HistoryActivity.History) { + if (h.aor == aor) return true + } + return false + } + + private fun callHasHistory(ua: String, call: String): Boolean { + for (h in HistoryActivity.History) { + if (h.ua == ua && h.call == call) return true + } + return false + } + + private fun updateStatus(event: String, ua: String, call: String) { + val aor = ua_aor(ua) + var call_index: Int + + Log.d("Baresip", "Handling event " + event + " for " + ua + "/" + call + "/" + + aor) + + for (account_index in Accounts.indices) { + if (Accounts[account_index].aoR == aor) { + Log.d("Baresip", "Found AoR at index $account_index") + when (event) { + "registering", "unregistering" -> { + } + "registered" -> { + Log.d("Baresip", "Setting status to green") + Accounts[account_index].status = "OK" + AoRs[account_index] = aor + Images[account_index] = R.drawable.green + runOnUiThread { AccountAdapter.notifyDataSetChanged() } + } + "registering failed" -> { + Log.d("Baresip", "Setting status to red") + Accounts[account_index].status = "FAIL" + AoRs[account_index] = aor + Images[account_index] = R.drawable.red + runOnUiThread { AccountAdapter.notifyDataSetChanged() } + } + "call ringing" -> { + } + "call established" -> { + val out_index = callIndex(Out, ua, call) + if (out_index >= 0) { + Log.d("Baresip", "Outbound call " + call + " established") + Out[out_index].status = "Hangup" + Out[out_index].hold = false + runOnUiThread { + if (ua == aor_ua(AoRs[AoRSpinner.selectedItemPosition])) { + callButton.text = "Hangup" + holdButton.text = "Hold" + holdButton.visibility = View.VISIBLE + } + } + HistoryActivity.History.add(History(ua, call, aor, call_peeruri(call), + "out", true)) + } else { + Log.d("Baresip", "Inbound call " + call + " established") + HistoryActivity.History.add(History(ua, call, aor, call_peeruri(call), + "in", true)) + } + } + "call incoming" -> { + val peer_uri = call_peeruri(call) + Log.d("Baresip", "Incoming call " + ua + "/" + call + "/" + + peer_uri) + val new_call = Call(ua, call, peer_uri, "Answer") + In.add(new_call) + this@MainActivity.runOnUiThread { + if (ua == aor_ua(AoRs[AoRSpinner.selectedItemPosition])) { + addCallViews(new_call, In.size * 10) + } + } + } + "call closed" -> { + call_index = callIndex(In, ua, call) + if (call_index != -1) { + Log.d("Baresip", "Removing inbound call " + ua + "/" + + call + "/" + In[call_index].peerURI) + val view_id = (call_index + 1) * 10 + val remove_count = In.size - call_index + In.removeAt(call_index) + this@MainActivity.runOnUiThread { + if (ua == aor_ua(AoRs[AoRSpinner.selectedItemPosition])) { + if (callButton.text == "Call") { + holdButton.text = "History" + holdButton.visibility = View.VISIBLE + } + val caller_heading = layout.findViewById(view_id) + val view_index = layout.indexOfChild(caller_heading) + Log.d("Baresip", "Index of caller heading is $view_index") + layout.removeViews(view_index, 4 * remove_count) + for (i in call_index until In.size) { + this@MainActivity.addCallViews(In[i], (i + 1) * 10) + } + } + } + if (!callHasHistory(ua, call)) { + HistoryActivity.History.add(History(ua, call, aor, call_peeruri(call), + "in", false)) + } + } else { + call_index = callIndex(Out, ua, call) + if (call_index != -1) { + Log.d("Baresip", "Removing outgoing call " + ua + "/" + + call + "/" + Out[call_index].peerURI) + Out.removeAt(call_index) + runOnUiThread { + if (ua == aor_ua(AoRs[AoRSpinner.selectedItemPosition])) { + callButton.text = "Call" + callButton.isEnabled = true + callee.setText("") + callee.hint = "Callee" + holdButton.text = "History" + holdButton.visibility = View.VISIBLE + } + } + if (!callHasHistory(ua, call)) { + HistoryActivity.History.add(History(ua, call, aor, call_peeruri(call), + "out", false)) + } + } else { + Log.e("Baresip", "Unknown call " + ua + "/" + call + + " closed") + } + } + } + else -> Log.d("Baresip", "Unknown event '$event'") + } + } + } + } + + external fun baresipStart(path: String) + external fun baresipStop() + external fun call_peeruri(call: String): String + external fun ua_aor(ua: String): String + external fun aor_ua(aor: String): String + external fun ua_connect(ua: String, peer_uri: String): String + external fun ua_answer(ua: String, call: String) + external fun call_hold(call: String): Int? + external fun call_unhold(call: String): Int? + external fun ua_hangup(ua: String, call: String, code: Int, reason: String) + external fun reload_config(): Int + + companion object { + + internal lateinit var mainActivityContext: Context + internal lateinit var layout: RelativeLayout + internal lateinit var callee: AutoCompleteTextView + internal lateinit var callButton: Button + internal lateinit var holdButton: Button + internal lateinit var AccountAdapter: AccountSpinnerAdapter + internal lateinit var AoRSpinner: Spinner + + internal var running: Boolean = false + internal var Accounts = ArrayList() + internal var AoRs = ArrayList() + internal var Images = ArrayList() + internal var CalleeAdapter: ArrayAdapter? = null + internal var In = ArrayList() + internal var Out = ArrayList() + + private val RECORD_AUDIO_PERMISSION = 1 + private val EDIT_ACCOUNTS_CODE = 1 + private val EDIT_CONTACTS_CODE = 2 + private val EDIT_CONFIG_CODE = 3 + private val HISTORY_CODE = 4 + private val ABOUT_CODE = 5 + + external fun contacts_remove() + external fun contact_add(contact: String) + + init { + System.loadLibrary("baresip") + } + } + +} diff --git a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt new file mode 100644 index 00000000..ac373c83 --- /dev/null +++ b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt @@ -0,0 +1,67 @@ +package com.tutpro.baresip + +import android.content.Context +import android.content.DialogInterface +import android.support.v7.app.AlertDialog +import android.util.Log + +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.io.OutputStreamWriter + +object Utils { + + fun getFileContents(file: File): String { + if (!file.exists()) { + Log.e("Baresip", "Failed to find file: " + file.path) + return "" + } else { + Log.e("Baresip", "Found file: " + file.path) + val length = file.length().toInt() + val bytes = ByteArray(length) + try { + val `in` = FileInputStream(file) + try { + `in`.read(bytes) + } finally { + `in`.close() + } + return String(bytes) + } catch (e: java.io.IOException) { + Log.e("Baresip", "Failed to read file: " + file.path + ": " + + e.toString()) + return "" + } + + } + } + + fun putFileContents(file: File, contents: String) { + try { + val fOut = FileOutputStream(file.absoluteFile, false) + val fWriter = OutputStreamWriter(fOut) + try { + fWriter.write(contents) + fWriter.close() + fOut.close() + } catch (e: java.io.IOException) { + Log.e("Baresip", "Failed to put contents to file: " + e.toString()) + } + + } catch (e: java.io.FileNotFoundException) { + Log.e("Baresip", "Failed to find contents file: " + e.toString()) + } + + } + + fun alertView(context: Context, message: String) { + val alertDialog = AlertDialog.Builder(context).create() + alertDialog.setTitle("Alert") + alertDialog.setMessage(message) + alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "OK" + ) { dialog, _ -> dialog.dismiss() } + alertDialog.show() + } + +} diff --git a/build.gradle b/build.gradle index a47fa4bb..94c35228 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,13 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext.kotlin_version = '1.2.30' repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files