Added limited support for multiple simultaneously active network interfaces
Show notification when baresip is started without network access
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
|
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
|||||||
+25
-53
@@ -5,19 +5,9 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <re.h>
|
#include <re.h>
|
||||||
#include <baresip.h>
|
#include <baresip.h>
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
#define LOGD(...) \
|
#define LOG_TAG "Baresip Lib"
|
||||||
if (log_level_get() < LEVEL_INFO) ((void)__android_log_print(ANDROID_LOG_DEBUG, "Baresip Lib", __VA_ARGS__))
|
|
||||||
|
|
||||||
#define LOGI(...) \
|
|
||||||
if (log_level_get() < LEVEL_WARN) ((void)__android_log_print(ANDROID_LOG_DEBUG, "Baresip Lib", __VA_ARGS__))
|
|
||||||
|
|
||||||
#define LOGW(...) \
|
|
||||||
if (log_level_get() < LEVEL_ERROR) ((void)__android_log_print(ANDROID_LOG_DEBUG, "Baresip Lib", __VA_ARGS__))
|
|
||||||
|
|
||||||
#define LOGE(...) \
|
|
||||||
if (log_level_get() <= LEVEL_ERROR) ((void)__android_log_print(ANDROID_LOG_DEBUG, "Baresip Lib", __VA_ARGS__))
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct baresip_context {
|
typedef struct baresip_context {
|
||||||
JavaVM *javaVM;
|
JavaVM *javaVM;
|
||||||
@@ -202,6 +192,8 @@ static void ua_event_handler(struct ua *ua, enum ua_event ev,
|
|||||||
len = re_snprintf(event_buf, sizeof event_buf, "call answered", prm);
|
len = re_snprintf(event_buf, sizeof event_buf, "call answered", prm);
|
||||||
break;
|
break;
|
||||||
case UA_EVENT_CALL_LOCAL_SDP:
|
case UA_EVENT_CALL_LOCAL_SDP:
|
||||||
|
if (strcmp(prm, "offer") == 0)
|
||||||
|
return;
|
||||||
len = re_snprintf(event_buf, sizeof event_buf, "call %sed", prm);
|
len = re_snprintf(event_buf, sizeof event_buf, "call %sed", prm);
|
||||||
break;
|
break;
|
||||||
case UA_EVENT_CALL_RINGING:
|
case UA_EVENT_CALL_RINGING:
|
||||||
@@ -436,23 +428,12 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||||||
return JNI_VERSION_1_6;
|
return JNI_VERSION_1_6;
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL
|
|
||||||
Java_com_tutpro_baresip_Api_net_1add_1address(JNIEnv *env, jobject thiz, jstring javaIp);
|
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL
|
|
||||||
Java_com_tutpro_baresip_Api_net_1use_1nameserver(JNIEnv *env, jobject thiz, jstring javaServers);
|
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instance,
|
Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instance, jstring jPath,
|
||||||
jstring jPath, jstring jIpAddrs, jstring jNetInterface, jint jNetAf, jint jLogLevel) {
|
jstring jAddrs, jint jLogLevel) {
|
||||||
|
|
||||||
LOGI("starting baresip\n");
|
LOGI("starting baresip\n");
|
||||||
|
|
||||||
const char *net_interface = (*env)->GetStringUTFChars(env, jNetInterface, 0);
|
|
||||||
const int net_af = jNetAf;
|
|
||||||
|
|
||||||
const char *ip_addrs = (*env)->GetStringUTFChars(env, jIpAddrs, 0);
|
|
||||||
|
|
||||||
char start_error[64] = "";
|
char start_error[64] = "";
|
||||||
|
|
||||||
JavaVM *javaVM = g_ctx.javaVM;
|
JavaVM *javaVM = g_ctx.javaVM;
|
||||||
@@ -468,6 +449,7 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc
|
|||||||
|
|
||||||
int err;
|
int err;
|
||||||
const char *path = (*env)->GetStringUTFChars(env, jPath, 0);
|
const char *path = (*env)->GetStringUTFChars(env, jPath, 0);
|
||||||
|
const char *addrs = (*env)->GetStringUTFChars(env, jAddrs, 0);
|
||||||
struct le *le;
|
struct le *le;
|
||||||
|
|
||||||
runLoggingThread();
|
runLoggingThread();
|
||||||
@@ -487,15 +469,6 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen(net_interface) > 0) {
|
|
||||||
struct config *theconf = conf_config();
|
|
||||||
str_ncpy(theconf->net.ifname, net_interface,
|
|
||||||
sizeof(theconf->net.ifname));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (net_af != AF_UNSPEC)
|
|
||||||
conf_config()->net.af = net_af;
|
|
||||||
|
|
||||||
err = baresip_init(conf_config());
|
err = baresip_init(conf_config());
|
||||||
if (err) {
|
if (err) {
|
||||||
LOGW("baresip_init() failed (%d)\n", err);
|
LOGW("baresip_init() failed (%d)\n", err);
|
||||||
@@ -503,20 +476,21 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen(ip_addrs) > 0) {
|
if (strlen(addrs) > 0) {
|
||||||
char* addr_list = (char*)malloc(strlen(ip_addrs));
|
char* addr_list = (char*)malloc(strlen(addrs));
|
||||||
struct sa temp_sa;
|
struct sa temp_sa;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
net_flush_addresses(baresip_network());
|
net_flush_addresses(baresip_network());
|
||||||
strcpy(addr_list, ip_addrs);
|
strcpy(addr_list, addrs);
|
||||||
char *ptr = strtok(addr_list, ";");
|
char *ptr = strtok(addr_list, ";");
|
||||||
while (ptr != NULL) {
|
while (ptr != NULL) {
|
||||||
LOGI("adding address '%s'", ptr);
|
|
||||||
if (0 == sa_set_str(&temp_sa, ptr, 0)) {
|
if (0 == sa_set_str(&temp_sa, ptr, 0)) {
|
||||||
sa_ntop(&temp_sa, buf, 256);
|
sa_ntop(&temp_sa, buf, 256);
|
||||||
net_add_address(baresip_network(), &temp_sa);
|
ptr = strtok(NULL, ";");
|
||||||
|
net_add_address_ifname(baresip_network(), &temp_sa, ptr);
|
||||||
} else {
|
} else {
|
||||||
LOGE("invalid ip address %s\n", ptr);
|
LOGE("invalid ip address %s\n", ptr);
|
||||||
|
ptr = strtok(NULL, ";");
|
||||||
res = EAFNOSUPPORT;
|
res = EAFNOSUPPORT;
|
||||||
}
|
}
|
||||||
ptr = strtok(NULL, ";");
|
ptr = strtok(NULL, ";");
|
||||||
@@ -524,7 +498,7 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc
|
|||||||
free(addr_list);
|
free(addr_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
net_debug_log();
|
// net_debug_log();
|
||||||
|
|
||||||
play_set_path(baresip_player(), path);
|
play_set_path(baresip_player(), path);
|
||||||
|
|
||||||
@@ -572,7 +546,6 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc
|
|||||||
(*env)->DeleteLocalRef(env, javaUA);
|
(*env)->DeleteLocalRef(env, javaUA);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGI("allocating mqeue\n");
|
|
||||||
err = mqueue_alloc(&mq, mqueue_handler, NULL);
|
err = mqueue_alloc(&mq, mqueue_handler, NULL);
|
||||||
if (err) {
|
if (err) {
|
||||||
LOGW("mqueue_alloc failed (%d)\n", err);
|
LOGW("mqueue_alloc failed (%d)\n", err);
|
||||||
@@ -1674,26 +1647,25 @@ Java_com_tutpro_baresip_Api_net_1use_1nameserver(JNIEnv *env, jobject thiz, jstr
|
|||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL
|
JNIEXPORT jint JNICALL
|
||||||
Java_com_tutpro_baresip_Api_net_1add_1address(JNIEnv *env, jobject thiz, jstring javaIp) {
|
Java_com_tutpro_baresip_Api_net_1add_1address_1ifname(JNIEnv *env, jobject thiz, jstring jAddr,
|
||||||
const char *native_ip = (*env)->GetStringUTFChars(env, javaIp, 0);
|
jstring jIfName) {
|
||||||
|
const char *addr = (*env)->GetStringUTFChars(env, jAddr, 0);
|
||||||
|
const char *name = (*env)->GetStringUTFChars(env, jIfName, 0);
|
||||||
int res = 0;
|
int res = 0;
|
||||||
struct sa temp_sa;
|
struct sa temp_sa;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
LOGI("adding address '%s'\n", native_ip);
|
LOGD("adding address/ifname '%s/%s'\n", addr, name);
|
||||||
if (str_len(native_ip) == 0) {
|
if (0 == sa_set_str(&temp_sa, addr, 0)) {
|
||||||
(*env)->ReleaseStringUTFChars(env, javaIp, native_ip);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (0 == sa_set_str(&temp_sa, native_ip, 0)) {
|
|
||||||
sa_ntop(&temp_sa, buf, 256);
|
sa_ntop(&temp_sa, buf, 256);
|
||||||
re_thread_enter();
|
re_thread_enter();
|
||||||
net_add_address(baresip_network(), &temp_sa);
|
res = net_add_address_ifname(baresip_network(), &temp_sa, name);
|
||||||
re_thread_leave();
|
re_thread_leave();
|
||||||
} else {
|
} else {
|
||||||
LOGE("invalid ip address %s\n", native_ip);
|
LOGE("invalid ip address %s\n", addr);
|
||||||
res = EAFNOSUPPORT;
|
res = EAFNOSUPPORT;
|
||||||
}
|
}
|
||||||
(*env)->ReleaseStringUTFChars(env, javaIp, native_ip);
|
(*env)->ReleaseStringUTFChars(env, jAddr, addr);
|
||||||
|
(*env)->ReleaseStringUTFChars(env, jIfName, name);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1711,7 +1683,7 @@ Java_com_tutpro_baresip_Api_net_1rm_1address(JNIEnv *env, jobject thiz, jstring
|
|||||||
if (0 == sa_set_str(&temp_sa, native_ip, 0)) {
|
if (0 == sa_set_str(&temp_sa, native_ip, 0)) {
|
||||||
sa_ntop(&temp_sa, buf, 256);
|
sa_ntop(&temp_sa, buf, 256);
|
||||||
re_thread_enter();
|
re_thread_enter();
|
||||||
net_rm_address(baresip_network(), &temp_sa);
|
res = net_rm_address(baresip_network(), &temp_sa);
|
||||||
re_thread_leave();
|
re_thread_leave();
|
||||||
} else {
|
} else {
|
||||||
LOGE("invalid ip address %s\n", native_ip);
|
LOGE("invalid ip address %s\n", native_ip);
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ object Api {
|
|||||||
external fun log_level_set(level: Int)
|
external fun log_level_set(level: Int)
|
||||||
|
|
||||||
external fun net_use_nameserver(servers: String): Int
|
external fun net_use_nameserver(servers: String): Int
|
||||||
external fun net_add_address(ip_addr: String): Int
|
external fun net_add_address_ifname(ip_addr: String, if_name: String): Int
|
||||||
external fun net_rm_address(ip_addr: String): Int
|
external fun net_rm_address(ip_addr: String): Int
|
||||||
external fun net_debug()
|
external fun net_debug()
|
||||||
external fun net_dns_debug()
|
external fun net_dns_debug()
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import android.text.SpannableString
|
|||||||
import android.text.style.ForegroundColorSpan
|
import android.text.style.ForegroundColorSpan
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
import androidx.annotation.ColorRes
|
import androidx.annotation.ColorRes
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
@@ -48,15 +50,18 @@ class BaresipService: Service() {
|
|||||||
private lateinit var partialWakeLock: PowerManager.WakeLock
|
private lateinit var partialWakeLock: PowerManager.WakeLock
|
||||||
private lateinit var proximityWakeLock: PowerManager.WakeLock
|
private lateinit var proximityWakeLock: PowerManager.WakeLock
|
||||||
private lateinit var wifiLock: WifiManager.WifiLock
|
private lateinit var wifiLock: WifiManager.WifiLock
|
||||||
private lateinit var br: BroadcastReceiver
|
private lateinit var bluetoothReceiver: BroadcastReceiver
|
||||||
|
private lateinit var hotSpotReceiver: BroadcastReceiver
|
||||||
|
|
||||||
private var rtTimer: Timer? = null
|
private var rtTimer: Timer? = null
|
||||||
private var audioFocusRequest: AudioFocusRequest? = null
|
private var audioFocusRequest: AudioFocusRequest? = null
|
||||||
private var audioFocusUsage = -1
|
private var audioFocusUsage = -1
|
||||||
private var origVolume = -1
|
private var origVolume = -1
|
||||||
private val btAdapter = BluetoothAdapter.getDefaultAdapter()
|
private val btAdapter = BluetoothAdapter.getDefaultAdapter()
|
||||||
|
private var linkAddresses = mutableMapOf<String, String>()
|
||||||
private var activeNetwork: Network? = null
|
private var activeNetwork: Network? = null
|
||||||
private var linkAddresses = mutableListOf<LinkAddress>()
|
private var hotSpotIsEnabled = false
|
||||||
|
private var hotSpotAddresses = mapOf<String, String>()
|
||||||
|
|
||||||
@SuppressLint("WakelockTimeout")
|
@SuppressLint("WakelockTimeout")
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
@@ -102,21 +107,35 @@ class BaresipService: Service() {
|
|||||||
|
|
||||||
override fun onAvailable(network: Network) {
|
override fun onAvailable(network: Network) {
|
||||||
super.onAvailable(network)
|
super.onAvailable(network)
|
||||||
Log.i(TAG, "Network $network is available")
|
Log.d(TAG, "Network $network is available")
|
||||||
|
// If API >= 26, this will be followed by onCapabilitiesChanged
|
||||||
|
if (isServiceRunning && VERSION.SDK_INT < 26)
|
||||||
updateNetwork()
|
updateNetwork()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onLosing(network: Network, maxMsToLive: Int) {
|
||||||
|
super.onLosing(network, maxMsToLive)
|
||||||
|
Log.d(TAG, "Network $network is losing after $maxMsToLive ms")
|
||||||
|
}
|
||||||
|
|
||||||
override fun onLost(network: Network) {
|
override fun onLost(network: Network) {
|
||||||
super.onLost(network)
|
super.onLost(network)
|
||||||
Log.i(TAG, "Network $network is lost")
|
Log.d(TAG, "Network $network is lost")
|
||||||
if (activeNetwork == network)
|
if (isServiceRunning)
|
||||||
|
updateNetwork()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCapabilitiesChanged(network: Network, caps: NetworkCapabilities) {
|
||||||
|
super.onCapabilitiesChanged(network, caps)
|
||||||
|
Log.d(TAG, "Network $network capabilities changed: $caps")
|
||||||
|
if (isServiceRunning)
|
||||||
updateNetwork()
|
updateNetwork()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onLinkPropertiesChanged(network: Network, props: LinkProperties) {
|
override fun onLinkPropertiesChanged(network: Network, props: LinkProperties) {
|
||||||
super.onLinkPropertiesChanged(network, props)
|
super.onLinkPropertiesChanged(network, props)
|
||||||
Log.i(TAG, "Network $network link properties changed")
|
Log.d(TAG, "Network $network link properties changed: $props")
|
||||||
if (activeNetwork == network)
|
if (isServiceRunning)
|
||||||
updateNetwork()
|
updateNetwork()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,6 +143,55 @@ class BaresipService: Service() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
wm = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
|
wm = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
|
||||||
|
hotSpotIsEnabled = Utils.isHotSpotOn(wm)
|
||||||
|
|
||||||
|
hotSpotReceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(contxt: Context, intent: Intent) {
|
||||||
|
val action = intent.action
|
||||||
|
if ("android.net.wifi.WIFI_AP_STATE_CHANGED" == action) {
|
||||||
|
val state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 0)
|
||||||
|
if (WifiManager.WIFI_STATE_ENABLED == state % 10) {
|
||||||
|
if (hotSpotIsEnabled) {
|
||||||
|
Log.d(TAG, "HotSpot is still enabled")
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "HotSpot is enabled")
|
||||||
|
hotSpotIsEnabled = true
|
||||||
|
Timer().schedule(1000) {
|
||||||
|
hotSpotAddresses = Utils.hotSpotAddresses()
|
||||||
|
Log.d(TAG, "HotSpot addresses $hotSpotAddresses")
|
||||||
|
if (hotSpotAddresses.isNotEmpty()) {
|
||||||
|
for ((k, v) in hotSpotAddresses)
|
||||||
|
if (Api.net_add_address_ifname(k, v) != 0)
|
||||||
|
Log.e(TAG, "Failed to add $v address $k")
|
||||||
|
Timer().schedule(2000) {
|
||||||
|
Api.uag_reset_transp(register = true, reinvite = false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Could not get hotspot addresses")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!hotSpotIsEnabled) {
|
||||||
|
Log.d(TAG, "HotSpot is still disabled")
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "HotSpot is disabled")
|
||||||
|
hotSpotIsEnabled = false
|
||||||
|
if (hotSpotAddresses.isNotEmpty()) {
|
||||||
|
for ((k, _) in hotSpotAddresses)
|
||||||
|
if (Api.net_rm_address(k) != 0)
|
||||||
|
Log.e(TAG, "Failed to remove address $k")
|
||||||
|
hotSpotAddresses = mapOf()
|
||||||
|
Api.uag_reset_transp(register = true, reinvite = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.registerReceiver(hotSpotReceiver,
|
||||||
|
IntentFilter("android.net.wifi.WIFI_AP_STATE_CHANGED"))
|
||||||
|
|
||||||
tm = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
|
tm = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
|
||||||
|
|
||||||
@@ -133,7 +201,7 @@ class BaresipService: Service() {
|
|||||||
wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "Baresip")
|
wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "Baresip")
|
||||||
wifiLock.setReferenceCounted(false)
|
wifiLock.setReferenceCounted(false)
|
||||||
|
|
||||||
br = object : BroadcastReceiver() {
|
bluetoothReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(ctx: Context, intent: Intent) {
|
override fun onReceive(ctx: Context, intent: Intent) {
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED -> {
|
BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED -> {
|
||||||
@@ -206,7 +274,7 @@ class BaresipService: Service() {
|
|||||||
filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)
|
filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)
|
||||||
filter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)
|
filter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)
|
||||||
filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)
|
filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)
|
||||||
this.registerReceiver(br, filter)
|
this.registerReceiver(bluetoothReceiver, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
@@ -230,26 +298,12 @@ class BaresipService: Service() {
|
|||||||
|
|
||||||
"Start" -> {
|
"Start" -> {
|
||||||
|
|
||||||
var ipAddrs = ""
|
|
||||||
for (n in cm.allNetworks) {
|
|
||||||
val props = cm.getLinkProperties(n)
|
|
||||||
if (props != null) {
|
|
||||||
for (a in props.linkAddresses)
|
|
||||||
linkAddresses.add(a)
|
|
||||||
val addrs = Utils.hostAddresses(props.linkAddresses)
|
|
||||||
if (addrs != "") {
|
|
||||||
if (ipAddrs == "")
|
|
||||||
ipAddrs = addrs
|
|
||||||
else
|
|
||||||
ipAddrs += ";$addrs"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDnsServers()
|
updateDnsServers()
|
||||||
|
|
||||||
val assets = arrayOf("accounts", "config", "contacts", "busy.wav", "callwaiting.wav",
|
val assets = arrayOf(
|
||||||
"error.wav", "ringback.wav")
|
"accounts", "config", "contacts", "busy.wav", "callwaiting.wav",
|
||||||
|
"error.wav", "ringback.wav"
|
||||||
|
)
|
||||||
var file = File(filesPath)
|
var file = File(filesPath)
|
||||||
if (!file.exists()) {
|
if (!file.exists()) {
|
||||||
Log.d(TAG, "Creating baresip directory")
|
Log.d(TAG, "Creating baresip directory")
|
||||||
@@ -278,13 +332,15 @@ class BaresipService: Service() {
|
|||||||
CallHistory.restore()
|
CallHistory.restore()
|
||||||
Message.restore()
|
Message.restore()
|
||||||
|
|
||||||
if (ipAddrs == "")
|
linkAddresses = linkAddresses()
|
||||||
Log.w(TAG, "Starting baresip without IP addresses")
|
var addrs = ""
|
||||||
|
for (la in linkAddresses)
|
||||||
|
addrs = "$addrs;${la.key};${la.value}"
|
||||||
|
|
||||||
|
Log.d(TAG, "Link addresses: $addrs")
|
||||||
|
|
||||||
Thread {
|
Thread {
|
||||||
baresipStart(
|
baresipStart(filesPath, addrs.removePrefix(";"), logLevel)
|
||||||
filesPath, ipAddrs, "", Api.AF_UNSPEC, logLevel
|
|
||||||
)
|
|
||||||
}.start()
|
}.start()
|
||||||
|
|
||||||
isServiceRunning = true
|
isServiceRunning = true
|
||||||
@@ -293,11 +349,22 @@ class BaresipService: Service() {
|
|||||||
|
|
||||||
if (AccountsActivity.noAccounts()) {
|
if (AccountsActivity.noAccounts()) {
|
||||||
val newIntent = Intent(this, MainActivity::class.java)
|
val newIntent = Intent(this, MainActivity::class.java)
|
||||||
newIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP or
|
newIntent.flags =
|
||||||
|
Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP or
|
||||||
Intent.FLAG_ACTIVITY_NEW_TASK
|
Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
newIntent.putExtra("action", "accounts")
|
newIntent.putExtra("action", "accounts")
|
||||||
startActivity(newIntent)
|
startActivity(newIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (linkAddresses.isEmpty()) {
|
||||||
|
val newIntent = Intent(this, MainActivity::class.java)
|
||||||
|
newIntent.flags =
|
||||||
|
Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP or
|
||||||
|
Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
newIntent.putExtra("action", "no network")
|
||||||
|
startActivity(newIntent)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"Call Reject" -> {
|
"Call Reject" -> {
|
||||||
@@ -333,8 +400,10 @@ class BaresipService: Service() {
|
|||||||
if (ua == null)
|
if (ua == null)
|
||||||
Log.w(TAG, "onStartCommand did not find UA $uap")
|
Log.w(TAG, "onStartCommand did not find UA $uap")
|
||||||
else
|
else
|
||||||
ChatsActivity.saveUaMessage(ua.account.aor,
|
ChatsActivity.saveUaMessage(
|
||||||
intent.getStringExtra("time")!!.toLong())
|
ua.account.aor,
|
||||||
|
intent.getStringExtra("time")!!.toLong()
|
||||||
|
)
|
||||||
nm.cancel(MESSAGE_NOTIFICATION_ID)
|
nm.cancel(MESSAGE_NOTIFICATION_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,8 +413,10 @@ class BaresipService: Service() {
|
|||||||
if (ua == null)
|
if (ua == null)
|
||||||
Log.w(TAG, "onStartCommand did not find UA $uap")
|
Log.w(TAG, "onStartCommand did not find UA $uap")
|
||||||
else
|
else
|
||||||
ChatsActivity.deleteUaMessage(ua.account.aor,
|
ChatsActivity.deleteUaMessage(
|
||||||
intent.getStringExtra("time")!!.toLong())
|
ua.account.aor,
|
||||||
|
intent.getStringExtra("time")!!.toLong()
|
||||||
|
)
|
||||||
nm.cancel(MESSAGE_NOTIFICATION_ID)
|
nm.cancel(MESSAGE_NOTIFICATION_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,7 +447,8 @@ class BaresipService: Service() {
|
|||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
Log.d(TAG, "At Baresip Service onDestroy")
|
Log.d(TAG, "At Baresip Service onDestroy")
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
this.unregisterReceiver(br)
|
this.unregisterReceiver(bluetoothReceiver)
|
||||||
|
this.unregisterReceiver(hotSpotReceiver)
|
||||||
if (am.isBluetoothScoOn) am.stopBluetoothSco()
|
if (am.isBluetoothScoOn) am.stopBluetoothSco()
|
||||||
cleanService()
|
cleanService()
|
||||||
if (isServiceRunning) {
|
if (isServiceRunning) {
|
||||||
@@ -847,6 +919,7 @@ class BaresipService: Service() {
|
|||||||
@Keep
|
@Keep
|
||||||
fun started() {
|
fun started() {
|
||||||
Log.d(TAG, "Received 'started' from baresip")
|
Log.d(TAG, "Received 'started' from baresip")
|
||||||
|
Api.net_debug()
|
||||||
val intent = Intent("service event")
|
val intent = Intent("service event")
|
||||||
intent.putExtra("event", "started")
|
intent.putExtra("event", "started")
|
||||||
intent.putExtra("params", arrayListOf(callActionUri))
|
intent.putExtra("params", arrayListOf(callActionUri))
|
||||||
@@ -1084,56 +1157,87 @@ class BaresipService: Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateNetwork() {
|
private fun updateNetwork() {
|
||||||
|
|
||||||
|
if (!isServiceRunning)
|
||||||
|
return
|
||||||
|
|
||||||
/* for (n in cm.allNetworks)
|
/* for (n in cm.allNetworks)
|
||||||
Log.i(TAG, "NETWORK $n with caps ${cm.getNetworkCapabilities(n)} and props " +
|
Log.d(TAG, "NETWORK $n with caps ${cm.getNetworkCapabilities(n)} and props " +
|
||||||
"${cm.getLinkProperties(n)} is active ${isNetworkActive(n)}") */
|
"${cm.getLinkProperties(n)} is active ${isNetworkActive(n)}") */
|
||||||
activeNetwork = activeNetwork()
|
|
||||||
if (activeNetwork != null) {
|
updateDnsServers()
|
||||||
val linkCaps = cm.getNetworkCapabilities(activeNetwork)
|
|
||||||
val linkProps = cm.getLinkProperties(activeNetwork)
|
val lnAddrs = linkAddresses()
|
||||||
Log.d(TAG, "Using active network $activeNetwork with caps: $linkCaps, props: $linkProps")
|
|
||||||
val lnAddrs = mutableListOf<LinkAddress>()
|
Log.d(TAG, "Old/new link addresses $linkAddresses/$lnAddrs")
|
||||||
for (n in cm.allNetworks) {
|
|
||||||
val props = cm.getLinkProperties(n)
|
var added = 0
|
||||||
if (props != null)
|
|
||||||
for (a in props.linkAddresses)
|
|
||||||
lnAddrs.add(a)
|
|
||||||
}
|
|
||||||
var addrUpdate = false
|
|
||||||
for (a in lnAddrs)
|
for (a in lnAddrs)
|
||||||
if (!linkAddresses.contains(a)) {
|
if (!linkAddresses.containsKey(a.key)) {
|
||||||
Api.net_add_address(a.address.hostAddress)
|
if (Api.net_add_address_ifname(a.key, a.value) != 0)
|
||||||
addrUpdate = true
|
Log.e(TAG, "Failed to add address: $a")
|
||||||
|
else
|
||||||
|
added++
|
||||||
}
|
}
|
||||||
|
var removed = 0
|
||||||
for (a in linkAddresses)
|
for (a in linkAddresses)
|
||||||
if (!lnAddrs.contains(a)) {
|
if (!lnAddrs.containsKey(a.key)) {
|
||||||
Api.net_rm_address(a.address.hostAddress)
|
if (Api.net_rm_address(a.key) != 0)
|
||||||
addrUpdate = true
|
Log.e(TAG, "Failed to remove address: $a")
|
||||||
|
else
|
||||||
|
removed++
|
||||||
}
|
}
|
||||||
val dnsUpdate = updateDnsServers()
|
|
||||||
if (addrUpdate) {
|
val active = activeNetwork()
|
||||||
|
Log.d(TAG, "Added/Removed/Active = $added/$removed/$active")
|
||||||
|
|
||||||
|
if (added > 0 || removed > 0 || active != activeNetwork) {
|
||||||
linkAddresses = lnAddrs
|
linkAddresses = lnAddrs
|
||||||
|
activeNetwork = active
|
||||||
Api.uag_reset_transp(register = true, reinvite = true)
|
Api.uag_reset_transp(register = true, reinvite = true)
|
||||||
} else {
|
|
||||||
if (dnsUpdate)
|
|
||||||
UserAgent.register()
|
|
||||||
}
|
}
|
||||||
if (linkCaps != null && linkCaps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
|
|
||||||
|
Api.net_debug()
|
||||||
|
|
||||||
|
if (activeNetwork != null) {
|
||||||
|
val caps = cm.getNetworkCapabilities(activeNetwork)
|
||||||
|
if (caps != null && caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
|
||||||
Log.d(TAG, "Acquiring WiFi Lock")
|
Log.d(TAG, "Acquiring WiFi Lock")
|
||||||
wifiLock.acquire()
|
wifiLock.acquire()
|
||||||
} else {
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
Log.d(TAG, "Releasing WiFi Lock")
|
Log.d(TAG, "Releasing WiFi Lock")
|
||||||
wifiLock.release()
|
wifiLock.release()
|
||||||
}
|
}
|
||||||
} else
|
|
||||||
Log.w(TAG, "No active network")
|
private fun linkAddresses(): MutableMap<String, String> {
|
||||||
|
val lnAddrs = mutableMapOf<String, String>()
|
||||||
|
for (n in cm.allNetworks) {
|
||||||
|
val caps = cm.getNetworkCapabilities(n) ?: continue
|
||||||
|
if (caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND) ||
|
||||||
|
VERSION.SDK_INT < 28) {
|
||||||
|
val props = cm.getLinkProperties(n) ?: continue
|
||||||
|
for (la in props.linkAddresses)
|
||||||
|
if (la.scope == android.system.OsConstants.RT_SCOPE_UNIVERSE &&
|
||||||
|
props.interfaceName != null)
|
||||||
|
lnAddrs[la.address.hostAddress] = props.interfaceName!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hotSpotIsEnabled) {
|
||||||
|
hotSpotAddresses = Utils.hotSpotAddresses()
|
||||||
|
Log.d(TAG, "HotSpot addresses $hotSpotAddresses")
|
||||||
|
for ((k, v) in hotSpotAddresses)
|
||||||
|
lnAddrs[k] = v
|
||||||
|
}
|
||||||
|
return lnAddrs
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateDnsServers(): Boolean {
|
private fun updateDnsServers() {
|
||||||
if (isServiceRunning && !dynDns)
|
if (isServiceRunning && !dynDns)
|
||||||
return false
|
return
|
||||||
val servers = mutableListOf<InetAddress>()
|
val servers = mutableListOf<InetAddress>()
|
||||||
// Use DNS servers first from active network (if given)
|
// Use DNS servers first from active network (if available)
|
||||||
for (n in cm.allNetworks)
|
for (n in cm.allNetworks)
|
||||||
if (isNetworkActive(n)) {
|
if (isNetworkActive(n)) {
|
||||||
val linkProps = cm.getLinkProperties(n)
|
val linkProps = cm.getLinkProperties(n)
|
||||||
@@ -1157,10 +1261,8 @@ class BaresipService: Service() {
|
|||||||
} else {
|
} else {
|
||||||
// Log.d(TAG, "Updated DNS servers: '${servers}'")
|
// Log.d(TAG, "Updated DNS servers: '${servers}'")
|
||||||
dnsServers = servers
|
dnsServers = servers
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun activeNetwork(): Network? {
|
private fun activeNetwork(): Network? {
|
||||||
@@ -1200,8 +1302,7 @@ class BaresipService: Service() {
|
|||||||
isServiceClean = true
|
isServiceClean = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private external fun baresipStart(path: String, ipAddrs: String, netInterface: String,
|
private external fun baresipStart(path: String, addrs: String, logLevel: Int)
|
||||||
netAf: Int, logLevel: Int)
|
|
||||||
private external fun baresipStop(force: Boolean)
|
private external fun baresipStop(force: Boolean)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.tutpro.baresip
|
package com.tutpro.baresip
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
||||||
import java.net.InetAddress
|
import java.net.InetAddress
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import android.text.TextWatcher
|
|||||||
import android.view.*
|
import android.view.*
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
@@ -330,8 +332,12 @@ class MainActivity : AppCompatActivity() {
|
|||||||
} else {
|
} else {
|
||||||
val latest = CallHistory.aorLatestHistory(aor)
|
val latest = CallHistory.aorLatestHistory(aor)
|
||||||
if (latest != null)
|
if (latest != null)
|
||||||
callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(latest.peerUri),
|
callUri.setText(
|
||||||
Utils.aorDomain(ua.account.aor)))
|
Utils.friendlyUri(
|
||||||
|
ContactsActivity.contactName(latest.peerUri),
|
||||||
|
Utils.aorDomain(ua.account.aor)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -707,8 +713,14 @@ class MainActivity : AppCompatActivity() {
|
|||||||
"accounts" -> {
|
"accounts" -> {
|
||||||
resumeAction = "accounts"
|
resumeAction = "accounts"
|
||||||
}
|
}
|
||||||
|
"no network" -> {
|
||||||
|
Utils.alertView(this, getString(R.string.notice),
|
||||||
|
getString(R.string.no_network))
|
||||||
|
return
|
||||||
|
}
|
||||||
"call" -> {
|
"call" -> {
|
||||||
if (Call.calls().isNotEmpty()) {
|
if (Call.calls().isNotEmpty()) {
|
||||||
|
|
||||||
Toast.makeText(applicationContext, getString(R.string.call_already_active),
|
Toast.makeText(applicationContext, getString(R.string.call_already_active),
|
||||||
Toast.LENGTH_SHORT).show()
|
Toast.LENGTH_SHORT).show()
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import android.graphics.Bitmap
|
|||||||
import android.graphics.Bitmap.createScaledBitmap
|
import android.graphics.Bitmap.createScaledBitmap
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.net.*
|
import android.net.*
|
||||||
|
import android.net.wifi.WifiManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
@@ -24,20 +25,21 @@ import androidx.annotation.RequiresApi
|
|||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
|
||||||
import java.io.*
|
import java.io.*
|
||||||
|
import java.lang.reflect.Method
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.zip.ZipEntry
|
import java.util.zip.ZipEntry
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
import java.util.zip.ZipOutputStream
|
import java.util.zip.ZipOutputStream
|
||||||
|
import java.net.InetAddress
|
||||||
|
import java.net.NetworkInterface
|
||||||
|
import java.net.SocketException
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.SecretKeyFactory
|
import javax.crypto.SecretKeyFactory
|
||||||
import javax.crypto.spec.IvParameterSpec
|
import javax.crypto.spec.IvParameterSpec
|
||||||
import javax.crypto.spec.PBEKeySpec
|
import javax.crypto.spec.PBEKeySpec
|
||||||
import javax.crypto.spec.SecretKeySpec
|
import javax.crypto.spec.SecretKeySpec
|
||||||
|
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
object Utils {
|
object Utils {
|
||||||
@@ -106,10 +108,14 @@ object Utils {
|
|||||||
var u = uri
|
var u = uri
|
||||||
if (uri.startsWith("<") && (uri.endsWith(">")))
|
if (uri.startsWith("<") && (uri.endsWith(">")))
|
||||||
u = uri.substring(1).substringBeforeLast(">")
|
u = uri.substring(1).substringBeforeLast(">")
|
||||||
|
u = u.replace(":5060", "")
|
||||||
|
u = u.replace(";transport=udp", "", true)
|
||||||
|
if (u.split(":").size == 3 || uriParams(u).isNotEmpty())
|
||||||
|
return u
|
||||||
return if (u.contains("@")) {
|
return if (u.contains("@")) {
|
||||||
val user = uriUserPart(u)
|
val user = uriUserPart(u)
|
||||||
val host = uriHostPart(u)
|
val host = uriHostPart(u)
|
||||||
if (isE164Number(user) || (host == domain))
|
if (isE164Number(user) || host == domain)
|
||||||
user
|
user
|
||||||
else
|
else
|
||||||
"$user@$host"
|
"$user@$host"
|
||||||
@@ -277,17 +283,6 @@ object Utils {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hostAddresses(list: List<LinkAddress>?): String {
|
|
||||||
var result = ""
|
|
||||||
if (list != null) for (la in list)
|
|
||||||
if (la.scope == android.system.OsConstants.RT_SCOPE_UNIVERSE)
|
|
||||||
if (result == "")
|
|
||||||
result = la.address.hostAddress
|
|
||||||
else
|
|
||||||
result += ";" + la.address.hostAddress
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun implode(list: List<String>, sep: String): String {
|
fun implode(list: List<String>, sep: String): String {
|
||||||
var res = ""
|
var res = ""
|
||||||
for (s in list) {
|
for (s in list) {
|
||||||
@@ -305,6 +300,39 @@ object Utils {
|
|||||||
return appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
|
return appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isHotSpotOn(wm: WifiManager): Boolean {
|
||||||
|
try {
|
||||||
|
val method: Method = wm.javaClass.getDeclaredMethod("isWifiApEnabled")
|
||||||
|
method.isAccessible = true
|
||||||
|
return method.invoke(wm) as Boolean
|
||||||
|
} catch (ignored: Throwable) {
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hotSpotAddresses(): Map<String, String> {
|
||||||
|
val result = mutableMapOf<String, String>()
|
||||||
|
try {
|
||||||
|
val interfaces: Enumeration<NetworkInterface> = NetworkInterface.getNetworkInterfaces()
|
||||||
|
while (interfaces.hasMoreElements()) {
|
||||||
|
val iface: NetworkInterface = interfaces.nextElement()
|
||||||
|
val ifName = iface.name
|
||||||
|
if (ifName.startsWith("ap") || ifName.startsWith("wlan")) {
|
||||||
|
val addresses: Enumeration<InetAddress> = iface.inetAddresses
|
||||||
|
while (addresses.hasMoreElements()) {
|
||||||
|
val inetAddress: InetAddress = addresses.nextElement()
|
||||||
|
if (!inetAddress.isLoopbackAddress && !inetAddress.isLinkLocalAddress)
|
||||||
|
result[inetAddress.hostAddress] = ifName
|
||||||
|
}
|
||||||
|
if (result.isNotEmpty()) return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ex: SocketException) {
|
||||||
|
Log.e(TAG, ex.toString())
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
fun dtmfWatcher(callp: String): TextWatcher {
|
fun dtmfWatcher(callp: String): TextWatcher {
|
||||||
return object : TextWatcher {
|
return object : TextWatcher {
|
||||||
override fun beforeTextChanged(sequence: CharSequence, start: Int, count: Int, after: Int) {}
|
override fun beforeTextChanged(sequence: CharSequence, start: Int, count: Int, after: Int) {}
|
||||||
|
|||||||
@@ -495,4 +495,5 @@
|
|||||||
</string>
|
</string>
|
||||||
<string name="no_cameras">Sinulla ei ole yhtään tuettua video-kameraa.</string>
|
<string name="no_cameras">Sinulla ei ole yhtään tuettua video-kameraa.</string>
|
||||||
<string name="show_password">Näytä salasana</string>
|
<string name="show_password">Näytä salasana</string>
|
||||||
|
<string name="no_network">Ei verkkoyhteyttä!</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -448,4 +448,5 @@
|
|||||||
<string name="no_video_calls">Grant \"Camera\" permission to place or answer video calls.</string>
|
<string name="no_video_calls">Grant \"Camera\" permission to place or answer video calls.</string>
|
||||||
<string name="no_cameras">You don\'t have any supported video cameras.</string>
|
<string name="no_cameras">You don\'t have any supported video cameras.</string>
|
||||||
<string name="show_password">Show Password</string>
|
<string name="show_password">Show Password</string>
|
||||||
|
<string name="no_network">No network connection!</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user