Added limited support for multiple simultaneously active network interfaces

Show notification when baresip is started without network access
This commit is contained in:
Juha Heinanen
2021-10-05 13:52:49 +03:00
parent f042fb9a31
commit 1c3c07186e
9 changed files with 271 additions and 156 deletions

View File

@ -8,6 +8,7 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<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_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

View File

@ -5,19 +5,9 @@
#include <stdlib.h>
#include <re.h>
#include <baresip.h>
#include "logger.h"
#define LOGD(...) \
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__))
#define LOG_TAG "Baresip Lib"
typedef struct baresip_context {
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);
break;
case UA_EVENT_CALL_LOCAL_SDP:
if (strcmp(prm, "offer") == 0)
return;
len = re_snprintf(event_buf, sizeof event_buf, "call %sed", prm);
break;
case UA_EVENT_CALL_RINGING:
@ -436,23 +428,12 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
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
Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instance,
jstring jPath, jstring jIpAddrs, jstring jNetInterface, jint jNetAf, jint jLogLevel) {
Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instance, jstring jPath,
jstring jAddrs, jint jLogLevel) {
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] = "";
JavaVM *javaVM = g_ctx.javaVM;
@ -468,6 +449,7 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc
int err;
const char *path = (*env)->GetStringUTFChars(env, jPath, 0);
const char *addrs = (*env)->GetStringUTFChars(env, jAddrs, 0);
struct le *le;
runLoggingThread();
@ -487,15 +469,6 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc
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());
if (err) {
LOGW("baresip_init() failed (%d)\n", err);
@ -503,20 +476,21 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc
goto out;
}
if (strlen(ip_addrs) > 0) {
char* addr_list = (char*)malloc(strlen(ip_addrs));
if (strlen(addrs) > 0) {
char* addr_list = (char*)malloc(strlen(addrs));
struct sa temp_sa;
char buf[256];
net_flush_addresses(baresip_network());
strcpy(addr_list, ip_addrs);
strcpy(addr_list, addrs);
char *ptr = strtok(addr_list, ";");
while (ptr != NULL) {
LOGI("adding address '%s'", ptr);
if (0 == sa_set_str(&temp_sa, ptr, 0)) {
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 {
LOGE("invalid ip address %s\n", ptr);
ptr = strtok(NULL, ";");
res = EAFNOSUPPORT;
}
ptr = strtok(NULL, ";");
@ -524,7 +498,7 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc
free(addr_list);
}
net_debug_log();
// net_debug_log();
play_set_path(baresip_player(), path);
@ -572,7 +546,6 @@ Java_com_tutpro_baresip_BaresipService_baresipStart(JNIEnv *env, jobject instanc
(*env)->DeleteLocalRef(env, javaUA);
}
LOGI("allocating mqeue\n");
err = mqueue_alloc(&mq, mqueue_handler, NULL);
if (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
Java_com_tutpro_baresip_Api_net_1add_1address(JNIEnv *env, jobject thiz, jstring javaIp) {
const char *native_ip = (*env)->GetStringUTFChars(env, javaIp, 0);
Java_com_tutpro_baresip_Api_net_1add_1address_1ifname(JNIEnv *env, jobject thiz, jstring jAddr,
jstring jIfName) {
const char *addr = (*env)->GetStringUTFChars(env, jAddr, 0);
const char *name = (*env)->GetStringUTFChars(env, jIfName, 0);
int res = 0;
struct sa temp_sa;
char buf[256];
LOGI("adding address '%s'\n", native_ip);
if (str_len(native_ip) == 0) {
(*env)->ReleaseStringUTFChars(env, javaIp, native_ip);
return 0;
}
if (0 == sa_set_str(&temp_sa, native_ip, 0)) {
LOGD("adding address/ifname '%s/%s'\n", addr, name);
if (0 == sa_set_str(&temp_sa, addr, 0)) {
sa_ntop(&temp_sa, buf, 256);
re_thread_enter();
net_add_address(baresip_network(), &temp_sa);
res = net_add_address_ifname(baresip_network(), &temp_sa, name);
re_thread_leave();
} else {
LOGE("invalid ip address %s\n", native_ip);
LOGE("invalid ip address %s\n", addr);
res = EAFNOSUPPORT;
}
(*env)->ReleaseStringUTFChars(env, javaIp, native_ip);
(*env)->ReleaseStringUTFChars(env, jAddr, addr);
(*env)->ReleaseStringUTFChars(env, jIfName, name);
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)) {
sa_ntop(&temp_sa, buf, 256);
re_thread_enter();
net_rm_address(baresip_network(), &temp_sa);
res = net_rm_address(baresip_network(), &temp_sa);
re_thread_leave();
} else {
LOGE("invalid ip address %s\n", native_ip);

View File

@ -75,7 +75,7 @@ object Api {
external fun log_level_set(level: 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_debug()
external fun net_dns_debug()

View File

@ -19,6 +19,8 @@ import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.view.View
import android.widget.RemoteViews
import android.content.Intent
import android.content.BroadcastReceiver
import androidx.annotation.ColorRes
import androidx.annotation.Keep
import androidx.annotation.StringRes
@ -48,15 +50,18 @@ class BaresipService: Service() {
private lateinit var partialWakeLock: PowerManager.WakeLock
private lateinit var proximityWakeLock: PowerManager.WakeLock
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 audioFocusRequest: AudioFocusRequest? = null
private var audioFocusUsage = -1
private var origVolume = -1
private val btAdapter = BluetoothAdapter.getDefaultAdapter()
private var linkAddresses = mutableMapOf<String, String>()
private var activeNetwork: Network? = null
private var linkAddresses = mutableListOf<LinkAddress>()
private var hotSpotIsEnabled = false
private var hotSpotAddresses = mapOf<String, String>()
@SuppressLint("WakelockTimeout")
override fun onCreate() {
@ -102,21 +107,35 @@ class BaresipService: Service() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
Log.i(TAG, "Network $network is available")
updateNetwork()
Log.d(TAG, "Network $network is available")
// If API >= 26, this will be followed by onCapabilitiesChanged
if (isServiceRunning && VERSION.SDK_INT < 26)
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) {
super.onLost(network)
Log.i(TAG, "Network $network is lost")
if (activeNetwork == network)
Log.d(TAG, "Network $network is lost")
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()
}
override fun onLinkPropertiesChanged(network: Network, props: LinkProperties) {
super.onLinkPropertiesChanged(network, props)
Log.i(TAG, "Network $network link properties changed")
if (activeNetwork == network)
Log.d(TAG, "Network $network link properties changed: $props")
if (isServiceRunning)
updateNetwork()
}
@ -124,6 +143,55 @@ class BaresipService: Service() {
)
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
@ -133,7 +201,7 @@ class BaresipService: Service() {
wifiLock = wm.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "Baresip")
wifiLock.setReferenceCounted(false)
br = object : BroadcastReceiver() {
bluetoothReceiver = object : BroadcastReceiver() {
override fun onReceive(ctx: Context, intent: Intent) {
when (intent.action) {
BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED -> {
@ -206,7 +274,7 @@ class BaresipService: Service() {
filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)
filter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)
filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)
this.registerReceiver(br, filter)
this.registerReceiver(bluetoothReceiver, filter)
}
super.onCreate()
@ -230,26 +298,12 @@ class BaresipService: Service() {
"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()
val assets = arrayOf("accounts", "config", "contacts", "busy.wav", "callwaiting.wav",
"error.wav", "ringback.wav")
val assets = arrayOf(
"accounts", "config", "contacts", "busy.wav", "callwaiting.wav",
"error.wav", "ringback.wav"
)
var file = File(filesPath)
if (!file.exists()) {
Log.d(TAG, "Creating baresip directory")
@ -278,13 +332,15 @@ class BaresipService: Service() {
CallHistory.restore()
Message.restore()
if (ipAddrs == "")
Log.w(TAG, "Starting baresip without IP addresses")
linkAddresses = linkAddresses()
var addrs = ""
for (la in linkAddresses)
addrs = "$addrs;${la.key};${la.value}"
Log.d(TAG, "Link addresses: $addrs")
Thread {
baresipStart(
filesPath, ipAddrs, "", Api.AF_UNSPEC, logLevel
)
baresipStart(filesPath, addrs.removePrefix(";"), logLevel)
}.start()
isServiceRunning = true
@ -293,11 +349,22 @@ class BaresipService: Service() {
if (AccountsActivity.noAccounts()) {
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.flags =
Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP or
Intent.FLAG_ACTIVITY_NEW_TASK
newIntent.putExtra("action", "accounts")
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" -> {
@ -333,8 +400,10 @@ class BaresipService: Service() {
if (ua == null)
Log.w(TAG, "onStartCommand did not find UA $uap")
else
ChatsActivity.saveUaMessage(ua.account.aor,
intent.getStringExtra("time")!!.toLong())
ChatsActivity.saveUaMessage(
ua.account.aor,
intent.getStringExtra("time")!!.toLong()
)
nm.cancel(MESSAGE_NOTIFICATION_ID)
}
@ -344,8 +413,10 @@ class BaresipService: Service() {
if (ua == null)
Log.w(TAG, "onStartCommand did not find UA $uap")
else
ChatsActivity.deleteUaMessage(ua.account.aor,
intent.getStringExtra("time")!!.toLong())
ChatsActivity.deleteUaMessage(
ua.account.aor,
intent.getStringExtra("time")!!.toLong()
)
nm.cancel(MESSAGE_NOTIFICATION_ID)
}
@ -376,7 +447,8 @@ class BaresipService: Service() {
override fun onDestroy() {
Log.d(TAG, "At Baresip Service onDestroy")
super.onDestroy()
this.unregisterReceiver(br)
this.unregisterReceiver(bluetoothReceiver)
this.unregisterReceiver(hotSpotReceiver)
if (am.isBluetoothScoOn) am.stopBluetoothSco()
cleanService()
if (isServiceRunning) {
@ -847,6 +919,7 @@ class BaresipService: Service() {
@Keep
fun started() {
Log.d(TAG, "Received 'started' from baresip")
Api.net_debug()
val intent = Intent("service event")
intent.putExtra("event", "started")
intent.putExtra("params", arrayListOf(callActionUri))
@ -1084,56 +1157,87 @@ class BaresipService: Service() {
}
private fun updateNetwork() {
if (!isServiceRunning)
return
/* 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)}") */
activeNetwork = activeNetwork()
updateDnsServers()
val lnAddrs = linkAddresses()
Log.d(TAG, "Old/new link addresses $linkAddresses/$lnAddrs")
var added = 0
for (a in lnAddrs)
if (!linkAddresses.containsKey(a.key)) {
if (Api.net_add_address_ifname(a.key, a.value) != 0)
Log.e(TAG, "Failed to add address: $a")
else
added++
}
var removed = 0
for (a in linkAddresses)
if (!lnAddrs.containsKey(a.key)) {
if (Api.net_rm_address(a.key) != 0)
Log.e(TAG, "Failed to remove address: $a")
else
removed++
}
val active = activeNetwork()
Log.d(TAG, "Added/Removed/Active = $added/$removed/$active")
if (added > 0 || removed > 0 || active != activeNetwork) {
linkAddresses = lnAddrs
activeNetwork = active
Api.uag_reset_transp(register = true, reinvite = true)
}
Api.net_debug()
if (activeNetwork != null) {
val linkCaps = cm.getNetworkCapabilities(activeNetwork)
val linkProps = cm.getLinkProperties(activeNetwork)
Log.d(TAG, "Using active network $activeNetwork with caps: $linkCaps, props: $linkProps")
val lnAddrs = mutableListOf<LinkAddress>()
for (n in cm.allNetworks) {
val props = cm.getLinkProperties(n)
if (props != null)
for (a in props.linkAddresses)
lnAddrs.add(a)
}
var addrUpdate = false
for (a in lnAddrs)
if (!linkAddresses.contains(a)) {
Api.net_add_address(a.address.hostAddress)
addrUpdate = true
}
for (a in linkAddresses)
if (!lnAddrs.contains(a)) {
Api.net_rm_address(a.address.hostAddress)
addrUpdate = true
}
val dnsUpdate = updateDnsServers()
if (addrUpdate) {
linkAddresses = lnAddrs
Api.uag_reset_transp(register = true, reinvite = true)
} else {
if (dnsUpdate)
UserAgent.register()
}
if (linkCaps != null && linkCaps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
val caps = cm.getNetworkCapabilities(activeNetwork)
if (caps != null && caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
Log.d(TAG, "Acquiring WiFi Lock")
wifiLock.acquire()
} else {
Log.d(TAG, "Releasing WiFi Lock")
wifiLock.release()
return
}
} else
Log.w(TAG, "No active network")
}
Log.d(TAG, "Releasing WiFi Lock")
wifiLock.release()
}
private fun updateDnsServers(): Boolean {
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() {
if (isServiceRunning && !dynDns)
return false
return
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)
if (isNetworkActive(n)) {
val linkProps = cm.getLinkProperties(n)
@ -1157,10 +1261,8 @@ class BaresipService: Service() {
} else {
// Log.d(TAG, "Updated DNS servers: '${servers}'")
dnsServers = servers
return true
}
}
return false
}
private fun activeNetwork(): Network? {
@ -1200,8 +1302,7 @@ class BaresipService: Service() {
isServiceClean = true
}
private external fun baresipStart(path: String, ipAddrs: String, netInterface: String,
netAf: Int, logLevel: Int)
private external fun baresipStart(path: String, addrs: String, logLevel: Int)
private external fun baresipStop(force: Boolean)
companion object {

View File

@ -1,7 +1,6 @@
package com.tutpro.baresip
import android.content.Context
import java.net.InetAddress
import java.nio.charset.StandardCharsets

View File

@ -21,6 +21,8 @@ import android.text.TextWatcher
import android.view.*
import android.view.inputmethod.InputMethodManager
import android.widget.*
import android.content.Intent
import android.content.BroadcastReceiver
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
@ -330,8 +332,12 @@ class MainActivity : AppCompatActivity() {
} else {
val latest = CallHistory.aorLatestHistory(aor)
if (latest != null)
callUri.setText(Utils.friendlyUri(ContactsActivity.contactName(latest.peerUri),
Utils.aorDomain(ua.account.aor)))
callUri.setText(
Utils.friendlyUri(
ContactsActivity.contactName(latest.peerUri),
Utils.aorDomain(ua.account.aor)
)
)
}
}
}
@ -707,8 +713,14 @@ class MainActivity : AppCompatActivity() {
"accounts" -> {
resumeAction = "accounts"
}
"no network" -> {
Utils.alertView(this, getString(R.string.notice),
getString(R.string.no_network))
return
}
"call" -> {
if (Call.calls().isNotEmpty()) {
Toast.makeText(applicationContext, getString(R.string.call_already_active),
Toast.LENGTH_SHORT).show()
return

View File

@ -10,6 +10,7 @@ import android.graphics.Bitmap
import android.graphics.Bitmap.createScaledBitmap
import android.graphics.Color
import android.net.*
import android.net.wifi.WifiManager
import android.os.Bundle
import android.os.Environment
import android.provider.DocumentsContract
@ -24,20 +25,21 @@ import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import java.io.*
import java.lang.reflect.Method
import java.security.SecureRandom
import java.util.*
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream
import java.net.InetAddress
import java.net.NetworkInterface
import java.net.SocketException
import javax.crypto.Cipher
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.PBEKeySpec
import javax.crypto.spec.SecretKeySpec
import kotlin.collections.ArrayList
object Utils {
@ -106,10 +108,14 @@ object Utils {
var u = uri
if (uri.startsWith("<") && (uri.endsWith(">")))
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("@")) {
val user = uriUserPart(u)
val host = uriHostPart(u)
if (isE164Number(user) || (host == domain))
if (isE164Number(user) || host == domain)
user
else
"$user@$host"
@ -277,17 +283,6 @@ object Utils {
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 {
var res = ""
for (s in list) {
@ -305,6 +300,39 @@ object Utils {
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 {
return object : TextWatcher {
override fun beforeTextChanged(sequence: CharSequence, start: Int, count: Int, after: Int) {}

View File

@ -495,4 +495,5 @@
</string>
<string name="no_cameras">Sinulla ei ole yhtään tuettua video-kameraa.</string>
<string name="show_password">Näytä salasana</string>
<string name="no_network">Ei verkkoyhteyttä!</string>
</resources>

View File

@ -448,4 +448,5 @@
<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="show_password">Show Password</string>
<string name="no_network">No network connection!</string>
</resources>