first commit
108
app/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.tutpro.baresip"
|
||||
android:installLocation="auto">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
|
||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.telephony"
|
||||
android:required="false" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-feature
|
||||
android:name="android.hardware.camera"
|
||||
android:required="false" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
|
||||
|
||||
<application
|
||||
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<activity android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize">
|
||||
<!-- android:screenOrientation="portrait"> -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".AccountsActivity"
|
||||
android:label="Accounts"
|
||||
android:parentActivityName=".MainActivity"
|
||||
android:theme="@style/AppTheme">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.tutpro.baresip.MainActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".EditAccountsActivity"
|
||||
android:label="Edit Accounts"
|
||||
android:parentActivityName=".MainActivity"
|
||||
android:theme="@style/AppTheme">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.tutpro.baresip.MainActivity" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".EditConfigActivity"
|
||||
android:label="Edit Config"
|
||||
android:parentActivityName=".MainActivity"
|
||||
android:theme="@style/AppTheme">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.tutpro.baresip.MainActivity" />
|
||||
</activity>
|
||||
|
||||
<!-- <activity
|
||||
android:name=".EditContactsActivity"
|
||||
android:label="Edit Contacts"
|
||||
android:parentActivityName=".MainActivity"
|
||||
android:theme="@style/AppTheme">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.tutpro.baresip.MainActivity" />
|
||||
</activity> -->
|
||||
|
||||
<activity
|
||||
android:name=".AboutActivity"
|
||||
android:label="About"
|
||||
android:parentActivityName=".MainActivity"
|
||||
android:theme="@style/AppTheme">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.tutpro.baresip.MainActivity" />
|
||||
</activity>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
34
app/src/main/assets/accounts
Normal file
@ -0,0 +1,34 @@
|
||||
#
|
||||
# SIP accounts - one account per line
|
||||
#
|
||||
# Displayname <sip:user:password@domain;uri-params>;addr-params
|
||||
#
|
||||
# uri-params:
|
||||
# ;transport={udp,tcp,tls}
|
||||
#
|
||||
# addr-params:
|
||||
# ;answermode={manual,early,auto}
|
||||
# ;audio_codecs=speex/16000,pcma,...
|
||||
# ;auth_user=username
|
||||
# ;mediaenc={srtp,srtp-mand,srtp-mandf,dtls_srtp,zrtp}
|
||||
# ;medianat={stun,turn,ice}
|
||||
# ;outbound="sip:primary.example.com;transport=tcp"
|
||||
# ;outbound2=sip:secondary.example.com
|
||||
# ;ptime={10,20,30,40,...}
|
||||
# ;regint=3600
|
||||
# ;pubint=0 (publishing off)
|
||||
# ;regq=0.5
|
||||
# ;rtpkeep={zero,stun,dyna,rtcp}
|
||||
# ;sipnat={outbound}
|
||||
# ;stunuser=STUN/TURN/ICE-username
|
||||
# ;stunpass=STUN/TURN/ICE-password
|
||||
# ;stunserver=stun:[user:pass]@host[:port]
|
||||
# ;video_codecs=h264,h263,...
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# <sip:user:secret@domain.com;transport=tcp>
|
||||
# <sip:user:secret@1.2.3.4;transport=tcp>
|
||||
# <sip:user:secret@[2001:df8:0:16:216:6fff:fe91:614c]:5070;transport=tcp>
|
||||
#
|
||||
<sip:foo:password@test.tutpro.com>;auth_user=foo;outbound="sip:192.168.43.98:5060;transport=tcp";ptime=20;audio_codecs=OPUS/48000/2,speex/16000,speex/8000;video_codecs="";regint=600;pubint=0;sipnat=outbound
|
||||
BIN
app/src/main/assets/busy.wav
Normal file
BIN
app/src/main/assets/callwaiting.wav
Normal file
200
app/src/main/assets/config
Normal file
@ -0,0 +1,200 @@
|
||||
#
|
||||
# baresip configuration
|
||||
#
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# Core
|
||||
poll_method epoll # poll, select, epoll ..
|
||||
|
||||
# SIP
|
||||
sip_trans_bsize 128
|
||||
#sip_listen 0.0.0.0:5060
|
||||
#sip_certificate cert.pem
|
||||
|
||||
# Call
|
||||
call_local_timeout 120
|
||||
call_max_calls 4
|
||||
|
||||
# Audio
|
||||
audio_player opensles,nil
|
||||
audio_source opensles,nil
|
||||
audio_alert opensles,nil
|
||||
audio_srate 8000-48000
|
||||
audio_channels 1-2
|
||||
#ausrc_srate 48000
|
||||
#auplay_srate 48000
|
||||
#ausrc_channels 0
|
||||
#auplay_channels 0
|
||||
audio_level no
|
||||
|
||||
# Video
|
||||
#video_source v4l2,/dev/video0
|
||||
#video_display x11,nil
|
||||
video_size 352x288
|
||||
video_bitrate 500000
|
||||
video_fps 25
|
||||
video_fullscreen yes
|
||||
|
||||
# AVT - Audio/Video Transport
|
||||
rtp_tos 184
|
||||
#rtp_ports 10000-20000
|
||||
#rtp_bandwidth 512-1024 # [kbit/s]
|
||||
rtcp_enable yes
|
||||
rtcp_mux no
|
||||
jitter_buffer_delay 5-10 # frames
|
||||
rtp_stats no
|
||||
#rtp_timeout 60
|
||||
|
||||
# Network
|
||||
#dns_server 10.0.0.1:53
|
||||
#net_interface ^
|
||||
|
||||
# BFCP
|
||||
#bfcp_proto udp
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Modules
|
||||
|
||||
# UI Modules
|
||||
#module stdio.so
|
||||
#module cons.so
|
||||
#module evdev.so
|
||||
#module httpd.so
|
||||
|
||||
# Audio codec Modules (in order)
|
||||
module opus.so
|
||||
#module silk.so
|
||||
#module amr.so
|
||||
#module g7221.so
|
||||
#module g722.so
|
||||
#module g726.so
|
||||
module g711.so
|
||||
#module gsm.so
|
||||
#module l16.so
|
||||
module speex.so
|
||||
#module bv32.so
|
||||
#module mpa.so
|
||||
#module codec2.so
|
||||
#module ilbc.so
|
||||
#module isac.so
|
||||
|
||||
# Audio filter Modules (in encoding order)
|
||||
#module vumeter.so
|
||||
#module sndfile.so
|
||||
#module speex_aec.so
|
||||
#module speex_pp.so
|
||||
#module plc.so
|
||||
|
||||
# Audio driver Modules
|
||||
module opensles.so
|
||||
#module jack.so
|
||||
#module portaudio.so
|
||||
#module aubridge.so
|
||||
#module aufile.so
|
||||
|
||||
# Video codec Modules (in order)
|
||||
#module avcodec.so
|
||||
#module vp8.so
|
||||
#module vp9.so
|
||||
#module h265.so
|
||||
|
||||
# Video filter Modules (in encoding order)
|
||||
#module selfview.so
|
||||
#module snapshot.so
|
||||
#module swscale.so
|
||||
#module vidinfo.so
|
||||
|
||||
# Video source modules
|
||||
#module v4l.so
|
||||
#module v4l2.so
|
||||
#module v4l2_codec.so
|
||||
#module x11grab.so
|
||||
#module cairo.so
|
||||
#module vidbridge.so
|
||||
|
||||
# Video display modules
|
||||
#module directfb.so
|
||||
#module x11.so
|
||||
#module sdl2.so
|
||||
#module fakevideo.so
|
||||
|
||||
# Audio/Video source modules
|
||||
#module rst.so
|
||||
#module gst1.so
|
||||
#module gst_video1.so
|
||||
|
||||
# Media NAT modules
|
||||
module stun.so
|
||||
module turn.so
|
||||
module ice.so
|
||||
#module natpmp.so
|
||||
|
||||
# Media encryption modules
|
||||
#module srtp.so
|
||||
#module dtls_srtp.so
|
||||
#module zrtp.so
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Temporary Modules (loaded then unloaded)
|
||||
|
||||
module_tmp uuid.so
|
||||
module_tmp account.so
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Application Modules
|
||||
|
||||
module_app auloop.so
|
||||
module_app contact.so
|
||||
module_app debug_cmd.so
|
||||
#module_app dtmfio.so
|
||||
#module_app echo.so
|
||||
#module_app gtk.so
|
||||
module_app menu.so
|
||||
#module_app mwi.so
|
||||
#module_app natbd.so
|
||||
#module_app presence.so
|
||||
#module_app syslog.so
|
||||
module_app vidloop.so
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Module parameters
|
||||
|
||||
|
||||
cons_listen 0.0.0.0:5555
|
||||
|
||||
http_listen 0.0.0.0:8000
|
||||
|
||||
evdev_device /dev/input/event0
|
||||
|
||||
# Speex codec parameters
|
||||
speex_quality 7 # 0-10
|
||||
speex_complexity 7 # 0-10
|
||||
speex_enhancement 0 # 0-1
|
||||
speex_mode_nb 3 # 1-6
|
||||
speex_mode_wb 6 # 1-6
|
||||
speex_vbr 0 # Variable Bit Rate 0-1
|
||||
speex_vad 0 # Voice Activity Detection 0-1
|
||||
speex_agc_level 8000
|
||||
|
||||
# Opus codec parameters
|
||||
opus_bitrate 28000 # 6000-510000
|
||||
opus_cbr no
|
||||
opus_inbandfec yes
|
||||
|
||||
# Selfview
|
||||
#video_selfview window # {window,pip}
|
||||
#selfview_size 64x64
|
||||
|
||||
# ICE
|
||||
ice_turn no
|
||||
ice_debug no
|
||||
ice_nomination regular # {regular,aggressive}
|
||||
ice_mode full # {full,lite}
|
||||
|
||||
# Menu
|
||||
#redial_attempts 3 # Num or <inf>
|
||||
#redial_delay 5 # Delay in seconds
|
||||
17
app/src/main/assets/contacts
Normal file
@ -0,0 +1,17 @@
|
||||
#
|
||||
# SIP contacts
|
||||
#
|
||||
# Displayname <sip:user@domain>;addr-params
|
||||
#
|
||||
# addr-params:
|
||||
# ;presence={none,p2p}
|
||||
# ;access={allow,block}
|
||||
#
|
||||
|
||||
"Echo Server" <sip:echo@creytiv.com>
|
||||
#"user" <sip:user@domain>;presence=p2p
|
||||
|
||||
|
||||
# Access rules
|
||||
#"Catch All" <sip:*@*>;access=block
|
||||
#"Good Friend" <sip:good@friend.com>;access=allow
|
||||
BIN
app/src/main/assets/error.wav
Normal file
BIN
app/src/main/assets/message.wav
Normal file
BIN
app/src/main/assets/notfound.wav
Normal file
BIN
app/src/main/assets/ring.wav
Normal file
BIN
app/src/main/assets/ringback.wav
Normal file
80
app/src/main/cpp/CMakeLists.txt
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
#
|
||||
# Copyright (C) The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
# configure import libs
|
||||
set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../distribution)
|
||||
|
||||
add_library(lib_crypto STATIC IMPORTED)
|
||||
set_target_properties(lib_crypto PROPERTIES IMPORTED_LOCATION
|
||||
${distribution_DIR}/openssl/lib/${ANDROID_ABI}/libcrypto.a)
|
||||
|
||||
add_library(lib_ssl STATIC IMPORTED)
|
||||
set_target_properties(lib_ssl PROPERTIES IMPORTED_LOCATION
|
||||
${distribution_DIR}/openssl/lib/${ANDROID_ABI}/libssl.a)
|
||||
|
||||
add_library(lib_re STATIC IMPORTED)
|
||||
set_target_properties(lib_re PROPERTIES IMPORTED_LOCATION
|
||||
${distribution_DIR}/re/lib/${ANDROID_ABI}/libre.a)
|
||||
|
||||
add_library(lib_rem STATIC IMPORTED)
|
||||
set_target_properties(lib_rem PROPERTIES IMPORTED_LOCATION
|
||||
${distribution_DIR}/rem/lib/${ANDROID_ABI}/librem.a)
|
||||
|
||||
add_library(lib_speex STATIC IMPORTED)
|
||||
set_target_properties(lib_speex PROPERTIES IMPORTED_LOCATION
|
||||
${distribution_DIR}/speex/lib/${ANDROID_ABI}/libspeex.a)
|
||||
|
||||
add_library(lib_opus STATIC IMPORTED)
|
||||
set_target_properties(lib_opus PROPERTIES IMPORTED_LOCATION
|
||||
${distribution_DIR}/opus/lib/${ANDROID_ABI}/libopus.a)
|
||||
|
||||
add_library(lib_bn STATIC IMPORTED)
|
||||
set_target_properties(lib_bn PROPERTIES IMPORTED_LOCATION
|
||||
${distribution_DIR}/bn/lib/${ANDROID_ABI}/libbn.a)
|
||||
|
||||
add_library(lib_zrtp STATIC IMPORTED)
|
||||
set_target_properties(lib_zrtp PROPERTIES IMPORTED_LOCATION
|
||||
${distribution_DIR}/zrtp/lib/${ANDROID_ABI}/libzrtp.a)
|
||||
|
||||
add_library(lib_baresip STATIC IMPORTED)
|
||||
set_target_properties(lib_baresip PROPERTIES IMPORTED_LOCATION
|
||||
${distribution_DIR}/baresip/lib/${ANDROID_ABI}/libbaresip.a)
|
||||
|
||||
add_library(baresip SHARED baresip.c)
|
||||
|
||||
target_include_directories(baresip PRIVATE
|
||||
${distribution_DIR}/openssl/include
|
||||
${distribution_DIR}/re/include
|
||||
${distribution_DIR}/rem/include
|
||||
${distribution_DIR}/baresip/include)
|
||||
|
||||
target_link_libraries(baresip
|
||||
android
|
||||
OpenSLES
|
||||
lib_baresip
|
||||
lib_rem
|
||||
lib_re
|
||||
lib_ssl
|
||||
lib_crypto
|
||||
lib_speex
|
||||
lib_opus
|
||||
lib_zrtp
|
||||
lib_bn
|
||||
z
|
||||
log)
|
||||
458
app/src/main/cpp/baresip.c
Normal file
@ -0,0 +1,458 @@
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
#include <stdlib.h>
|
||||
#include <re.h>
|
||||
#include <baresip.h>
|
||||
|
||||
#define LOGD(...) \
|
||||
((void)__android_log_print(ANDROID_LOG_DEBUG, "Baresip", __VA_ARGS__))
|
||||
|
||||
#define LOGI(...) \
|
||||
((void)__android_log_print(ANDROID_LOG_INFO, "Baresip", __VA_ARGS__))
|
||||
|
||||
#define LOGW(...) \
|
||||
((void)__android_log_print(ANDROID_LOG_WARN, "Baresip", __VA_ARGS__))
|
||||
|
||||
#define LOGE(...) \
|
||||
((void)__android_log_print(ANDROID_LOG_ERROR, "Baresip", __VA_ARGS__))
|
||||
|
||||
typedef struct tick_context {
|
||||
JavaVM *javaVM;
|
||||
jclass jniHelperClz;
|
||||
jobject jniHelperObj;
|
||||
jclass mainActivityClz;
|
||||
jobject mainActivityObj;
|
||||
pthread_mutex_t lock;
|
||||
int done;
|
||||
} TickContext;
|
||||
|
||||
TickContext g_ctx;
|
||||
|
||||
static void signal_handler(int sig)
|
||||
{
|
||||
static bool term = false;
|
||||
|
||||
if (term) {
|
||||
mod_close();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
term = true;
|
||||
LOGI("terminated by signal (%d)\n", sig);
|
||||
ua_stop_all(false);
|
||||
}
|
||||
|
||||
static void ua_exit_handler(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
LOGD("ua exited -- stopping main runloop\n");
|
||||
re_cancel();
|
||||
}
|
||||
|
||||
static const char *ua_event_reg_str(enum ua_event ev)
|
||||
{
|
||||
switch (ev) {
|
||||
case UA_EVENT_REGISTERING: return "registering";
|
||||
case UA_EVENT_REGISTER_OK: return "registered";
|
||||
case UA_EVENT_REGISTER_FAIL: return "registering failed";
|
||||
case UA_EVENT_UNREGISTERING: return "unregistering";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
static void ua_event_handler(struct ua *ua, enum ua_event ev,
|
||||
struct call *call, const char *prm, void *arg)
|
||||
{
|
||||
const char *event;
|
||||
char event_buf[256];
|
||||
char ua_buf[256];
|
||||
char call_buf[256];
|
||||
|
||||
LOGD("ua event (%s)\n", uag_event_str(ev));
|
||||
|
||||
switch (ev) {
|
||||
case UA_EVENT_REGISTERING:
|
||||
case UA_EVENT_UNREGISTERING:
|
||||
case UA_EVENT_REGISTER_OK:
|
||||
case UA_EVENT_REGISTER_FAIL:
|
||||
re_snprintf(event_buf, sizeof event_buf, "%s", ua_event_reg_str(ev));
|
||||
break;
|
||||
case UA_EVENT_CALL_RINGING:
|
||||
re_snprintf(event_buf, sizeof event_buf, "%s", "call ringing");
|
||||
break;
|
||||
case UA_EVENT_CALL_PROGRESS:
|
||||
re_snprintf(event_buf, sizeof event_buf, "%s", "call progress");
|
||||
break;
|
||||
case UA_EVENT_CALL_ESTABLISHED:
|
||||
re_snprintf(event_buf, sizeof event_buf, "%s", "call established");
|
||||
break;
|
||||
case UA_EVENT_CALL_INCOMING:
|
||||
re_snprintf(event_buf, sizeof event_buf, "%s", "call incoming");
|
||||
break;
|
||||
case UA_EVENT_CALL_CLOSED:
|
||||
re_snprintf(event_buf, sizeof event_buf, "%s", "call closed");
|
||||
break;
|
||||
case UA_EVENT_EXIT:
|
||||
re_snprintf(event_buf, sizeof event_buf, "%s", "exit");
|
||||
break;
|
||||
default:
|
||||
re_snprintf(event_buf, sizeof event_buf, "%s", "unknown event");
|
||||
}
|
||||
event = event_buf;
|
||||
|
||||
TickContext *pctx = (TickContext*)(&g_ctx);
|
||||
JavaVM *javaVM = pctx->javaVM;
|
||||
JNIEnv *env;
|
||||
jint res = (*javaVM)->GetEnv(javaVM, (void**)&env, JNI_VERSION_1_6);
|
||||
if (res != JNI_OK) {
|
||||
res = (*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
|
||||
if (JNI_OK != res) {
|
||||
LOGE("Failed to AttachCurrentThread, ErrorCode = %d", res);
|
||||
return;
|
||||
}
|
||||
}
|
||||
jmethodID statusId = (*env)->GetMethodID(env, pctx->jniHelperClz,
|
||||
"updateStatus",
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
|
||||
sprintf(ua_buf, "%lu", (unsigned long)ua);
|
||||
jstring javaUA = (*env)->NewStringUTF(env, ua_buf);
|
||||
sprintf(call_buf, "%lu", (unsigned long)call);
|
||||
jstring javaCall = (*env)->NewStringUTF(env, call_buf);
|
||||
jstring javaEvent = (*env)->NewStringUTF(env, event);
|
||||
LOGD("sending ua/call %s/%s event %s\n", ua_buf, call_buf, event);
|
||||
(*env)->CallVoidMethod(env, pctx->jniHelperObj, statusId, javaEvent, javaUA, javaCall);
|
||||
(*env)->DeleteLocalRef(env, javaUA);
|
||||
(*env)->DeleteLocalRef(env, javaCall);
|
||||
(*env)->DeleteLocalRef(env, javaEvent);
|
||||
}
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
static int pfd[2];
|
||||
static pthread_t loggingThread;
|
||||
|
||||
static void *loggingFunction() {
|
||||
ssize_t readSize;
|
||||
char buf[128];
|
||||
|
||||
while((readSize = read(pfd[0], buf, sizeof buf - 1)) > 0) {
|
||||
if(buf[readSize - 1] == '\n') {
|
||||
--readSize;
|
||||
}
|
||||
buf[readSize] = 0;
|
||||
LOGD("%s", buf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int runLoggingThread() {
|
||||
setvbuf(stdout, 0, _IOLBF, 0);
|
||||
setvbuf(stderr, 0, _IONBF, 0);
|
||||
|
||||
pipe(pfd);
|
||||
dup2(pfd[1], 1);
|
||||
dup2(pfd[1], 2);
|
||||
|
||||
if (pthread_create(&loggingThread, 0, loggingFunction, 0) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_detach(loggingThread);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
JNIEnv* env;
|
||||
|
||||
LOGD("Executing JNI_OnLoad\n");
|
||||
|
||||
memset(&g_ctx, 0, sizeof(g_ctx));
|
||||
|
||||
g_ctx.javaVM = vm;
|
||||
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
|
||||
return JNI_ERR; // JNI version not supported.
|
||||
}
|
||||
|
||||
jclass clz = (*env)->FindClass(env, "com/tutpro/baresip/MainActivity");
|
||||
g_ctx.jniHelperClz = (*env)->NewGlobalRef(env, clz);
|
||||
|
||||
jmethodID jniHelperCtor = (*env)->GetMethodID(env, g_ctx.jniHelperClz,
|
||||
"<init>", "()V");
|
||||
jobject handler = (*env)->NewObject(env, g_ctx.jniHelperClz,
|
||||
jniHelperCtor);
|
||||
g_ctx.jniHelperObj = (*env)->NewGlobalRef(env, handler);
|
||||
|
||||
g_ctx.done = 0;
|
||||
g_ctx.mainActivityObj = NULL;
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_tutpro_baresip_MainActivity_baresipStart(JNIEnv *env, jobject thiz, jstring javaPath)
|
||||
{
|
||||
int err;
|
||||
const char *path = (*env)->GetStringUTFChars(env, javaPath, 0);
|
||||
struct le *le;
|
||||
|
||||
runLoggingThread();
|
||||
|
||||
err = libre_init();
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
conf_path_set(path);
|
||||
|
||||
err = conf_configure();
|
||||
if (err) {
|
||||
LOGW("conf_configure() failed: (%d)\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = baresip_init(conf_config(), false);
|
||||
if (err) {
|
||||
LOGW("baresip_init() failed (%d)\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
play_set_path(baresip_player(), path);
|
||||
|
||||
err = ua_init("baresip v" BARESIP_VERSION " (" ARCH "/" OS ")",
|
||||
true, true, true, false);
|
||||
if (err) {
|
||||
LOGE("ua_init() failed (%d)\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
uag_set_exit_handler(ua_exit_handler, NULL);
|
||||
uag_event_register(ua_event_handler, NULL);
|
||||
|
||||
err = conf_modules();
|
||||
if (err) {
|
||||
LOGW("conf_modules() failed (%d)\n", err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
TickContext *pctx = (TickContext*)(&g_ctx);
|
||||
JavaVM *javaVM = pctx->javaVM;
|
||||
jint res = (*javaVM)->GetEnv(javaVM, (void**)&env, JNI_VERSION_1_6);
|
||||
if (res != JNI_OK) {
|
||||
res = (*javaVM)->AttachCurrentThread(javaVM, &env, NULL);
|
||||
if (JNI_OK != res) {
|
||||
LOGE("Failed to AttachCurrentThread, ErrorCode = %d", res);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LOGD("Adding %u accounts", list_count(uag_list()));
|
||||
char ua_buf[256];
|
||||
struct ua *ua;
|
||||
for (le = list_head(uag_list()); le; le = le->next) {
|
||||
ua = le->data;
|
||||
sprintf(ua_buf, "%lu", (unsigned long)ua);
|
||||
jstring javaUA = (*env)->NewStringUTF(env, ua_buf);
|
||||
LOGD("adding account %s/%s\n", ua_aor(ua), ua_buf);
|
||||
jmethodID accountId = (*env)->GetMethodID(env, pctx->jniHelperClz, "addAccount",
|
||||
"(Ljava/lang/String;)V");
|
||||
(*env)->CallVoidMethod(env, pctx->jniHelperObj, accountId, javaUA);
|
||||
(*env)->DeleteLocalRef(env, javaUA);
|
||||
}
|
||||
|
||||
LOGI("Running main loop\n");
|
||||
err = re_main(signal_handler);
|
||||
|
||||
out:
|
||||
|
||||
if (err) {
|
||||
LOGE("error: (%d)\n", err);
|
||||
ua_stop_all(true);
|
||||
}
|
||||
|
||||
LOGD("closing upon main loop exit");
|
||||
ua_close();
|
||||
conf_close();
|
||||
baresip_close();
|
||||
mod_close();
|
||||
libre_close();
|
||||
|
||||
// tmr_debug();
|
||||
// mem_debug();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_tutpro_baresip_MainActivity_baresipStop(JNIEnv *env, jobject thiz)
|
||||
{
|
||||
LOGD("closing upon stop");
|
||||
ua_stop_all(false);
|
||||
ua_close();
|
||||
// conf_close();
|
||||
// baresip_close();
|
||||
// mod_close();
|
||||
// libre_close();
|
||||
|
||||
// tmr_debug();
|
||||
// mem_debug();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_com_tutpro_baresip_MainActivity_ua_1aor(JNIEnv *env, jobject thiz, jstring javaUA)
|
||||
{
|
||||
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
|
||||
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
|
||||
|
||||
if (strlen(native_ua) > 0)
|
||||
return (*env)->NewStringUTF(env, ua_aor(ua));
|
||||
else
|
||||
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
|
||||
Java_com_tutpro_baresip_MainActivity_ua_1current_1set(JNIEnv *env, jobject thiz,
|
||||
jstring javaAoR)
|
||||
{
|
||||
struct ua *new_current_ua;
|
||||
const char *native_aor = (*env)->GetStringUTFChars(env, javaAoR, 0);
|
||||
|
||||
LOGD("running ua_current_set on %s\n", native_aor);
|
||||
new_current_ua = uag_find_aor(native_aor);
|
||||
uag_current_set(new_current_ua);
|
||||
(*env)->ReleaseStringUTFChars(env, javaAoR, native_aor);
|
||||
return;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_com_tutpro_baresip_MainActivity_ua_1current(JNIEnv *env, jobject thiz)
|
||||
{
|
||||
struct ua *current_ua = uag_current();
|
||||
char ua_buf[256];
|
||||
|
||||
if (current_ua == NULL)
|
||||
ua_buf[0] = '\0';
|
||||
else
|
||||
sprintf(ua_buf, "%lu", (unsigned long)current_ua);
|
||||
return (*env)->NewStringUTF(env, ua_buf);
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_com_tutpro_baresip_MainActivity_ua_1prev_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_prev_call(ua);
|
||||
char call_buf[256];
|
||||
if (call == NULL)
|
||||
call_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);
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_com_tutpro_baresip_MainActivity_call_1peeruri(JNIEnv *env, jobject thiz, jstring javaCall)
|
||||
{
|
||||
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
|
||||
struct call *call;
|
||||
|
||||
call = (struct call *)strtoul(native_call, NULL, 10);
|
||||
(*env)->ReleaseStringUTFChars(env, javaCall, native_call);
|
||||
return (*env)->NewStringUTF(env, call_peeruri(call));
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_com_tutpro_baresip_MainActivity_ua_1connect(JNIEnv *env, jobject thiz,
|
||||
jstring uri) {
|
||||
struct call *call;
|
||||
struct ua *ua;
|
||||
int err;
|
||||
const char *native_uri = (*env)->GetStringUTFChars(env, uri, 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");
|
||||
call_buf[0] = '\0';
|
||||
}
|
||||
(*env)->ReleaseStringUTFChars(env, uri, native_uri);
|
||||
return (*env)->NewStringUTF(env, call_buf);
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_tutpro_baresip_MainActivity_ua_1answer(JNIEnv *env, jobject thiz,
|
||||
jstring javaUA, jstring javaCall) {
|
||||
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
|
||||
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
|
||||
LOGD("answering call %s/%s\n", native_ua, native_call);
|
||||
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
|
||||
struct call *call = (struct call *)strtoul(native_call, NULL, 10);
|
||||
ua_answer(ua, call);
|
||||
(*env)->ReleaseStringUTFChars(env, javaUA, native_ua);
|
||||
(*env)->ReleaseStringUTFChars(env, javaCall, native_call);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_tutpro_baresip_MainActivity_ua_1hangup(JNIEnv *env, jobject thiz,
|
||||
jstring javaUA, jstring javaCall, jint code,
|
||||
jstring reason) {
|
||||
const char *native_ua = (*env)->GetStringUTFChars(env, javaUA, 0);
|
||||
const char *native_call = (*env)->GetStringUTFChars(env, javaCall, 0);
|
||||
struct ua *ua = (struct ua *)strtoul(native_ua, NULL, 10);
|
||||
struct call *call = (struct call *)strtoul(native_call, NULL, 10);
|
||||
const uint16_t native_code = code;
|
||||
const char *native_reason = (*env)->GetStringUTFChars(env, reason, 0);
|
||||
LOGD("hanging up call %s/%s\n", native_ua, native_call);
|
||||
// ua_hangup(ua, call, native_code, native_reason);
|
||||
ua_hangup(uag_current(), NULL, 0, NULL);
|
||||
(*env)->ReleaseStringUTFChars(env, javaUA, native_ua);
|
||||
(*env)->ReleaseStringUTFChars(env, javaCall, native_call);
|
||||
(*env)->ReleaseStringUTFChars(env, reason, native_reason);
|
||||
return;
|
||||
}
|
||||
21
app/src/main/java/com/tutpro/baresip/AboutActivity.java
Normal file
@ -0,0 +1,21 @@
|
||||
package com.tutpro.baresip;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
34
app/src/main/java/com/tutpro/baresip/Account.java
Normal file
@ -0,0 +1,34 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
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<Integer> {
|
||||
|
||||
private ArrayList<Integer> images;
|
||||
private ArrayList<String> aors;
|
||||
private Context context;
|
||||
|
||||
public AccountSpinnerAdapter(Context context, ArrayList<String> aors, ArrayList<Integer> 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));
|
||||
ImageView imageView = (ImageView) row.findViewById(R.id.spinnerImage);
|
||||
imageView.setImageResource(images.get(position));
|
||||
return row;
|
||||
}
|
||||
}
|
||||
|
||||
32
app/src/main/java/com/tutpro/baresip/Call.java
Normal file
@ -0,0 +1,32 @@
|
||||
package com.tutpro.baresip;
|
||||
|
||||
public class Call {
|
||||
|
||||
private String ua, call, peer_uri, status;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
105
app/src/main/java/com/tutpro/baresip/EditAccountsActivity.java
Normal file
@ -0,0 +1,105 @@
|
||||
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(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 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");
|
||||
i.putExtra("action", "save");
|
||||
setResult(RESULT_OK, i);
|
||||
finish();
|
||||
return true;
|
||||
case R.id.cancel:
|
||||
i.putExtra("action", "cancel");
|
||||
setResult(RESULT_OK, i);
|
||||
finish();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
105
app/src/main/java/com/tutpro/baresip/EditConfigActivity.java
Normal file
@ -0,0 +1,105 @@
|
||||
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");
|
||||
i.putExtra("action", "save");
|
||||
setResult(RESULT_OK, i);
|
||||
finish();
|
||||
return true;
|
||||
case R.id.cancel:
|
||||
i.putExtra("action", "cancel");
|
||||
setResult(RESULT_OK, i);
|
||||
finish();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
104
app/src/main/java/com/tutpro/baresip/EditContactsActivity.java
Normal file
@ -0,0 +1,104 @@
|
||||
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 EditContactsActivity 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_contacts);
|
||||
|
||||
editText = (EditText)findViewById(R.id.editText);
|
||||
path = getApplicationContext().getFilesDir().getAbsolutePath() +
|
||||
"/contacts";
|
||||
file = new File(path);
|
||||
String content;
|
||||
|
||||
if (!file.exists()) {
|
||||
Log.e("Baresip", "Failed to find contacts file");
|
||||
content = "No contacts";
|
||||
} else {
|
||||
Log.e("Baresip", "Found contacts 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 contacts 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 contacts file: " +
|
||||
e.toString());
|
||||
}
|
||||
} catch (java.io.FileNotFoundException e) {
|
||||
Log.e("Baresip", "Failed to find contacts file: " +
|
||||
e.toString());
|
||||
}
|
||||
Log.d("Baresip", "Updated contacts file");
|
||||
i.putExtra("action", "save");
|
||||
startActivity(i);
|
||||
return true;
|
||||
case R.id.cancel:
|
||||
i.putExtra("action", "cancel");
|
||||
startActivity(i);
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
499
app/src/main/java/com/tutpro/baresip/MainActivity.java
Normal file
@ -0,0 +1,499 @@
|
||||
package com.tutpro.baresip;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.widget.*;
|
||||
import android.widget.RelativeLayout.LayoutParams;
|
||||
import android.view.*;
|
||||
import android.util.Log;
|
||||
import java.util.*;
|
||||
import java.io.*;
|
||||
import android.content.Context;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
static Context mainActivityContext;
|
||||
static Boolean running = false;
|
||||
static List <Account> accountList = new ArrayList<>();
|
||||
static EditText callee;
|
||||
static RelativeLayout layout;
|
||||
static Button callButton;
|
||||
static ArrayList<Account> Accounts = new ArrayList<>();
|
||||
static ArrayList<String> AoRs = new ArrayList<>();
|
||||
static ArrayList<Integer> Images = new ArrayList<>();
|
||||
static AccountSpinnerAdapter AccountAdapter = null;
|
||||
static ArrayList<Call> In = new ArrayList<>();
|
||||
static ArrayList<Call> Out = new ArrayList<>();
|
||||
|
||||
@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);
|
||||
ArrayList<Call> out = uaCalls(Out, ua_current());
|
||||
if (out.size() == 0) {
|
||||
callee.setText("");
|
||||
callButton.setText("Call");
|
||||
} else {
|
||||
callee.setText(out.get(0).getPeerURI());
|
||||
callButton.setText(out.get(0).getStatus());
|
||||
}
|
||||
ArrayList<Call> in = uaCalls(In, ua_current());
|
||||
int view_count = layout.getChildCount();
|
||||
Log.d("Baresip", "View count is " + view_count);
|
||||
if (view_count > 4) {
|
||||
layout.removeViews(4, view_count - 4);
|
||||
}
|
||||
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 = {"config", "accounts", "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;
|
||||
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);
|
||||
copyAsset(a, path + "/" + a);
|
||||
}
|
||||
}
|
||||
|
||||
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 = (EditText)findViewById(R.id.callee);
|
||||
|
||||
callButton = (Button)findViewById(R.id.callButton);
|
||||
callButton.setText("Call");
|
||||
callButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
String ua = ua_current();
|
||||
if (callButton.getText().toString().equals("Call")) {
|
||||
String uri = ((EditText) findViewById(R.id.callee)).getText().toString();
|
||||
if (!uri.startsWith("sip:")) uri = "sip:" + uri;
|
||||
if (!uri.contains("@")) {
|
||||
String aor = ua_aor(ua);
|
||||
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 " + ua + "/" + call +
|
||||
"/" + uri);
|
||||
Out.add(new Call(ua, call, uri,"Cancel"));
|
||||
callButton.setText("Cancel");
|
||||
}
|
||||
} else {
|
||||
Log.i("Baresip", "Hanging up " +
|
||||
((EditText) findViewById(R.id.callee)).getText());
|
||||
ua_hangup(ua, "", 486, "Rejected");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@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");
|
||||
}
|
||||
}
|
||||
|
||||
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 = new Intent(this, MainActivity.class);
|
||||
switch (item.getItemId()) {
|
||||
case R.id.accounts:
|
||||
startActivity(new Intent(MainActivity.this,
|
||||
EditAccountsActivity.class));
|
||||
return true;
|
||||
case R.id.config:
|
||||
startActivity(new Intent(MainActivity.this,
|
||||
EditConfigActivity.class));
|
||||
return true;
|
||||
//case R.id.contacts:
|
||||
// startActivity(new Intent(MainActivity.this,
|
||||
// EditContactsActivity.class));
|
||||
//return true;
|
||||
case R.id.about:
|
||||
startActivity(new Intent(MainActivity.this,
|
||||
AboutActivity.class));
|
||||
return true;
|
||||
case R.id.quit:
|
||||
if (running) {
|
||||
Log.d("Baresip", "Stopping");
|
||||
baresipStop();
|
||||
Accounts.clear();
|
||||
AoRs.clear();
|
||||
Images.clear();
|
||||
running = false;
|
||||
}
|
||||
finish();
|
||||
System.exit(0);
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<String> aorStatusList() {
|
||||
ArrayList<String> res = new ArrayList<>();
|
||||
for (Account a : Accounts) {
|
||||
res.add(a.getAoR() + " (" + a.getStatus() + ")");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
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 copyAsset(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("Answer");
|
||||
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", "Changing answer button text to Hangup");
|
||||
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.setEnabled(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
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);
|
||||
reject_button.setText("Reject");
|
||||
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) {
|
||||
Log.i("Baresip", "UA " + call.getUA() +
|
||||
" rejecting incoming call " + call.getCall());
|
||||
ua_hangup(call.getUA(), call.getCall(), 486, "Rejected");
|
||||
}
|
||||
});
|
||||
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<Call> 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<Call> uaCalls(ArrayList<Call> calls, String ua) {
|
||||
ArrayList<Call> 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 void updateStatus(String event, final String ua, final String call) {
|
||||
String aor = ua_aor(ua);
|
||||
int index, call_index;
|
||||
|
||||
Log.d("Baresip", "Handling event " + event + " for " + ua + "/" + call + "/" +
|
||||
aor);
|
||||
for (index = 0; index < Accounts.size(); index++) {
|
||||
if (Accounts.get(index).getAoR().equals(aor)) {
|
||||
Log.d("Baresip", "Found AoR at index " + 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(index).setStatus("OK");
|
||||
AoRs.set(index, aor);
|
||||
Images.set(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(index).setStatus("FAIL");
|
||||
AoRs.set(index, aor);
|
||||
Images.set(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 number is " + Out.size());
|
||||
int out_index = callIndex(Out, ua, call);
|
||||
if (out_index >= 0) {
|
||||
Log.d("Baresip", "Changing call button text to Hangup");
|
||||
Out.get(out_index).setStatus("Hangup");
|
||||
if (ua.equals(ua_current())) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callButton.setText("Hangup");
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
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);
|
||||
if (ua.equals(ua_current())) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
addCallViews(new_call, In.size() * 10);
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "call closed":
|
||||
call_index = callIndex(In, ua, call);
|
||||
if (call_index != -1) {
|
||||
Log.d("Baresip", "Removing inbound call " + ua + "/" +
|
||||
call + "/" + In.get(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 " + ua_current());
|
||||
if (ua.equals(ua_current())) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
call_index = callIndex(Out, ua, call);
|
||||
if (call_index != -1) {
|
||||
Log.d("Baresip", "Removing called call " + ua + "/" +
|
||||
call + "/" + Out.get(index).getPeerURI());
|
||||
Out.remove(call_index);
|
||||
if (ua.equals(ua_current())) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callButton.setText("Call");
|
||||
callButton.setEnabled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
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 native String ua_aor(String ua);
|
||||
public native Boolean ua_isregistered(long ua_ptr);
|
||||
public native String ua_call(String ua);
|
||||
public native String ua_prev_call(String ua);
|
||||
public native String call_peeruri(String call);
|
||||
public native String ua_current();
|
||||
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 void ua_hangup(String ua, String call, int code, String reason);
|
||||
|
||||
static {
|
||||
System.loadLibrary("baresip");
|
||||
}
|
||||
|
||||
}
|
||||
5
app/src/main/res/drawable/edit_aor.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="#d3d3d3" />
|
||||
</shape>
|
||||
BIN
app/src/main/res/drawable/green.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable/red.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/drawable/yellow.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
26
app/src/main/res/layout/account_spinner.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/spinnerLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" >
|
||||
|
||||
<TableRow
|
||||
android:id="@+id/spinnerRow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="5dip" >
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/spinnerImage"
|
||||
android:contentDescription="@string/app_name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/spinnerText"
|
||||
android:textSize="18dp"
|
||||
android:paddingLeft="6dip"
|
||||
android:textColor="@android:color/black" />
|
||||
|
||||
</TableRow>
|
||||
|
||||
</TableLayout>
|
||||
17
app/src/main/res/layout/activity_about.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context="com.tutpro.baresip.AboutActivity">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/aboutText"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
||||
|
||||
</RelativeLayout>
|
||||
18
app/src/main/res/layout/activity_edit_accounts.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context="com.tutpro.baresip.EditAccountsActivity">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editAccounts"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!" />
|
||||
|
||||
</RelativeLayout>
|
||||
18
app/src/main/res/layout/activity_edit_config.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context="com.tutpro.baresip.EditConfigActivity">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editConfig"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Hello World!" />
|
||||
|
||||
</RelativeLayout>
|
||||
18
app/src/main/res/layout/activity_edit_contacts.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
tools:context="com.tutpro.baresip.EditContactsActivity">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editText"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:text="Hello World!" />
|
||||
|
||||
</RelativeLayout>
|
||||
60
app/src/main/res/layout/activity_main.xml
Normal file
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context="com.tutpro.baresip.MainActivity"
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true" >
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/mainActivityLayout"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/AoRList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:drawable/btn_dropdown"
|
||||
android:spinnerMode="dropdown"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/outTitle"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/AoRList"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingLeft="3dp"
|
||||
android:textSize="20dp"
|
||||
android:textColor="@android:color/black"
|
||||
android:text="Outgoing call to ..." >
|
||||
</TextView>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/callee"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/outTitle"
|
||||
android:inputType="textEmailAddress"
|
||||
android:textSize="20dp"
|
||||
android:hint="Callee" >
|
||||
<requestFocus />
|
||||
</EditText>
|
||||
|
||||
<Button
|
||||
android:id="@+id/callButton"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:drawable/btn_default"
|
||||
android:layout_below="@id/callee"
|
||||
android:text="@id/callee" >
|
||||
</Button>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</ScrollView>
|
||||
12
app/src/main/res/menu/accounts_menu.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:id="@+id/stop"
|
||||
android:title="Stop" />
|
||||
|
||||
<item android:id="@+id/quit"
|
||||
android:title="Quit" />
|
||||
|
||||
<item android:id="@+id/help"
|
||||
android:title="Help" />
|
||||
|
||||
</menu>
|
||||
10
app/src/main/res/menu/edit_menu.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:id="@+id/save"
|
||||
android:title="Save" />
|
||||
|
||||
<item android:id="@+id/cancel"
|
||||
android:title="Cancel" />
|
||||
|
||||
</menu>
|
||||
|
||||
18
app/src/main/res/menu/main_menu.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:id="@+id/accounts"
|
||||
android:title="Accounts" />
|
||||
|
||||
<item android:id="@+id/config"
|
||||
android:title="Config" />
|
||||
|
||||
<!-- <item android:id="@+id/contacts"
|
||||
android:title="Contacts" /> -->
|
||||
|
||||
<item android:id="@+id/about"
|
||||
android:title="About" />
|
||||
|
||||
<item android:id="@+id/quit"
|
||||
android:title="Quit" />
|
||||
|
||||
</menu>
|
||||
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
6
app/src/main/res/values-w820dp/dimens.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||
(such as screen margins) for screens with more than 820dp of available width. This
|
||||
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||
</resources>
|
||||
6
app/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
</resources>
|
||||
6
app/src/main/res/values/dimens.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
<dimen name="fab_margin">16dp</dimen>
|
||||
</resources>
|
||||
4
app/src/main/res/values/dimensions.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="textsize">12sp</dimen>
|
||||
</resources>
|
||||
13
app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">baresip</string>
|
||||
<string name="title_activity_edit">EditActivity</string>
|
||||
<string name="title_activity_edit_accounts">EditAccounts</string>
|
||||
<string name="aboutText">
|
||||
Baresip Android application\n\n
|
||||
Work in progress\n\n
|
||||
Juha Heinanen jh@tutpro.com\n\n
|
||||
</string>
|
||||
<string name="noAccounts">No accounts</string>
|
||||
<string name="emptyString"></string>
|
||||
</resources>
|
||||
20
app/src/main/res/values/styles.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
|
||||
</resources>
|
||||