Resolved merge conflict in BaresipService.kt
This commit is contained in:
@ -5,45 +5,46 @@ import java.net.URLDecoder
|
|||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
class Account(val accp: Long) {
|
class Account(val accp: Long, virtualAor: String? = null) {
|
||||||
|
|
||||||
|
var isMobile = false
|
||||||
var nickName = ""
|
var nickName = ""
|
||||||
var displayName = Api.account_display_name(accp)
|
var displayName = if (accp != 0L) Api.account_display_name(accp) else ""
|
||||||
val aor = Api.account_aor(accp)
|
val aor = if (accp != 0L) Api.account_aor(accp) else (virtualAor ?: "")
|
||||||
val luri = Api.account_luri(accp)
|
val luri = if (accp != 0L) Api.account_luri(accp) else (virtualAor ?: "")
|
||||||
var authUser = Api.account_auth_user(accp)
|
var authUser = if (accp != 0L) Api.account_auth_user(accp) else ""
|
||||||
var authPass = Api.account_auth_pass(accp)
|
var authPass = if (accp != 0L) Api.account_auth_pass(accp) else ""
|
||||||
var outbound = ArrayList<String>()
|
var outbound = ArrayList<String>()
|
||||||
var mediaNat = Api.account_medianat(accp)
|
var mediaNat = if (accp != 0L) Api.account_medianat(accp) else ""
|
||||||
var stunServer = Api.account_stun_uri(accp)
|
var stunServer = if (accp != 0L) Api.account_stun_uri(accp) else ""
|
||||||
var stunUser = Api.account_stun_user(accp)
|
var stunUser = if (accp != 0L) Api.account_stun_user(accp) else ""
|
||||||
var stunPass = Api.account_stun_pass(accp)
|
var stunPass = if (accp != 0L) Api.account_stun_pass(accp) else ""
|
||||||
var audioCodec = ArrayList<String>()
|
var audioCodec = ArrayList<String>()
|
||||||
var videoCodec = ArrayList<String>()
|
var videoCodec = ArrayList<String>()
|
||||||
var regint = Api.account_regint(accp)
|
var regint = if (accp != 0L) Api.account_regint(accp) else 0
|
||||||
var checkOrigin = Api.account_check_origin(accp)
|
var checkOrigin = if (accp != 0L) Api.account_check_origin(accp) else true
|
||||||
var configuredRegInt = REGISTRATION_INTERVAL
|
var configuredRegInt = REGISTRATION_INTERVAL
|
||||||
var mediaEnc = Api.account_mediaenc(accp)
|
var mediaEnc = if (accp != 0L) Api.account_mediaenc(accp) else ""
|
||||||
var rtcpMux = Api.account_rtcp_mux(accp)
|
var rtcpMux = if (accp != 0L) Api.account_rtcp_mux(accp) else false
|
||||||
var rel100Mode = Api.account_rel100_mode(accp)
|
var rel100Mode = if (accp != 0L) Api.account_rel100_mode(accp) else Api.REL100_DISABLED
|
||||||
var dtmfMode = Api.account_dtmfmode(accp)
|
var dtmfMode = if (accp != 0L) Api.account_dtmfmode(accp) else Api.DTMFMODE_AUTO
|
||||||
var answerMode = Api.account_answermode(accp)
|
var answerMode = if (accp != 0L) Api.account_answermode(accp) else Api.ANSWERMODE_MANUAL
|
||||||
var autoRedirect = Api.account_sip_autoredirect(accp)
|
var autoRedirect = if (accp != 0L) Api.account_sip_autoredirect(accp) else false
|
||||||
var blockUnknown = false
|
var blockUnknown = false
|
||||||
var vmUri = Api.account_vm_uri(accp)
|
var vmUri = if (accp != 0L) Api.account_vm_uri(accp) else ""
|
||||||
var vmNew = 0
|
var vmNew = 0
|
||||||
var vmOld = 0
|
var vmOld = 0
|
||||||
var missedCalls = false
|
var missedCalls = false
|
||||||
var unreadMessages = false
|
var unreadMessages = false
|
||||||
var callHistory = true
|
var callHistory = true
|
||||||
var countryCode = ""
|
var countryCode = ""
|
||||||
var telProvider = Utils.aorDomain(aor)
|
var telProvider = if (accp != 0L) Utils.aorDomain(aor) else ""
|
||||||
var resumeUri = ""
|
var resumeUri = ""
|
||||||
var numericKeypad = false
|
var numericKeypad = false
|
||||||
var customParams = ""
|
var customParams = ""
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
if (accp != 0L) {
|
||||||
if (authPass == "")
|
if (authPass == "")
|
||||||
authPass = NO_AUTH_PASS
|
authPass = NO_AUTH_PASS
|
||||||
|
|
||||||
@ -68,14 +69,16 @@ class Account(val accp: Long) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val extra = Api.account_extra(accp)
|
val extra = Api.account_extra(accp)
|
||||||
if (Utils.paramExists(extra, "nickname"))
|
if (Utils.paramExists(extra, "nickname"))
|
||||||
nickName = Utils.paramValue(extra, "nickname")
|
nickName = Utils.paramValue(extra, "nickname")
|
||||||
|
isMobile = Utils.paramExists(extra, "is_mobile")
|
||||||
if (Utils.paramExists(extra, "regint"))
|
if (Utils.paramExists(extra, "regint"))
|
||||||
configuredRegInt = Utils.paramValue(extra, "regint").toInt()
|
configuredRegInt = Utils.paramValue(extra, "regint").toInt()
|
||||||
callHistory = Utils.paramValue(extra, "call_history") == ""
|
callHistory = Utils.paramValue(extra, "call_history") == ""
|
||||||
blockUnknown= Utils.paramExists(extra, "block_unknown")
|
blockUnknown = Utils.paramExists(extra, "block_unknown")
|
||||||
if (Utils.paramExists(extra, "country_code"))
|
if (Utils.paramExists(extra, "country_code"))
|
||||||
countryCode = Utils.paramValue(extra, "country_code")
|
countryCode = Utils.paramValue(extra, "country_code")
|
||||||
if (Utils.paramExists(extra, "tel_provider"))
|
if (Utils.paramExists(extra, "tel_provider"))
|
||||||
@ -86,14 +89,19 @@ class Account(val accp: Long) {
|
|||||||
|
|
||||||
fun print() : String {
|
fun print() : String {
|
||||||
|
|
||||||
var res = if (displayName != "")
|
var res = if (isMobile) {
|
||||||
|
"<${aor};transport=udp>"
|
||||||
|
} else {
|
||||||
|
if (displayName != "")
|
||||||
"\"${displayName}\" "
|
"\"${displayName}\" "
|
||||||
else
|
else
|
||||||
""
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isMobile) {
|
||||||
res = "$res<$luri>"
|
res = "$res<$luri>"
|
||||||
|
|
||||||
if (authUser != "") res += ";auth_user=\"${authUser}\""
|
if (authUser != "") res += ";auth_user=\"${authUser}\""
|
||||||
|
}
|
||||||
|
|
||||||
if ((authPass != "") && !BaresipService.aorPasswords.containsKey(aor))
|
if ((authPass != "") && !BaresipService.aorPasswords.containsKey(aor))
|
||||||
res += ";auth_pass=\"${authPass}\""
|
res += ";auth_pass=\"${authPass}\""
|
||||||
@ -155,13 +163,19 @@ class Account(val accp: Long) {
|
|||||||
if (autoRedirect)
|
if (autoRedirect)
|
||||||
res += ";sip_autoredirect=yes"
|
res += ";sip_autoredirect=yes"
|
||||||
|
|
||||||
res += ";ptime=20;regint=${regint};regq=0.5;pubint=0;inreq_allowed=yes;call_transfer=yes"
|
res += ";ptime=20;regint=${regint};regq=0.5;pubint=0;inreq_allowed=yes"
|
||||||
|
|
||||||
|
if (isMobile)
|
||||||
|
res += ";call_transfer=no"
|
||||||
|
|
||||||
var extra = ""
|
var extra = ""
|
||||||
|
|
||||||
if (nickName != "")
|
if (nickName != "")
|
||||||
extra += ";nickname=${nickName}"
|
extra += ";nickname=${nickName}"
|
||||||
|
|
||||||
|
if (isMobile)
|
||||||
|
extra += ";is_mobile=yes"
|
||||||
|
|
||||||
if (!callHistory)
|
if (!callHistory)
|
||||||
extra += ";call_history=no"
|
extra += ";call_history=no"
|
||||||
|
|
||||||
@ -271,7 +285,7 @@ class Account(val accp: Long) {
|
|||||||
BaresipService.filesPath + "/accounts",
|
BaresipService.filesPath + "/accounts",
|
||||||
accounts.toByteArray(Charsets.UTF_8)
|
accounts.toByteArray(Charsets.UTF_8)
|
||||||
)
|
)
|
||||||
Log.d(TAG, "Saved accounts '${accounts}' to '${BaresipService.filesPath}/accounts'")
|
// Log.d(TAG, "Saved accounts '${accounts}' to '${BaresipService.filesPath}/accounts'")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ofAor(aor: String): Account? {
|
fun ofAor(aor: String): Account? {
|
||||||
|
|||||||
@ -232,14 +232,25 @@ private fun AccountContent(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.Start
|
horizontalArrangement = Arrangement.Start
|
||||||
) {
|
) {
|
||||||
|
val aorText = if (ua.account.isMobile) {
|
||||||
|
if (ua.account.aor == "sip:mobile@pstn")
|
||||||
|
stringResource(R.string.not_available)
|
||||||
|
else
|
||||||
|
ua.account.aor
|
||||||
|
} else
|
||||||
|
ua.account.luri
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = ua.account.luri,
|
value = aorText,
|
||||||
enabled = false,
|
enabled = false,
|
||||||
onValueChange = {},
|
onValueChange = {},
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
textStyle = TextStyle(fontSize = 18.sp),
|
textStyle = TextStyle(fontSize = 18.sp),
|
||||||
label = {
|
label = {
|
||||||
Text(text = stringResource(R.string.sip_uri), fontWeight = FontWeight.Bold)
|
Text(
|
||||||
|
text = stringResource(if (ua.account.isMobile) R.string.tel_uri else R.string.sip_uri),
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
},
|
},
|
||||||
colors = OutlinedTextFieldDefaults.colors(
|
colors = OutlinedTextFieldDefaults.colors(
|
||||||
disabledTextColor = MaterialTheme.colorScheme.onSurface,
|
disabledTextColor = MaterialTheme.colorScheme.onSurface,
|
||||||
@ -1056,7 +1067,10 @@ private fun AccountContent(
|
|||||||
@Composable
|
@Composable
|
||||||
fun Voicemail() {
|
fun Voicemail() {
|
||||||
val voicemailUriTitle = stringResource(R.string.voicemail_uri)
|
val voicemailUriTitle = stringResource(R.string.voicemail_uri)
|
||||||
val voicemailUriHelp = stringResource(R.string.voicemain_uri_help)
|
val voicemailUriHelp = if (ua.account.isMobile)
|
||||||
|
stringResource(R.string.voicemain_tel_uri_help)
|
||||||
|
else
|
||||||
|
stringResource(R.string.voicemain_uri_help)
|
||||||
val vmUri by viewModel.vmUri.collectAsState()
|
val vmUri by viewModel.vmUri.collectAsState()
|
||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth().padding(end = 10.dp),
|
Modifier.fillMaxWidth().padding(end = 10.dp),
|
||||||
@ -1247,6 +1261,7 @@ private fun AccountContent(
|
|||||||
) {
|
) {
|
||||||
AoR()
|
AoR()
|
||||||
Nickname()
|
Nickname()
|
||||||
|
if (!ua.account.isMobile) {
|
||||||
DisplayName()
|
DisplayName()
|
||||||
AuthUser()
|
AuthUser()
|
||||||
AuthPass()
|
AuthPass()
|
||||||
@ -1258,7 +1273,9 @@ private fun AccountContent(
|
|||||||
RegInt()
|
RegInt()
|
||||||
CheckOrigin()
|
CheckOrigin()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
BlockUnknown()
|
BlockUnknown()
|
||||||
|
if (!ua.account.isMobile) {
|
||||||
AudioCodecs(navController, aor)
|
AudioCodecs(navController, aor)
|
||||||
MediaEnc()
|
MediaEnc()
|
||||||
MediaNat()
|
MediaNat()
|
||||||
@ -1270,13 +1287,16 @@ private fun AccountContent(
|
|||||||
RtcpMux()
|
RtcpMux()
|
||||||
Rel100()
|
Rel100()
|
||||||
Dtmf()
|
Dtmf()
|
||||||
Answer()
|
|
||||||
Redirect()
|
Redirect()
|
||||||
|
Answer()
|
||||||
|
}
|
||||||
Voicemail()
|
Voicemail()
|
||||||
CountryCode()
|
CountryCode()
|
||||||
|
if (!ua.account.isMobile)
|
||||||
TelProvider()
|
TelProvider()
|
||||||
NumericKeypad()
|
NumericKeypad()
|
||||||
DefaultAccount()
|
DefaultAccount()
|
||||||
|
if (!ua.account.isMobile)
|
||||||
CustomParams()
|
CustomParams()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1598,8 +1618,13 @@ private fun checkOnClick(ctx: Context, viewModel: AccountViewModel, ua: UserAgen
|
|||||||
var newVmUri = viewModel.vmUri.value.trim()
|
var newVmUri = viewModel.vmUri.value.trim()
|
||||||
if (newVmUri != acc.vmUri) {
|
if (newVmUri != acc.vmUri) {
|
||||||
if (newVmUri != "") {
|
if (newVmUri != "") {
|
||||||
|
if (acc.isMobile) {
|
||||||
|
if (!newVmUri.startsWith("tel:")) newVmUri = "tel:$newVmUri"
|
||||||
|
}
|
||||||
|
else {
|
||||||
if (!newVmUri.startsWith("sip:")) newVmUri = "sip:$newVmUri"
|
if (!newVmUri.startsWith("sip:")) newVmUri = "sip:$newVmUri"
|
||||||
if (!newVmUri.contains("@")) newVmUri = "$newVmUri@${acc.host()}"
|
if (!newVmUri.contains("@")) newVmUri = "$newVmUri@${acc.host()}"
|
||||||
|
}
|
||||||
if (!Utils.checkUri(newVmUri)) {
|
if (!Utils.checkUri(newVmUri)) {
|
||||||
alertTitle.value = noticeTitle
|
alertTitle.value = noticeTitle
|
||||||
alertMessage.value = String.format(ctx.getString(R.string.invalid_sip_or_tel_uri),
|
alertMessage.value = String.format(ctx.getString(R.string.invalid_sip_or_tel_uri),
|
||||||
@ -1654,7 +1679,7 @@ private fun checkOnClick(ctx: Context, viewModel: AccountViewModel, ua: UserAgen
|
|||||||
|
|
||||||
if (viewModel.defaultAccount.value) ua.makeDefault()
|
if (viewModel.defaultAccount.value) ua.makeDefault()
|
||||||
|
|
||||||
Api.account_debug(acc.accp)
|
// Api.account_debug(acc.accp)
|
||||||
|
|
||||||
Account.saveAccounts()
|
Account.saveAccounts()
|
||||||
|
|
||||||
|
|||||||
@ -164,8 +164,6 @@ private var newOpusPacketLoss = oldOpusPacketLoss
|
|||||||
private var newAudioDelay = BaresipService.audioDelay.toString()
|
private var newAudioDelay = BaresipService.audioDelay.toString()
|
||||||
private var newToneCountry = BaresipService.toneCountry
|
private var newToneCountry = BaresipService.toneCountry
|
||||||
private var newRingtoneUri = ""
|
private var newRingtoneUri = ""
|
||||||
private var oldTelecom = BaresipService.telecom
|
|
||||||
private var newTelecom = oldTelecom
|
|
||||||
|
|
||||||
private var save = false
|
private var save = false
|
||||||
|
|
||||||
@ -178,8 +176,6 @@ private fun AudioContent(contentPadding: PaddingValues) {
|
|||||||
|
|
||||||
oldSpeakerPhone = Config.variable("speaker_phone") == "yes"
|
oldSpeakerPhone = Config.variable("speaker_phone") == "yes"
|
||||||
newSpeakerPhone = oldSpeakerPhone
|
newSpeakerPhone = oldSpeakerPhone
|
||||||
oldTelecom = Config.variable("telecom") == "yes"
|
|
||||||
newTelecom = oldTelecom
|
|
||||||
oldAudioModules = Config.variables("module")
|
oldAudioModules = Config.variables("module")
|
||||||
oldOpusBitrate = Config.variable("opus_bitrate")
|
oldOpusBitrate = Config.variable("opus_bitrate")
|
||||||
oldOpusPacketLoss = Config.variable("opus_packet_loss")
|
oldOpusPacketLoss = Config.variable("opus_packet_loss")
|
||||||
@ -206,7 +202,6 @@ private fun AudioContent(contentPadding: PaddingValues) {
|
|||||||
.verticalScroll(state = scrollState),
|
.verticalScroll(state = scrollState),
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
) {
|
) {
|
||||||
Telecom()
|
|
||||||
Ringtone()
|
Ringtone()
|
||||||
ToneCountry()
|
ToneCountry()
|
||||||
SpeakerPhone()
|
SpeakerPhone()
|
||||||
@ -219,37 +214,6 @@ private fun AudioContent(contentPadding: PaddingValues) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun Telecom() {
|
|
||||||
Row(
|
|
||||||
Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(end = 10.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.Start
|
|
||||||
) {
|
|
||||||
val telecomTitle = stringResource(R.string.telecom)
|
|
||||||
val telecomHelp = stringResource(R.string.telecom_help)
|
|
||||||
Text(text = telecomTitle,
|
|
||||||
modifier = Modifier
|
|
||||||
.weight(1f)
|
|
||||||
.clickable {
|
|
||||||
alertTitle.value = telecomTitle
|
|
||||||
alertMessage.value = telecomHelp
|
|
||||||
showAlert.value = true
|
|
||||||
},
|
|
||||||
fontSize = 18.sp)
|
|
||||||
var telecom by remember { mutableStateOf(oldTelecom) }
|
|
||||||
Switch(
|
|
||||||
checked = telecom,
|
|
||||||
onCheckedChange = {
|
|
||||||
telecom = it
|
|
||||||
newTelecom = telecom
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun Ringtone() {
|
private fun Ringtone() {
|
||||||
val ringToneTitle = stringResource(R.string.ringtone)
|
val ringToneTitle = stringResource(R.string.ringtone)
|
||||||
@ -629,13 +593,6 @@ private fun checkOnClick(ctx: Context): Result {
|
|||||||
|
|
||||||
var restart = false
|
var restart = false
|
||||||
|
|
||||||
if (newTelecom != oldTelecom) {
|
|
||||||
Config.replaceVariable("telecom", if (newTelecom) "yes" else "no")
|
|
||||||
BaresipService.telecom = newTelecom
|
|
||||||
restart = true
|
|
||||||
save = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Preferences(ctx).ringtoneUri != newRingtoneUri) {
|
if (Preferences(ctx).ringtoneUri != newRingtoneUri) {
|
||||||
Preferences(ctx).ringtoneUri = newRingtoneUri
|
Preferences(ctx).ringtoneUri = newRingtoneUri
|
||||||
BaresipService.rt = RingtoneManager.getRingtone(ctx, newRingtoneUri.toUri())
|
BaresipService.rt = RingtoneManager.getRingtone(ctx, newRingtoneUri.toUri())
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
package com.tutpro.baresip
|
package com.tutpro.baresip
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.Manifest.permission.RECORD_AUDIO
|
import android.Manifest.permission.RECORD_AUDIO
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
@ -61,7 +60,6 @@ import androidx.appcompat.app.AppCompatDelegate
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.core.app.ActivityCompat
|
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationCompat.MessagingStyle
|
import androidx.core.app.NotificationCompat.MessagingStyle
|
||||||
import androidx.core.app.Person
|
import androidx.core.app.Person
|
||||||
@ -70,6 +68,7 @@ import androidx.core.content.ContextCompat
|
|||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import com.tutpro.baresip.Utils.e164Uri
|
||||||
import com.tutpro.baresip.Utils.toCircle
|
import com.tutpro.baresip.Utils.toCircle
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@ -491,18 +490,7 @@ class BaresipService: Service() {
|
|||||||
activeNetwork = cm.activeNetwork
|
activeNetwork = cm.activeNetwork
|
||||||
Log.i(TAG, "Active network: $activeNetwork")
|
Log.i(TAG, "Active network: $activeNetwork")
|
||||||
|
|
||||||
if (telecom)
|
|
||||||
registerPhoneAccount()
|
registerPhoneAccount()
|
||||||
else
|
|
||||||
if (btAdapter != null) {
|
|
||||||
Log.i(TAG, "Registering bluetooth receiver")
|
|
||||||
val filter = IntentFilter()
|
|
||||||
filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)
|
|
||||||
filter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)
|
|
||||||
filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)
|
|
||||||
this.registerReceiver(bluetoothReceiver, filter)
|
|
||||||
bluetoothReceiverRegistered = true
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "AEC/AGC/NS available = $aecAvailable/$agcAvailable/$nsAvailable")
|
Log.i(TAG, "AEC/AGC/NS available = $aecAvailable/$agcAvailable/$nsAvailable")
|
||||||
|
|
||||||
@ -544,16 +532,14 @@ class BaresipService: Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
"Call Answer" -> {
|
"Call Answer" -> {
|
||||||
val uap = intent!!.getLongExtra("uap", 0L)
|
val callp = intent!!.getLongExtra("callp", 0L)
|
||||||
val callp = intent.getLongExtra("callp", 0L)
|
val call = Call.ofCallp(callp)
|
||||||
stopRinging()
|
stopRinging()
|
||||||
stopMediaPlayer()
|
stopMediaPlayer()
|
||||||
setCallVolume()
|
setCallVolume()
|
||||||
proximitySensing(proximitySensing)
|
proximitySensing(proximitySensing)
|
||||||
Api.ua_answer(uap, callp, Api.VIDMODE_OFF)
|
call?.answer()
|
||||||
updateStatusNotification()
|
updateStatusNotification()
|
||||||
if (!telecom)
|
|
||||||
ensureCommunicationMode()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"Call Reject" -> {
|
"Call Reject" -> {
|
||||||
@ -566,20 +552,15 @@ class BaresipService: Service() {
|
|||||||
val aor = call.ua.account.aor
|
val aor = call.ua.account.aor
|
||||||
Log.d(TAG, "Aor $aor rejected incoming call $callp from $peerUri")
|
Log.d(TAG, "Aor $aor rejected incoming call $callp from $peerUri")
|
||||||
call.rejected = true
|
call.rejected = true
|
||||||
Api.ua_hangup(call.ua.uap, callp, 486, "Rejected")
|
call.reject()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"Call Hangup" -> {
|
"Call Hangup" -> {
|
||||||
val callp = intent!!.getLongExtra("callp", 0L)
|
val callp = intent!!.getLongExtra("callp", 0L)
|
||||||
Log.d(TAG, "onStartCommand Hangup action for $callp")
|
Log.d(TAG, "onStartCommand Hangup action for $callp")
|
||||||
val connection = ConnectionService.connections[callp]
|
|
||||||
if (connection != null) {
|
|
||||||
connection.onDisconnect() // This calls Api.ua_hangup(..., 0, "")
|
|
||||||
} else {
|
|
||||||
val call = Call.ofCallp(callp)
|
val call = Call.ofCallp(callp)
|
||||||
if (call != null) Api.ua_hangup(call.ua.uap, callp, 0, "")
|
call?.hangup(0, "")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"Transfer Deny" -> {
|
"Transfer Deny" -> {
|
||||||
@ -731,7 +712,9 @@ class BaresipService: Service() {
|
|||||||
if (ev[0] == "create") {
|
if (ev[0] == "create") {
|
||||||
|
|
||||||
val ua = UserAgent(uap)
|
val ua = UserAgent(uap)
|
||||||
ua.status = if (ua.account.regint == 0)
|
ua.status = if (ua.account.isMobile)
|
||||||
|
R.drawable.circle_green
|
||||||
|
else if (ua.account.regint == 0)
|
||||||
R.drawable.circle_white
|
R.drawable.circle_white
|
||||||
else
|
else
|
||||||
circleYellow.getValue(colorblind)
|
circleYellow.getValue(colorblind)
|
||||||
@ -876,21 +859,15 @@ class BaresipService: Service() {
|
|||||||
break
|
break
|
||||||
speakerPhone = speakerPhoneAuto
|
speakerPhone = speakerPhoneAuto
|
||||||
stopMediaPlayer()
|
stopMediaPlayer()
|
||||||
val hasTelecom = ConnectionService.connections.containsKey(callp) ||
|
|
||||||
ConnectionService.pendingOutgoingConnection != null
|
|
||||||
if (!hasTelecom) {
|
|
||||||
setCallVolume()
|
setCallVolume()
|
||||||
ensureCommunicationMode()
|
ensureCommunicationMode()
|
||||||
}
|
|
||||||
proximitySensing(proximitySensing)
|
proximitySensing(proximitySensing)
|
||||||
}
|
}
|
||||||
"call ringing" -> {
|
"call ringing" -> {
|
||||||
if (!telecom) ensureCommunicationMode()
|
|
||||||
playRingBack()
|
playRingBack()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
"call progress" -> {
|
"call progress" -> {
|
||||||
if (!telecom) ensureCommunicationMode()
|
|
||||||
if ((ev[1].toInt() and Api.SDP_RECVONLY) != 0)
|
if ((ev[1].toInt() and Api.SDP_RECVONLY) != 0)
|
||||||
stopMediaPlayer()
|
stopMediaPlayer()
|
||||||
else {
|
else {
|
||||||
@ -934,7 +911,6 @@ class BaresipService: Service() {
|
|||||||
)
|
)
|
||||||
if (resourceId != 0) {
|
if (resourceId != 0) {
|
||||||
ensureCommunicationMode()
|
ensureCommunicationMode()
|
||||||
if (!telecom) playUnInterrupted(resourceId, 1)
|
|
||||||
} else {
|
} else {
|
||||||
Log.e(TAG, "Callwaiting tone $name.wav not found")
|
Log.e(TAG, "Callwaiting tone $name.wav not found")
|
||||||
}
|
}
|
||||||
@ -954,7 +930,6 @@ class BaresipService: Service() {
|
|||||||
Log.d(TAG, "Incoming call $uap/$callp/$peerUri")
|
Log.d(TAG, "Incoming call $uap/$callp/$peerUri")
|
||||||
if (Call.ofCallp(callp) == null)
|
if (Call.ofCallp(callp) == null)
|
||||||
Call(callp, ua, peerUri, "in", "incoming").add()
|
Call(callp, ua, peerUri, "in", "incoming").add()
|
||||||
if (telecom) {
|
|
||||||
val extras = android.os.Bundle()
|
val extras = android.os.Bundle()
|
||||||
extras.putLong("uap", uap)
|
extras.putLong("uap", uap)
|
||||||
extras.putLong("callp", callp)
|
extras.putLong("callp", callp)
|
||||||
@ -964,14 +939,6 @@ class BaresipService: Service() {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Telecom addNewIncomingCall failed: ${e.message}")
|
Log.e(TAG, "Telecom addNewIncomingCall failed: ${e.message}")
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (!requestAudioFocus(applicationContext)) {
|
|
||||||
Log.w(TAG, "Audio focus denied for incoming call")
|
|
||||||
Api.ua_hangup(uap, callp, 486, "Busy Here")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
handleIncomingCall(Call.ofCallp(callp)!!)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
"call answered" -> {
|
"call answered" -> {
|
||||||
@ -1031,12 +998,6 @@ class BaresipService: Service() {
|
|||||||
if (call.state() == Api.CALL_STATE_EARLY) {
|
if (call.state() == Api.CALL_STATE_EARLY) {
|
||||||
if ((ev[1].toInt() and Api.SDP_RECVONLY) != 0)
|
if ((ev[1].toInt() and Api.SDP_RECVONLY) != 0)
|
||||||
stopMediaPlayer()
|
stopMediaPlayer()
|
||||||
else {
|
|
||||||
if (!telecom) {
|
|
||||||
ConnectionService.connections[callp]?.setRinging()
|
|
||||||
playRingBack()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (call.status.value == "connected" && !call.held && !call.onhold) {
|
if (call.status.value == "connected" && !call.held && !call.onhold) {
|
||||||
if (call.callOnHold.value || call.showOnHoldNotice.value) {
|
if (call.callOnHold.value || call.showOnHoldNotice.value) {
|
||||||
@ -1111,11 +1072,8 @@ class BaresipService: Service() {
|
|||||||
if (call != null) {
|
if (call != null) {
|
||||||
call.terminated.value = true
|
call.terminated.value = true
|
||||||
call.remove()
|
call.remove()
|
||||||
if (!Call.inCall()) {
|
if (!Call.inCall())
|
||||||
proximitySensing(false)
|
proximitySensing(false)
|
||||||
if (!telecom)
|
|
||||||
abandonAudioFocus(applicationContext)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ConnectionService.lastDisconnectTime = System.currentTimeMillis()
|
ConnectionService.lastDisconnectTime = System.currentTimeMillis()
|
||||||
val connection = ConnectionService.connections[callp]
|
val connection = ConnectionService.connections[callp]
|
||||||
@ -1174,7 +1132,6 @@ class BaresipService: Service() {
|
|||||||
if (tone == "busy")
|
if (tone == "busy")
|
||||||
playBusy()
|
playBusy()
|
||||||
else
|
else
|
||||||
if (!Call.inCall())
|
|
||||||
ensureCommunicationMode()
|
ensureCommunicationMode()
|
||||||
if (call.dir == "out")
|
if (call.dir == "out")
|
||||||
call.rejected = call.startTime == null &&
|
call.rejected = call.startTime == null &&
|
||||||
@ -1473,9 +1430,7 @@ class BaresipService: Service() {
|
|||||||
}, audioDelay)
|
}, audioDelay)
|
||||||
}
|
}
|
||||||
|
|
||||||
val isTelecom = Call.calls().any { ConnectionService.connections.containsKey(it.callp) } ||
|
if (Call.hasTelecomCall())
|
||||||
ConnectionService.pendingOutgoingConnection != null
|
|
||||||
if (isTelecom)
|
|
||||||
executeCall()
|
executeCall()
|
||||||
else if (VERSION.SDK_INT < 31) {
|
else if (VERSION.SDK_INT < 31) {
|
||||||
Log.d(TAG, "Setting audio mode to MODE_IN_COMMUNICATION")
|
Log.d(TAG, "Setting audio mode to MODE_IN_COMMUNICATION")
|
||||||
@ -1511,6 +1466,7 @@ class BaresipService: Service() {
|
|||||||
fun started() {
|
fun started() {
|
||||||
Log.d(TAG, "Received 'started' from baresip")
|
Log.d(TAG, "Received 'started' from baresip")
|
||||||
isNativeReady = true
|
isNativeReady = true
|
||||||
|
addMobileUserAgent()
|
||||||
Api.net_debug()
|
Api.net_debug()
|
||||||
postServiceEvent(ServiceEvent("started", arrayListOf(callActionUri), System.nanoTime()))
|
postServiceEvent(ServiceEvent("started", arrayListOf(callActionUri), System.nanoTime()))
|
||||||
callActionUri = ""
|
callActionUri = ""
|
||||||
@ -1747,6 +1703,178 @@ class BaresipService: Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun handleExternalCall(telecomCall: android.telecom.Call, preferredAor: String? = null) {
|
||||||
|
val rawUri = telecomCall.details.handle?.toString() ?: "Unknown"
|
||||||
|
val uri = try {
|
||||||
|
java.net.URLDecoder.decode(rawUri, "UTF-8")
|
||||||
|
} catch (_: Exception) {
|
||||||
|
rawUri
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uas.value.isEmpty()) {
|
||||||
|
Log.e(TAG, "No User Agents available to handle external call")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val ua = preferredAor?.let { UserAgent.ofAor(it) }
|
||||||
|
?: uas.value.find { it.account.isMobile }
|
||||||
|
?: uas.value[0]
|
||||||
|
|
||||||
|
val telecomState = if (VERSION.SDK_INT >= 31)
|
||||||
|
telecomCall.details.state
|
||||||
|
else
|
||||||
|
@Suppress("DEPRECATION") telecomCall.state
|
||||||
|
|
||||||
|
val isIncoming = telecomState == android.telecom.Call.STATE_RINGING
|
||||||
|
Log.d(TAG, "Handling external call ${if (isIncoming) "from" else "to"} $uri (preferredAor=$preferredAor)")
|
||||||
|
|
||||||
|
val initialStatus = when (telecomState) {
|
||||||
|
android.telecom.Call.STATE_RINGING -> "incoming"
|
||||||
|
android.telecom.Call.STATE_DIALING, android.telecom.Call.STATE_CONNECTING -> "outgoing"
|
||||||
|
else -> "connected"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isIncoming) {
|
||||||
|
val e164Uri = e164Uri(uri, ua.account.countryCode)
|
||||||
|
if (ua.account.blockUnknown && Contact.contactName(e164Uri) == e164Uri) {
|
||||||
|
Log.d(TAG, "Auto-rejecting incoming PSTN call from $uri")
|
||||||
|
telecomCall.disconnect()
|
||||||
|
toast(String.format(getString(R.string.call_blocked),
|
||||||
|
Utils.friendlyUri(this, uri, ua.account)))
|
||||||
|
if (ua.account.callHistory) {
|
||||||
|
Blocked(
|
||||||
|
ua.account.aor,
|
||||||
|
uri,
|
||||||
|
"invite",
|
||||||
|
GregorianCalendar().timeInMillis
|
||||||
|
).add()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val call = Call.ExternalCall(
|
||||||
|
telecomCall,
|
||||||
|
ua,
|
||||||
|
uri,
|
||||||
|
if (telecomState == android.telecom.Call.STATE_RINGING) "in" else "out",
|
||||||
|
initialStatus
|
||||||
|
)
|
||||||
|
|
||||||
|
telecomCall.registerCallback(object : android.telecom.Call.Callback() {
|
||||||
|
override fun onStateChanged(call: android.telecom.Call, state: Int) {
|
||||||
|
super.onStateChanged(call, state)
|
||||||
|
val newStatus = when (state) {
|
||||||
|
android.telecom.Call.STATE_RINGING -> "incoming"
|
||||||
|
android.telecom.Call.STATE_DIALING, android.telecom.Call.STATE_CONNECTING -> "outgoing"
|
||||||
|
android.telecom.Call.STATE_ACTIVE -> "connected"
|
||||||
|
android.telecom.Call.STATE_DISCONNECTED, android.telecom.Call.STATE_DISCONNECTING -> "closed"
|
||||||
|
android.telecom.Call.STATE_HOLDING -> {
|
||||||
|
calls.find { it.callp == call.hashCode().toLong() }?.onhold = true
|
||||||
|
"connected"
|
||||||
|
}
|
||||||
|
else -> "connected"
|
||||||
|
}
|
||||||
|
calls.find { it.callp == call.hashCode().toLong() }?.let {
|
||||||
|
if (it.status.value != newStatus) {
|
||||||
|
it.status.value = newStatus
|
||||||
|
if (newStatus == "connected")
|
||||||
|
it.startTime = GregorianCalendar()
|
||||||
|
postServiceEvent(ServiceEvent(
|
||||||
|
"call update",
|
||||||
|
arrayListOf(it.ua.uap, it.callp),
|
||||||
|
System.nanoTime())
|
||||||
|
)
|
||||||
|
if (newStatus == "closed")
|
||||||
|
handleExternalCallRemoved(call)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
calls.add(call)
|
||||||
|
setCallVolume()
|
||||||
|
ensureCommunicationMode()
|
||||||
|
postServiceEvent(ServiceEvent(
|
||||||
|
if (isIncoming) "call incoming" else "call outgoing",
|
||||||
|
arrayListOf(ua.uap, call.callp),
|
||||||
|
System.nanoTime())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleExternalCallRemoved(telecomCall: android.telecom.Call) {
|
||||||
|
val callp = telecomCall.hashCode().toLong()
|
||||||
|
val call = calls.find { it.callp == callp }
|
||||||
|
if (call != null) {
|
||||||
|
if (call.ua.account.callHistory) {
|
||||||
|
val historyPeerUri = e164Uri(call.peerUri, call.ua.account.countryCode)
|
||||||
|
val history = CallHistoryNew(call.ua.account.aor, historyPeerUri, call.dir)
|
||||||
|
history.stopTime = GregorianCalendar()
|
||||||
|
history.startTime = call.startTime
|
||||||
|
history.rejected = call.rejected
|
||||||
|
history.add()
|
||||||
|
if (call.dir == "in" && call.startTime == null && !call.rejected)
|
||||||
|
call.ua.account.missedCalls = true
|
||||||
|
}
|
||||||
|
calls.remove(call)
|
||||||
|
}
|
||||||
|
if (!Call.inCall()) {
|
||||||
|
proximitySensing(false)
|
||||||
|
stopMediaPlayer()
|
||||||
|
}
|
||||||
|
messageUpdate.postValue(System.currentTimeMillis())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addMobileUserAgent() {
|
||||||
|
if (VERSION.SDK_INT < 29) return
|
||||||
|
|
||||||
|
val mobileAccountHandle = Utils.pstnAccountHandle(this)
|
||||||
|
val existingMobileUa = uas.value.find { it.account.isMobile }
|
||||||
|
|
||||||
|
// If mobile account should not exist (role lost or no SIM), remove it if it exists
|
||||||
|
if (mobileAccountHandle == null) {
|
||||||
|
if (existingMobileUa != null) {
|
||||||
|
Log.d(TAG, "Removing Mobile account (role lost or SIM missing)")
|
||||||
|
existingMobileUa.remove()
|
||||||
|
Account.saveAccounts()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val userPart = Utils.getLine1Number(this) ?: "mobile"
|
||||||
|
val mobileAor = "sip:$userPart@pstn"
|
||||||
|
|
||||||
|
if (existingMobileUa != null) {
|
||||||
|
// Update AOR if it previously was sip:mobile@pstn but now a real number
|
||||||
|
if (existingMobileUa.account.aor == "sip:mobile@pstn" && mobileAor != "sip:mobile@pstn") {
|
||||||
|
Log.d(TAG, "Updating existing Mobile account AOR to $mobileAor")
|
||||||
|
val aorField = Account::class.java.getDeclaredField("aor")
|
||||||
|
aorField.isAccessible = true
|
||||||
|
aorField.set(existingMobileUa.account, mobileAor)
|
||||||
|
val luriField = Account::class.java.getDeclaredField("luri")
|
||||||
|
luriField.isAccessible = true
|
||||||
|
luriField.set(existingMobileUa.account, mobileAor)
|
||||||
|
Account.saveAccounts()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Injecting new virtual Mobile account: $mobileAor")
|
||||||
|
val account = Account(0L, mobileAor)
|
||||||
|
account.isMobile = true
|
||||||
|
account.nickName = "Mobile"
|
||||||
|
account.regint = 0
|
||||||
|
account.telProvider = ""
|
||||||
|
val mobileUa = UserAgent(0L, account)
|
||||||
|
|
||||||
|
val updatedUas = uas.value.toMutableList()
|
||||||
|
updatedUas.add(mobileUa)
|
||||||
|
uas.value = updatedUas.toList()
|
||||||
|
uasStatus.value = UserAgent.statusMap()
|
||||||
|
|
||||||
|
Account.saveAccounts()
|
||||||
|
}
|
||||||
|
|
||||||
private fun toast(message: String, length: Int = Toast.LENGTH_SHORT) {
|
private fun toast(message: String, length: Int = Toast.LENGTH_SHORT) {
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
Toast.makeText(this@BaresipService.applicationContext, message, length).show()
|
Toast.makeText(this@BaresipService.applicationContext, message, length).show()
|
||||||
@ -2048,8 +2176,7 @@ class BaresipService: Service() {
|
|||||||
cleanupRunnable = null
|
cleanupRunnable = null
|
||||||
}
|
}
|
||||||
if (isSpeakerphoneOn == speakerPhone) {
|
if (isSpeakerphoneOn == speakerPhone) {
|
||||||
val hasTelecom = Call.calls().any { ConnectionService.connections.containsKey(it.callp) }
|
if (Call.hasTelecomCall() || currentMode == MODE_IN_COMMUNICATION) {
|
||||||
if (hasTelecom || currentMode == MODE_IN_COMMUNICATION) {
|
|
||||||
Log.d(TAG, "Already in valid call mode ($currentMode) with correct speaker state.")
|
Log.d(TAG, "Already in valid call mode ($currentMode) with correct speaker state.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2076,13 +2203,17 @@ class BaresipService: Service() {
|
|||||||
val runnable = Runnable {
|
val runnable = Runnable {
|
||||||
cleanupRunnable = null
|
cleanupRunnable = null
|
||||||
if (Call.inCall()) {
|
if (Call.inCall()) {
|
||||||
val hasTelecom = Call.calls().any { ConnectionService.connections.containsKey(it.callp) }
|
val hasTelecom = Call.hasTelecomCall()
|
||||||
if (!hasTelecom && am.mode != MODE_IN_COMMUNICATION && am.mode != AudioManager.MODE_IN_CALL) {
|
if (!hasTelecom && am.mode != MODE_IN_COMMUNICATION && am.mode != AudioManager.MODE_IN_CALL) {
|
||||||
am.mode = MODE_IN_COMMUNICATION
|
am.mode = MODE_IN_COMMUNICATION
|
||||||
Log.d(TAG, "Manual Mode Guard: Setting MODE_IN_COMMUNICATON from ${am.mode}")
|
Log.d(TAG, "Manual Mode Guard: Setting MODE_IN_COMMUNICATON from ${am.mode}")
|
||||||
}
|
}
|
||||||
Log.d(TAG, "Applying speakerphone state: $speakerPhone")
|
Log.d(TAG, "Applying speakerphone state: $speakerPhone")
|
||||||
if (!hasTelecom) {
|
if (InCallService.instance != null) {
|
||||||
|
Log.d(TAG, "Using InCallService for audio route: $speakerPhone")
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
InCallService.instance!!.setAudioRoute(if (speakerPhone) android.telecom.CallAudioState.ROUTE_SPEAKER else android.telecom.CallAudioState.ROUTE_EARPIECE)
|
||||||
|
} else if (!hasTelecom) {
|
||||||
Log.d(TAG, "No Telecom connection, using AudioManager for speaker")
|
Log.d(TAG, "No Telecom connection, using AudioManager for speaker")
|
||||||
Utils.setSpeakerPhone(mainExecutor, am, speakerPhone)
|
Utils.setSpeakerPhone(mainExecutor, am, speakerPhone)
|
||||||
} else {
|
} else {
|
||||||
@ -2117,7 +2248,7 @@ class BaresipService: Service() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (!Call.calls().any { ConnectionService.connections.containsKey(it.callp) })
|
if (!Call.hasTelecomCall())
|
||||||
resetCallVolume()
|
resetCallVolume()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2377,7 +2508,6 @@ class BaresipService: Service() {
|
|||||||
var isRecOn = false
|
var isRecOn = false
|
||||||
var toneCountry = "us"
|
var toneCountry = "us"
|
||||||
var proximitySensing = true
|
var proximitySensing = true
|
||||||
var telecom = true
|
|
||||||
|
|
||||||
val uas = mutableStateOf(emptyList<UserAgent>())
|
val uas = mutableStateOf(emptyList<UserAgent>())
|
||||||
val uasStatus = mutableStateOf(emptyMap<String, Int>())
|
val uasStatus = mutableStateOf(emptyMap<String, Int>())
|
||||||
@ -2471,8 +2601,6 @@ class BaresipService: Service() {
|
|||||||
) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
|
) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
|
||||||
) {
|
) {
|
||||||
Log.d(TAG, "requestAudioFocus granted")
|
Log.d(TAG, "requestAudioFocus granted")
|
||||||
if (!telecom && isBluetoothHeadsetConnected(ctx))
|
|
||||||
startBluetoothSco(ctx, 250L, 3)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Log.w(TAG, "requestAudioFocus denied")
|
Log.w(TAG, "requestAudioFocus denied")
|
||||||
@ -2481,39 +2609,6 @@ class BaresipService: Service() {
|
|||||||
return audioFocusRequest != null
|
return audioFocusRequest != null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun abandonAudioFocus(ctx: Context) {
|
|
||||||
val am = ctx.getSystemService(AUDIO_SERVICE) as AudioManager
|
|
||||||
if (audioFocusRequest != null) {
|
|
||||||
Log.d(TAG, "Abandoning audio focus")
|
|
||||||
if (androidx.media.AudioManagerCompat.abandonAudioFocusRequest(
|
|
||||||
am,
|
|
||||||
audioFocusRequest!!
|
|
||||||
) ==
|
|
||||||
AudioManager.AUDIOFOCUS_REQUEST_GRANTED
|
|
||||||
) {
|
|
||||||
audioFocusRequest = null
|
|
||||||
if (!telecom && isBluetoothHeadsetConnected(ctx))
|
|
||||||
stopBluetoothSco(ctx)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Log.e(TAG, "Failed to abandon audio focus")
|
|
||||||
}
|
|
||||||
am.mode = MODE_NORMAL
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isBluetoothHeadsetConnected(ctx: Context): Boolean {
|
|
||||||
if (VERSION.SDK_INT >= 31 &&
|
|
||||||
ActivityCompat.checkSelfPermission(
|
|
||||||
ctx,
|
|
||||||
Manifest.permission.BLUETOOTH_CONNECT
|
|
||||||
) == PackageManager.PERMISSION_DENIED
|
|
||||||
)
|
|
||||||
return false
|
|
||||||
return btAdapter != null && btAdapter!!.isEnabled &&
|
|
||||||
btAdapter!!.getProfileConnectionState(BluetoothHeadset.HEADSET) ==
|
|
||||||
BluetoothAdapter.STATE_CONNECTED
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isBluetoothScoOn(am: AudioManager): Boolean {
|
private fun isBluetoothScoOn(am: AudioManager): Boolean {
|
||||||
return if (VERSION.SDK_INT < 31)
|
return if (VERSION.SDK_INT < 31)
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: String, initialStatus: String) {
|
open class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: String, initialStatus: String) {
|
||||||
|
|
||||||
var status: MutableState<String> = mutableStateOf(initialStatus)
|
var status: MutableState<String> = mutableStateOf(initialStatus)
|
||||||
|
|
||||||
@ -55,11 +55,11 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
|
|||||||
BaresipService.calls.remove(this)
|
BaresipService.calls.remove(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun connect(uri: String): Boolean {
|
open fun connect(uri: String): Boolean {
|
||||||
return Api.call_connect(callp, uri) == 0
|
return Api.call_connect(callp, uri) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hold(): Boolean {
|
open fun hold(): Boolean {
|
||||||
if (onhold) return true
|
if (onhold) return true
|
||||||
if (Api.call_hold(callp, true)) {
|
if (Api.call_hold(callp, true)) {
|
||||||
onhold = true
|
onhold = true
|
||||||
@ -71,7 +71,7 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resume(): Boolean {
|
open fun resume(): Boolean {
|
||||||
if (!onhold && !held) return true
|
if (!onhold && !held) return true
|
||||||
// 1. Hold other calls first
|
// 1. Hold other calls first
|
||||||
for (c in BaresipService.calls) {
|
for (c in BaresipService.calls) {
|
||||||
@ -94,13 +94,13 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun transfer(uri: String): Boolean {
|
open fun transfer(uri: String): Boolean {
|
||||||
if (!onhold) hold()
|
if (!onhold) hold()
|
||||||
Log.d(TAG, "Transferring call $callp to $uri")
|
Log.d(TAG, "Transferring call $callp to $uri")
|
||||||
return Api.call_transfer(callp, uri) == 0
|
return Api.call_transfer(callp, uri) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fun executeTransfer(): Boolean {
|
open fun executeTransfer(): Boolean {
|
||||||
return if (onHoldCall != null) {
|
return if (onHoldCall != null) {
|
||||||
if (Api.call_hold(callp, true))
|
if (Api.call_hold(callp, true))
|
||||||
Api.call_replace_transfer(onHoldCall!!.callp, callp)
|
Api.call_replace_transfer(onHoldCall!!.callp, callp)
|
||||||
@ -110,31 +110,31 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sendDigit(digit: Char): Int {
|
open fun sendDigit(digit: Char): Int {
|
||||||
return Api.call_send_digit(callp, digit)
|
return Api.call_send_digit(callp, digit)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun notifySipfrag(code: Int, reason: String) {
|
open fun notifySipfrag(code: Int, reason: String) {
|
||||||
Api.call_notify_sipfrag(callp, code, reason)
|
Api.call_notify_sipfrag(callp, code, reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun duration(): Int {
|
open fun duration(): Int {
|
||||||
return Api.call_duration(callp)
|
return Api.call_duration(callp)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun stats(stream: String): String {
|
open fun stats(stream: String): String {
|
||||||
return Api.call_stats(callp, stream)
|
return Api.call_stats(callp, stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun state(): Int {
|
open fun state(): Int {
|
||||||
return Api.call_state(callp)
|
return Api.call_state(callp)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun audioCodecs(): String {
|
open fun audioCodecs(): String {
|
||||||
return Api.call_audio_codecs(callp)
|
return Api.call_audio_codecs(callp)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun replaces(): Boolean {
|
open fun replaces(): Boolean {
|
||||||
return Api.call_replaces(callp)
|
return Api.call_replaces(callp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,10 +146,81 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
|
|||||||
if (ua.account.mediaEnc != "") security = R.color.colorTrafficRed
|
if (ua.account.mediaEnc != "") security = R.color.colorTrafficRed
|
||||||
}
|
}
|
||||||
|
|
||||||
fun destroy() {
|
open fun destroy() {
|
||||||
Api.call_destroy(callp)
|
Api.call_destroy(callp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun hangup(code: Int, reason: String) {
|
||||||
|
val connection = ConnectionService.connections[callp]
|
||||||
|
if (connection != null)
|
||||||
|
connection.onDisconnect()
|
||||||
|
else
|
||||||
|
Api.ua_hangup(ua.uap, callp, code, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun answer() {
|
||||||
|
Api.ua_answer(ua.uap, callp, Api.VIDMODE_OFF)
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun reject() {
|
||||||
|
hangup(486, "Busy Here")
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExternalCall(
|
||||||
|
val telecomCall: android.telecom.Call,
|
||||||
|
ua: UserAgent,
|
||||||
|
peerUri: String,
|
||||||
|
dir: String,
|
||||||
|
initialStatus: String
|
||||||
|
) : Call(telecomCall.hashCode().toLong(), ua, peerUri, dir, initialStatus) {
|
||||||
|
|
||||||
|
override fun connect(uri: String): Boolean {
|
||||||
|
telecomCall.answer(android.telecom.VideoProfile.STATE_AUDIO_ONLY)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun answer() {
|
||||||
|
telecomCall.answer(android.telecom.VideoProfile.STATE_AUDIO_ONLY)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hold(): Boolean {
|
||||||
|
telecomCall.hold()
|
||||||
|
onhold = true
|
||||||
|
callOnHold.value = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resume(): Boolean {
|
||||||
|
telecomCall.unhold()
|
||||||
|
onhold = false
|
||||||
|
callOnHold.value = false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hangup(code: Int, reason: String) {
|
||||||
|
telecomCall.disconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reject() {
|
||||||
|
telecomCall.disconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun destroy() {
|
||||||
|
telecomCall.disconnect()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sendDigit(digit: Char): Int {
|
||||||
|
telecomCall.playDtmfTone(digit)
|
||||||
|
telecomCall.stopDtmfTone()
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun duration(): Int = 0
|
||||||
|
override fun stats(stream: String): String = ""
|
||||||
|
override fun state(): Int = 0
|
||||||
|
override fun audioCodecs(): String = "PSTN"
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun calls(): ArrayList<Call> {
|
fun calls(): ArrayList<Call> {
|
||||||
@ -172,6 +243,12 @@ class Call(val callp: Long, val ua: UserAgent, val peerUri: String, val dir: Str
|
|||||||
return BaresipService.calls.isNotEmpty()
|
return BaresipService.calls.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hasTelecomCall(): Boolean {
|
||||||
|
return BaresipService.calls.any {
|
||||||
|
it is ExternalCall || ConnectionService.connections.containsKey(it.callp)
|
||||||
|
} || ConnectionService.pendingOutgoingConnection != null
|
||||||
|
}
|
||||||
|
|
||||||
fun isAnyCallActive(ctx: Context): Boolean {
|
fun isAnyCallActive(ctx: Context): Boolean {
|
||||||
// Check if there exist SIP calls that are not onhold or held
|
// Check if there exist SIP calls that are not onhold or held
|
||||||
if (BaresipService.calls.any { !it.onhold && !it.held }) return true
|
if (BaresipService.calls.any { !it.onhold && !it.held }) return true
|
||||||
|
|||||||
@ -226,13 +226,6 @@ object Config {
|
|||||||
BaresipService.toneCountry = toneCountry
|
BaresipService.toneCountry = toneCountry
|
||||||
config = "${config}tone_country ${BaresipService.toneCountry}\n"
|
config = "${config}tone_country ${BaresipService.toneCountry}\n"
|
||||||
|
|
||||||
val telecom = previousVariable("telecom")
|
|
||||||
if (telecom != "")
|
|
||||||
BaresipService.telecom = telecom == "yes"
|
|
||||||
else
|
|
||||||
BaresipService.telecom = true
|
|
||||||
config = "${config}telecom ${if (BaresipService.telecom) "yes" else "no"}\n"
|
|
||||||
|
|
||||||
save()
|
save()
|
||||||
BaresipService.isConfigInitialized = true
|
BaresipService.isConfigInitialized = true
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.tutpro.baresip
|
package com.tutpro.baresip
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.telecom.CallAudioState
|
import android.telecom.CallAudioState
|
||||||
import android.telecom.Connection
|
import android.telecom.Connection
|
||||||
import android.telecom.ConnectionRequest
|
import android.telecom.ConnectionRequest
|
||||||
@ -8,7 +9,6 @@ import android.telecom.ConnectionService
|
|||||||
import android.telecom.DisconnectCause
|
import android.telecom.DisconnectCause
|
||||||
import android.telecom.PhoneAccountHandle
|
import android.telecom.PhoneAccountHandle
|
||||||
import android.telecom.TelecomManager
|
import android.telecom.TelecomManager
|
||||||
import android.net.Uri
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
class ConnectionService : ConnectionService() {
|
class ConnectionService : ConnectionService() {
|
||||||
@ -64,7 +64,8 @@ class ConnectionService : ConnectionService() {
|
|||||||
|
|
||||||
val connection = BaresipConnection(uap, callp)
|
val connection = BaresipConnection(uap, callp)
|
||||||
connections[callp] = connection
|
connections[callp] = connection
|
||||||
connection.setAddress(Uri.fromParts("sip", peerUri, null), TelecomManager.PRESENTATION_ALLOWED)
|
connection.setAddress(Uri.fromParts("sip", peerUri, null),
|
||||||
|
TelecomManager.PRESENTATION_ALLOWED)
|
||||||
connection.connectionCapabilities = Connection.CAPABILITY_SUPPORT_HOLD or
|
connection.connectionCapabilities = Connection.CAPABILITY_SUPPORT_HOLD or
|
||||||
Connection.CAPABILITY_HOLD or
|
Connection.CAPABILITY_HOLD or
|
||||||
Connection.CAPABILITY_MERGE_CONFERENCE or
|
Connection.CAPABILITY_MERGE_CONFERENCE or
|
||||||
@ -108,6 +109,9 @@ class ConnectionService : ConnectionService() {
|
|||||||
val conferenceCall = rootExtras?.getBoolean("conferenceCall", false) ?:
|
val conferenceCall = rootExtras?.getBoolean("conferenceCall", false) ?:
|
||||||
nestedExtras?.getBoolean("conferenceCall") ?: false
|
nestedExtras?.getBoolean("conferenceCall") ?: false
|
||||||
|
|
||||||
|
val pstnCall = rootExtras?.getBoolean("pstnCall", false) ?:
|
||||||
|
nestedExtras?.getBoolean("pstnCall") ?: false
|
||||||
|
|
||||||
val onHoldCallp = rootExtras?.getLong("onHoldCallp", 0L).takeIf { it != 0L }
|
val onHoldCallp = rootExtras?.getLong("onHoldCallp", 0L).takeIf { it != 0L }
|
||||||
?: nestedExtras?.getLong("onHoldCallp") ?: 0L
|
?: nestedExtras?.getLong("onHoldCallp") ?: 0L
|
||||||
|
|
||||||
@ -123,20 +127,15 @@ class ConnectionService : ConnectionService() {
|
|||||||
Connection.CAPABILITY_HOLD or
|
Connection.CAPABILITY_HOLD or
|
||||||
Connection.CAPABILITY_MERGE_CONFERENCE or
|
Connection.CAPABILITY_MERGE_CONFERENCE or
|
||||||
Connection.CAPABILITY_SWAP_CONFERENCE
|
Connection.CAPABILITY_SWAP_CONFERENCE
|
||||||
connection.audioModeIsVoip = true
|
|
||||||
|
|
||||||
// Start the SIP connection logic
|
if (!pstnCall) {
|
||||||
if (uap != 0L) {
|
connection.audioModeIsVoip = true
|
||||||
val sipUri = if (destination.startsWith("sip:")) destination else "sip:$destination"
|
val sipUri = if (destination.startsWith("sip:")) destination else "sip:$destination"
|
||||||
BaresipService.instance?.runCall(uap, sipUri, conferenceCall, onHoldCallp)
|
BaresipService.instance?.runCall(uap, sipUri, conferenceCall, onHoldCallp)
|
||||||
} else {
|
|
||||||
Log.e(TAG, "Cannot start outgoing call: uap is 0")
|
|
||||||
connection.setDisconnected(DisconnectCause(DisconnectCause.ERROR, "No Account"))
|
|
||||||
connection.destroy()
|
|
||||||
pendingOutgoingConnection = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.setDialing()
|
connection.setDialing()
|
||||||
|
|
||||||
return connection
|
return connection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,24 +1,53 @@
|
|||||||
package com.tutpro.baresip
|
package com.tutpro.baresip
|
||||||
|
|
||||||
import android.app.Service
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
|
|
||||||
// This is needed in order to allow choosing baresip as default Phone app
|
|
||||||
class InCallService : Service() {
|
|
||||||
override fun onBind(intent: Intent): IBinder? {
|
|
||||||
Log.d(TAG, "InCallService onBind with intent: ${intent.action}")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*import android.telecom.InCallService
|
|
||||||
import android.telecom.Call
|
import android.telecom.Call
|
||||||
|
import android.telecom.InCallService
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
class InCallService : InCallService() {
|
class InCallService : InCallService() {
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent): IBinder? {
|
||||||
|
instance = this
|
||||||
|
return super.onBind(intent)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCallAdded(call: Call) {
|
override fun onCallAdded(call: Call) {
|
||||||
super.onCallAdded(call)
|
super.onCallAdded(call)
|
||||||
// This is triggered when the system wants YOU to show the call UI
|
Log.d(TAG, "InCallService: Call added")
|
||||||
Log.d("Baresip", "InCallService: Call added")
|
|
||||||
|
val handle = call.details.accountHandle
|
||||||
|
val baresipHandle = BaresipService.getPhoneAccountHandle(this)
|
||||||
|
|
||||||
|
if (handle == baresipHandle) {
|
||||||
|
Log.d(TAG, "InCallService: Identified as SIP call")
|
||||||
|
// SIP call is already managed by ConnectionService/BaresipService
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "InCallService: Identified as PSTN call from $handle")
|
||||||
|
val aor = call.details.intentExtras?.getString("aor")
|
||||||
|
BaresipService.instance?.handleExternalCall(call, aor)
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
|
override fun onCallRemoved(call: Call) {
|
||||||
|
super.onCallRemoved(call)
|
||||||
|
Log.d(TAG, "InCallService: Call removed")
|
||||||
|
BaresipService.instance?.handleExternalCallRemoved(call)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUnbind(intent: Intent?): Boolean {
|
||||||
|
instance = null
|
||||||
|
return super.onUnbind(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "Baresip"
|
||||||
|
private var _instance = WeakReference<InCallService>(null)
|
||||||
|
var instance: InCallService?
|
||||||
|
get() = _instance.get()
|
||||||
|
set(value) {
|
||||||
|
_instance = WeakReference(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -12,12 +12,15 @@ import android.content.res.Configuration
|
|||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build.VERSION
|
import android.os.Build.VERSION
|
||||||
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.os.Process
|
import android.os.Process
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
|
import android.telecom.TelecomManager
|
||||||
|
import android.view.WindowManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
@ -237,6 +240,12 @@ private fun MainScreen(
|
|||||||
BaresipService.isMainVisible = true
|
BaresipService.isMainVisible = true
|
||||||
viewModel.updateSpeakerPhoneStatus(BaresipService.speakerPhone)
|
viewModel.updateSpeakerPhoneStatus(BaresipService.speakerPhone)
|
||||||
viewModel.updateCalls(Call.calls().toList())
|
viewModel.updateCalls(Call.calls().toList())
|
||||||
|
|
||||||
|
if (Call.inCall())
|
||||||
|
(ctx as? Activity)?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
else
|
||||||
|
(ctx as? Activity)?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
|
||||||
(Call.call("incoming") ?: Call.calls().lastOrNull())?.let {
|
(Call.call("incoming") ?: Call.calls().lastOrNull())?.let {
|
||||||
spinToAor(viewModel, it.ua.account.aor)
|
spinToAor(viewModel, it.ua.account.aor)
|
||||||
} ?: run {
|
} ?: run {
|
||||||
@ -731,6 +740,9 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont
|
|||||||
val hasNewVoicemail = remember(aor, accountUpdate) {
|
val hasNewVoicemail = remember(aor, accountUpdate) {
|
||||||
if (aor.isNotEmpty()) (Account.ofAor(aor)?.vmNew ?: 0) > 0 else false
|
if (aor.isNotEmpty()) (Account.ofAor(aor)?.vmNew ?: 0) > 0 else false
|
||||||
}
|
}
|
||||||
|
val isMobile = remember(aor, accountUpdate) {
|
||||||
|
if (aor.isNotEmpty()) Account.ofAor(aor)?.isMobile ?: false else false
|
||||||
|
}
|
||||||
val hasUnreadMessages = remember(aor, accountUpdate) {
|
val hasUnreadMessages = remember(aor, accountUpdate) {
|
||||||
if (aor.isNotEmpty()) Account.ofAor(aor)?.unreadMessages ?: false else false
|
if (aor.isNotEmpty()) Account.ofAor(aor)?.unreadMessages ?: false else false
|
||||||
}
|
}
|
||||||
@ -759,6 +771,12 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont
|
|||||||
val ua = UserAgent.ofAor(aor)!!
|
val ua = UserAgent.ofAor(aor)!!
|
||||||
val acc = ua.account
|
val acc = ua.account
|
||||||
if (acc.vmUri.isNotEmpty()) {
|
if (acc.vmUri.isNotEmpty()) {
|
||||||
|
if (isMobile) {
|
||||||
|
val intent = Intent(ctx, MainActivity::class.java)
|
||||||
|
intent.putExtra("uap", ua.uap)
|
||||||
|
intent.putExtra("peer", acc.vmUri)
|
||||||
|
handleIntent(ctx, viewModel, intent, "call")
|
||||||
|
} else {
|
||||||
dialogTitle.value = ctx.getString(R.string.voicemail_messages)
|
dialogTitle.value = ctx.getString(R.string.voicemail_messages)
|
||||||
dialogMessage.value = acc.vmMessages(ctx)
|
dialogMessage.value = acc.vmMessages(ctx)
|
||||||
firstText.value = ctx.getString(R.string.cancel)
|
firstText.value = ctx.getString(R.string.cancel)
|
||||||
@ -773,6 +791,7 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont
|
|||||||
}
|
}
|
||||||
showDialog.value = true
|
showDialog.value = true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
@ -800,6 +819,7 @@ private fun BottomBar(ctx: Context, viewModel: ViewModel, navController: NavCont
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isMobile)
|
||||||
IconButton(
|
IconButton(
|
||||||
enabled = aor.isNotEmpty(),
|
enabled = aor.isNotEmpty(),
|
||||||
onClick = {
|
onClick = {
|
||||||
@ -896,6 +916,7 @@ private fun MainContent(navController: NavController, viewModel: ViewModel, cont
|
|||||||
|
|
||||||
val calls by viewModel.calls.collectAsState()
|
val calls by viewModel.calls.collectAsState()
|
||||||
val selectedAor by viewModel.selectedAor.collectAsState()
|
val selectedAor by viewModel.selectedAor.collectAsState()
|
||||||
|
val ua = uas.value.find { it.account.aor == selectedAor }
|
||||||
val aorCalls = calls.filter { it.ua.account.aor == selectedAor }
|
val aorCalls = calls.filter { it.ua.account.aor == selectedAor }
|
||||||
val hasActiveCalls = aorCalls.any { !it.callOnHold.value }
|
val hasActiveCalls = aorCalls.any { !it.callOnHold.value }
|
||||||
val conferenceCall = aorCalls.any { it.conferenceCall }
|
val conferenceCall = aorCalls.any { it.conferenceCall }
|
||||||
@ -1007,7 +1028,12 @@ private fun MainContent(navController: NavController, viewModel: ViewModel, cont
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasActiveCalls || conferenceCall)
|
val showEmptyCard = if (ua?.account?.isMobile == true)
|
||||||
|
aorCalls.isEmpty()
|
||||||
|
else
|
||||||
|
!hasActiveCalls || conferenceCall
|
||||||
|
|
||||||
|
if (showEmptyCard)
|
||||||
CallCard(ctx = ctx, viewModel = viewModel, call = null, dialerState = viewModel.dialerState)
|
CallCard(ctx = ctx, viewModel = viewModel, call = null, dialerState = viewModel.dialerState)
|
||||||
|
|
||||||
Indicator(
|
Indicator(
|
||||||
@ -1470,7 +1496,8 @@ private fun CallRow(
|
|||||||
Row( modifier = Modifier
|
Row( modifier = Modifier
|
||||||
.fillMaxWidth(),
|
.fillMaxWidth(),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.Absolute.SpaceBetween
|
horizontalArrangement = if (isDialer || call?.showCancelButton?.value == true || call?.showAnswerRejectButtons?.value == true)
|
||||||
|
Arrangement.Center else Arrangement.SpaceBetween
|
||||||
) {
|
) {
|
||||||
if (isDialer) {
|
if (isDialer) {
|
||||||
if (dialerState.showCallButton.value)
|
if (dialerState.showCallButton.value)
|
||||||
@ -1495,7 +1522,7 @@ private fun CallRow(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (dialerState.showCallConferenceButton.value) {
|
if (dialerState.showCallConferenceButton.value) {
|
||||||
Spacer(modifier = Modifier.weight(1f, true))
|
Spacer(modifier = Modifier.width(32.dp))
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier.size(48.dp),
|
modifier = Modifier.size(48.dp),
|
||||||
enabled = dialerState.callButtonsEnabled.value,
|
enabled = dialerState.callButtonsEnabled.value,
|
||||||
@ -1522,26 +1549,14 @@ private fun CallRow(
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (call!!.showCancelButton.value) {
|
if (call!!.showCancelButton.value) {
|
||||||
if (!call.conferenceCall)
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier.size(48.dp),
|
modifier = Modifier.size(48.dp),
|
||||||
enabled = !call.terminated.value,
|
enabled = !call.terminated.value,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (call.terminated.value) return@IconButton
|
if (call.terminated.value) return@IconButton
|
||||||
call.terminated.value = true
|
call.terminated.value = true
|
||||||
if (BaresipService.telecom) {
|
|
||||||
val connection = ConnectionService.connections[call.callp]
|
|
||||||
if (connection != null)
|
|
||||||
connection.onDisconnect()
|
|
||||||
else {
|
|
||||||
Log.d(TAG, "AoR ${call.ua.account.aor} canceling call ${call.callp}")
|
Log.d(TAG, "AoR ${call.ua.account.aor} canceling call ${call.callp}")
|
||||||
Api.ua_hangup(call.ua.uap, call.callp, 487, "Request Terminated")
|
call.hangup(487, "Request Terminated")
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "AoR ${call.ua.account.aor} canceling call ${call.callp}")
|
|
||||||
Api.ua_hangup(call.ua.uap, call.callp, 487, "Request Terminated")
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
@ -1551,29 +1566,17 @@ private fun CallRow(
|
|||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.width(12.dp))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (call.showHangupButton.value) {
|
if (call.showHangupButton.value) {
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier.size(48.dp),
|
modifier = Modifier.size(48.dp),
|
||||||
enabled = !call.terminated.value,
|
enabled = !call.terminated.value,
|
||||||
onClick = {
|
onClick = {
|
||||||
if (call.terminated.value) return@IconButton
|
if (call.terminated.value) return@IconButton
|
||||||
call.terminated.value = true
|
call.terminated.value = true
|
||||||
if (BaresipService.telecom) {
|
|
||||||
val connection = ConnectionService.connections[call.callp]
|
|
||||||
if (connection != null)
|
|
||||||
connection.onDisconnect()
|
|
||||||
else {
|
|
||||||
Log.d(TAG, "AoR ${call.ua.account.aor} hanging up call ${call.callp}")
|
Log.d(TAG, "AoR ${call.ua.account.aor} hanging up call ${call.callp}")
|
||||||
Api.ua_hangup(call.ua.uap, call.callp, 487, "Request Terminated")
|
call.hangup(487, "Request Terminated")
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.d(TAG, "AoR ${call.ua.account.aor} hanging up call ${call.callp}")
|
|
||||||
Api.ua_hangup(call.ua.uap, call.callp, 487, "Request Terminated")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
@ -1583,8 +1586,9 @@ private fun CallRow(
|
|||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!call.conferenceCall)
|
if (call.showHangupButton.value && !call.conferenceCall)
|
||||||
IconButton( modifier = Modifier.size(48.dp),
|
IconButton( modifier = Modifier.size(48.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
if (call.callOnHold.value) {
|
if (call.callOnHold.value) {
|
||||||
@ -1609,7 +1613,7 @@ private fun CallRow(
|
|||||||
|
|
||||||
var showTransferDialog by remember { mutableStateOf(false) }
|
var showTransferDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
if (!call.conferenceCall)
|
if (call.showHangupButton.value && !call.conferenceCall && !call.ua.account.isMobile)
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier.size(48.dp),
|
modifier = Modifier.size(48.dp),
|
||||||
enabled = call.transferButtonEnabled.value,
|
enabled = call.transferButtonEnabled.value,
|
||||||
@ -1621,8 +1625,7 @@ private fun CallRow(
|
|||||||
showAlert.value = true
|
showAlert.value = true
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val connection = ConnectionService.connections[call.callp]
|
call.hold()
|
||||||
connection?.onHold()
|
|
||||||
if (!call.executeTransfer()) {
|
if (!call.executeTransfer()) {
|
||||||
alertTitle.value = ctx.getString(R.string.notice)
|
alertTitle.value = ctx.getString(R.string.notice)
|
||||||
alertMessage.value = ctx.getString(R.string.transfer_failed)
|
alertMessage.value = ctx.getString(R.string.transfer_failed)
|
||||||
@ -1889,6 +1892,7 @@ private fun CallRow(
|
|||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
val shouldRequestFocus by call.focusDtmf
|
val shouldRequestFocus by call.focusDtmf
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
if (call.showHangupButton.value)
|
||||||
BasicTextField(
|
BasicTextField(
|
||||||
value = call.dtmfText.value,
|
value = call.dtmfText.value,
|
||||||
onValueChange = { newText ->
|
onValueChange = { newText ->
|
||||||
@ -1943,6 +1947,7 @@ private fun CallRow(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if (call.showHangupButton.value)
|
||||||
LaunchedEffect(shouldRequestFocus) {
|
LaunchedEffect(shouldRequestFocus) {
|
||||||
if (shouldRequestFocus) {
|
if (shouldRequestFocus) {
|
||||||
focusRequester.requestFocus()
|
focusRequester.requestFocus()
|
||||||
@ -1950,6 +1955,7 @@ private fun CallRow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (call.showHangupButton.value && !call.ua.account.isMobile)
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = Modifier.size(48.dp),
|
modifier = Modifier.size(48.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
@ -1989,7 +1995,6 @@ private fun CallRow(
|
|||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (call.showAnswerRejectButtons.value) {
|
if (call.showAnswerRejectButtons.value) {
|
||||||
|
|
||||||
@ -2061,14 +2066,14 @@ private fun callClick(ctx: Context, viewModel: ViewModel, dialerState: ViewModel
|
|||||||
ctx,
|
ctx,
|
||||||
viewModel,
|
viewModel,
|
||||||
uriText,
|
uriText,
|
||||||
dialerState.showCallConferenceButton.value
|
dialerState
|
||||||
)
|
)
|
||||||
else if (uris.size == 1)
|
else if (uris.size == 1)
|
||||||
makeCall(
|
makeCall(
|
||||||
ctx,
|
ctx,
|
||||||
viewModel,
|
viewModel,
|
||||||
uris[0],
|
uris[0],
|
||||||
dialerState.showCallConferenceButton.value
|
dialerState
|
||||||
)
|
)
|
||||||
else {
|
else {
|
||||||
selectItems.value = uris
|
selectItems.value = uris
|
||||||
@ -2077,7 +2082,7 @@ private fun callClick(ctx: Context, viewModel: ViewModel, dialerState: ViewModel
|
|||||||
ctx,
|
ctx,
|
||||||
viewModel,
|
viewModel,
|
||||||
uris[index],
|
uris[index],
|
||||||
dialerState.showCallConferenceButton.value
|
dialerState
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
showSelectItemDialog.value = true
|
showSelectItemDialog.value = true
|
||||||
@ -2095,8 +2100,8 @@ private fun callClick(ctx: Context, viewModel: ViewModel, dialerState: ViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String, conferenceCall: Boolean,
|
private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String,
|
||||||
onHoldCallp: Long = 0L) {
|
dialerState: ViewModel.DialerState, onHoldCallp: Long = 0L) {
|
||||||
val aor = viewModel.selectedAor.value
|
val aor = viewModel.selectedAor.value
|
||||||
val ua = UserAgent.ofAor(aor)!!
|
val ua = UserAgent.ofAor(aor)!!
|
||||||
val peerUri = if (Utils.isTelNumber(uriText))
|
val peerUri = if (Utils.isTelNumber(uriText))
|
||||||
@ -2104,12 +2109,15 @@ private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String, confer
|
|||||||
else
|
else
|
||||||
uriText
|
uriText
|
||||||
val uri = if (Utils.isTelUri(peerUri)) {
|
val uri = if (Utils.isTelUri(peerUri)) {
|
||||||
if (ua.account.telProvider == "") {
|
if (ua.account.isMobile)
|
||||||
|
peerUri
|
||||||
|
else if (ua.account.telProvider == "") {
|
||||||
alertTitle.value = ctx.getString(R.string.notice)
|
alertTitle.value = ctx.getString(R.string.notice)
|
||||||
alertMessage.value = String.format(ctx.getString(R.string.no_telephony_provider), aor)
|
alertMessage.value = String.format(ctx.getString(R.string.no_telephony_provider), aor)
|
||||||
showAlert.value = true
|
showAlert.value = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
else
|
||||||
Utils.telToSip(peerUri, ua.account)
|
Utils.telToSip(peerUri, ua.account)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2118,47 +2126,69 @@ private fun makeCall(ctx: Context, viewModel: ViewModel, uriText: String, confer
|
|||||||
alertTitle.value = ctx.getString(R.string.notice)
|
alertTitle.value = ctx.getString(R.string.notice)
|
||||||
alertMessage.value = String.format(ctx.getString(R.string.invalid_sip_or_tel_uri), uri)
|
alertMessage.value = String.format(ctx.getString(R.string.invalid_sip_or_tel_uri), uri)
|
||||||
showAlert.value = true
|
showAlert.value = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else if (ua.account.isMobile && !Utils.isTelUri(uri)) {
|
||||||
|
alertTitle.value = ctx.getString(R.string.notice)
|
||||||
|
alertMessage.value = "Telephone call can only be made to telephone number"
|
||||||
|
showAlert.value = true
|
||||||
|
return
|
||||||
}
|
}
|
||||||
else if (Utils.isAudioMode(ctx,AudioManager.MODE_IN_CALL) &&
|
else if (Utils.isAudioMode(ctx,AudioManager.MODE_IN_CALL) &&
|
||||||
!Call.calls().any { it.ua.account.aor == ua.account.aor })
|
!Call.calls().any { it.ua.account.aor == ua.account.aor })
|
||||||
Toast.makeText(ctx, R.string.call_already_active, Toast.LENGTH_SHORT).show()
|
Toast.makeText(ctx, R.string.call_already_active, Toast.LENGTH_SHORT).show()
|
||||||
else {
|
else {
|
||||||
viewModel.dialerState.callButtonsEnabled.value = false
|
viewModel.dialerState.callButtonsEnabled.value = false
|
||||||
if (BaresipService.telecom) {
|
var error = ""
|
||||||
val tm = ctx.getSystemService(Context.TELECOM_SERVICE) as android.telecom.TelecomManager
|
val tm = ctx.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
|
||||||
val extras = android.os.Bundle()
|
if (VERSION.SDK_INT >= 29 && ua.account.isMobile) {
|
||||||
extras.putParcelable(android.telecom.TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
|
val phoneAccountHandle = Utils.pstnAccountHandle(ctx)
|
||||||
BaresipService.getPhoneAccountHandle(ctx))
|
if (phoneAccountHandle != null) {
|
||||||
val callExtras = android.os.Bundle()
|
val extras = Bundle().apply {
|
||||||
callExtras.putBoolean("conferenceCall", conferenceCall)
|
putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
|
||||||
|
}
|
||||||
|
val callExtras = Bundle()
|
||||||
|
callExtras.putBoolean("pstnCall", true)
|
||||||
|
callExtras.putString("aor", aor)
|
||||||
|
extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras)
|
||||||
|
try {
|
||||||
|
Log.i(TAG, "Placing Telecom PSTN call to $uri with uap=${ua.uap}")
|
||||||
|
tm.placeCall(uri.toUri(), extras)
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
error = "placeCall failed: ${e.message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error = "no phone account"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val extras = Bundle()
|
||||||
|
extras.putParcelable(
|
||||||
|
TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
|
||||||
|
BaresipService.getPhoneAccountHandle(ctx)
|
||||||
|
)
|
||||||
|
val callExtras = Bundle()
|
||||||
|
callExtras.putBoolean("conferenceCall", dialerState.showCallConferenceButton.value)
|
||||||
callExtras.putLong("uap", ua.uap)
|
callExtras.putLong("uap", ua.uap)
|
||||||
if (onHoldCallp != 0L)
|
if (onHoldCallp != 0L)
|
||||||
callExtras.putLong("onHoldCallp", onHoldCallp)
|
callExtras.putLong("onHoldCallp", onHoldCallp)
|
||||||
extras.putBundle(android.telecom.TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras)
|
extras.putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, callExtras)
|
||||||
try {
|
try {
|
||||||
Log.d(TAG, "Placing Telecom call to $uri with uap=${ua.uap}")
|
Log.d(TAG, "Placing Telecom SIP call to $uri with uap=${ua.uap}")
|
||||||
tm.placeCall(uri.toUri(), extras)
|
tm.placeCall(uri.toUri(), extras)
|
||||||
} catch (e: SecurityException) {
|
} catch (e: SecurityException) {
|
||||||
Log.e(TAG, "placeCall failed: ${e.message}")
|
error = "placeCall failed: ${e.message}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (error != "") {
|
||||||
|
Log.e(TAG, error)
|
||||||
viewModel.dialerState.callButtonsEnabled.value = true
|
viewModel.dialerState.callButtonsEnabled.value = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
val intent = Intent(ctx, BaresipService::class.java)
|
|
||||||
intent.action = "Start Call"
|
|
||||||
intent.putExtra("uap", ua.uap)
|
|
||||||
intent.putExtra("uri", uri)
|
|
||||||
intent.putExtra("conferenceCall", conferenceCall)
|
|
||||||
intent.putExtra("onHoldCallp", onHoldCallp)
|
|
||||||
ctx.startService(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun answer(ctx: Context, call: Call) {
|
private fun answer(ctx: Context, call: Call) {
|
||||||
Log.d(TAG, "AoR ${call.ua.account.aor} answering call from ${call.callUri.value}")
|
Log.d(TAG, "AoR ${call.ua.account.aor} answering call from ${call.callUri.value}")
|
||||||
if (BaresipService.telecom)
|
|
||||||
ConnectionService.connections[call.callp]?.setActive()
|
|
||||||
val intent = Intent(ctx, BaresipService::class.java)
|
val intent = Intent(ctx, BaresipService::class.java)
|
||||||
intent.action = "Call Answer"
|
intent.action = "Call Answer"
|
||||||
intent.putExtra("uap", call.ua.uap)
|
intent.putExtra("uap", call.ua.uap)
|
||||||
@ -2168,19 +2198,7 @@ private fun answer(ctx: Context, call: Call) {
|
|||||||
|
|
||||||
private fun reject(call: Call) {
|
private fun reject(call: Call) {
|
||||||
Log.d(TAG, "AoR ${call.ua.account.aor} rejecting call ${call.callp} from ${call.callUri.value}")
|
Log.d(TAG, "AoR ${call.ua.account.aor} rejecting call ${call.callp} from ${call.callUri.value}")
|
||||||
if (BaresipService.telecom) {
|
call.reject()
|
||||||
val connection = ConnectionService.connections[call.callp]
|
|
||||||
if (connection != null)
|
|
||||||
connection.onReject()
|
|
||||||
else {
|
|
||||||
call.rejected = true
|
|
||||||
Api.ua_hangup(call.ua.uap, call.callp, 486, "Busy Here")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
call.rejected = true
|
|
||||||
Api.ua_hangup(call.ua.uap, call.callp, 486, "Busy Here")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun transfer(ctx: Context, viewModel: ViewModel, ua: UserAgent, uriText: String, attended: Boolean) {
|
private fun transfer(ctx: Context, viewModel: ViewModel, ua: UserAgent, uriText: String, attended: Boolean) {
|
||||||
@ -2197,23 +2215,15 @@ private fun transfer(ctx: Context, viewModel: ViewModel, ua: UserAgent, uriText:
|
|||||||
val call = ua.currentCall()
|
val call = ua.currentCall()
|
||||||
if (call != null) {
|
if (call != null) {
|
||||||
if (attended) {
|
if (attended) {
|
||||||
val connection = ConnectionService.connections[call.callp]
|
if (call.hold()) {
|
||||||
val success = if (connection != null) {
|
|
||||||
connection.onHold()
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
call.hold()
|
|
||||||
}
|
|
||||||
if (success) {
|
|
||||||
call.onhold = true
|
call.onhold = true
|
||||||
call.referTo = uri
|
call.referTo = uri
|
||||||
makeCall(ctx, viewModel, uri, false, call.callp)
|
makeCall(ctx, viewModel, uri, viewModel.dialerState, call.callp)
|
||||||
showCall(ctx, viewModel, ua, call)
|
showCall(ctx, viewModel, ua, call)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val connection = ConnectionService.connections[call.callp]
|
call.hold()
|
||||||
connection?.onHold()
|
|
||||||
if (!call.transfer(uri)) {
|
if (!call.transfer(uri)) {
|
||||||
alertTitle.value = ctx.getString(R.string.notice)
|
alertTitle.value = ctx.getString(R.string.notice)
|
||||||
alertMessage.value = ctx.getString(R.string.transfer_failed)
|
alertMessage.value = ctx.getString(R.string.transfer_failed)
|
||||||
@ -2237,7 +2247,7 @@ private fun showCall(ctx: Context, viewModel: ViewModel, ua: UserAgent?, showCal
|
|||||||
viewModel.dialerState.callUriEnabled.value = true
|
viewModel.dialerState.callUriEnabled.value = true
|
||||||
}, 100)
|
}, 100)
|
||||||
viewModel.dialerState.showCallButton.value = true
|
viewModel.dialerState.showCallButton.value = true
|
||||||
viewModel.dialerState.showCallConferenceButton.value = true
|
viewModel.dialerState.showCallConferenceButton.value = !ua.account.isMobile
|
||||||
viewModel.dialerState.callButtonsEnabled.value = true
|
viewModel.dialerState.callButtonsEnabled.value = true
|
||||||
viewModel.dialerState.showSuggestions.value = false
|
viewModel.dialerState.showSuggestions.value = false
|
||||||
dialpadButtonEnabled.value = true
|
dialpadButtonEnabled.value = true
|
||||||
@ -2313,7 +2323,7 @@ private fun showCall(ctx: Context, viewModel: ViewModel, ua: UserAgent?, showCal
|
|||||||
call.callUriLabel.value = ctx.getString(R.string.incoming_call_from_dots)
|
call.callUriLabel.value = ctx.getString(R.string.incoming_call_from_dots)
|
||||||
call.callUri.value = Utils.friendlyUri(ctx, call.peerUri, ua.account)
|
call.callUri.value = Utils.friendlyUri(ctx, call.peerUri, ua.account)
|
||||||
}
|
}
|
||||||
call.transferButtonEnabled.value = true
|
call.transferButtonEnabled.value = !ua.account.isMobile
|
||||||
}
|
}
|
||||||
call.callUri2.value = ""
|
call.callUri2.value = ""
|
||||||
call.callTransfer.value = call.onHoldCall != null
|
call.callTransfer.value = call.onHoldCall != null
|
||||||
@ -2395,8 +2405,6 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params
|
|||||||
|
|
||||||
when (ev[0]) {
|
when (ev[0]) {
|
||||||
"call rejected" -> {
|
"call rejected" -> {
|
||||||
if (!BaresipService.telecom)
|
|
||||||
BaresipService.abandonAudioFocus(ctx)
|
|
||||||
if (aor == viewModel.selectedAor.value)
|
if (aor == viewModel.selectedAor.value)
|
||||||
viewModel.triggerAccountUpdate()
|
viewModel.triggerAccountUpdate()
|
||||||
}
|
}
|
||||||
@ -2439,6 +2447,7 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params
|
|||||||
showCall(ctx, viewModel, ua)
|
showCall(ctx, viewModel, ua)
|
||||||
}
|
}
|
||||||
"call established" -> {
|
"call established" -> {
|
||||||
|
(ctx as? Activity)?.window?.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
if (aor == viewModel.selectedAor.value) {
|
if (aor == viewModel.selectedAor.value) {
|
||||||
viewModel.dialerState.callButtonsEnabled.value = true // Re-enable dialer
|
viewModel.dialerState.callButtonsEnabled.value = true // Re-enable dialer
|
||||||
val callp = params[1] as Long
|
val callp = params[1] as Long
|
||||||
@ -2521,7 +2530,7 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params
|
|||||||
if (call in Call.calls())
|
if (call in Call.calls())
|
||||||
acceptTransfer(ctx, viewModel, ua, call!!, ev[1])
|
acceptTransfer(ctx, viewModel, ua, call!!, ev[1])
|
||||||
else
|
else
|
||||||
makeCall(ctx, viewModel, ev[1], false)
|
makeCall(ctx, viewModel, ev[1], viewModel.dialerState)
|
||||||
}
|
}
|
||||||
showDialog.value = true
|
showDialog.value = true
|
||||||
}
|
}
|
||||||
@ -2529,14 +2538,16 @@ fun handleServiceEvent(ctx: Context, viewModel: ViewModel, event: String, params
|
|||||||
val callp = params[1] as Long
|
val callp = params[1] as Long
|
||||||
val call = Call.ofCallp(callp)
|
val call = Call.ofCallp(callp)
|
||||||
if (call in Call.calls())
|
if (call in Call.calls())
|
||||||
Api.ua_hangup(uap, callp, 487, "Request Terminated")
|
call!!.hangup(487, "Request Terminated")
|
||||||
makeCall(ctx, viewModel, ev[1], false)
|
makeCall(ctx, viewModel, ev[1], viewModel.dialerState)
|
||||||
showCall(ctx, viewModel, ua)
|
showCall(ctx, viewModel, ua)
|
||||||
}
|
}
|
||||||
"transfer failed" -> {
|
"transfer failed" -> {
|
||||||
showCall(ctx, viewModel, ua)
|
showCall(ctx, viewModel, ua)
|
||||||
}
|
}
|
||||||
"call closed" -> {
|
"call closed" -> {
|
||||||
|
if (Call.calls().isEmpty())
|
||||||
|
(ctx as? Activity)?.window?.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
viewModel.updateCalls(Call.calls().toList())
|
viewModel.updateCalls(Call.calls().toList())
|
||||||
val activity = ctx as? Activity
|
val activity = ctx as? Activity
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
|
|||||||
@ -1031,6 +1031,7 @@ private fun SettingsContent(
|
|||||||
) { result ->
|
) { result ->
|
||||||
Log.d(TAG, "dialerRoleRequest result: $result")
|
Log.d(TAG, "dialerRoleRequest result: $result")
|
||||||
viewModel.defaultDialer.value = roleManager.isRoleHeld(RoleManager.ROLE_DIALER)
|
viewModel.defaultDialer.value = roleManager.isRoleHeld(RoleManager.ROLE_DIALER)
|
||||||
|
BaresipService.instance?.addMobileUserAgent()
|
||||||
}
|
}
|
||||||
Switch(
|
Switch(
|
||||||
checked = defaultDialer,
|
checked = defaultDialer,
|
||||||
@ -1181,14 +1182,14 @@ private fun SettingsContent(
|
|||||||
UserAgent()
|
UserAgent()
|
||||||
UniqueContactUri()
|
UniqueContactUri()
|
||||||
AudioSettings(navController)
|
AudioSettings(navController)
|
||||||
|
if (VERSION.SDK_INT >= 29)
|
||||||
|
DefaultDialer()
|
||||||
BatteryOptimizations()
|
BatteryOptimizations()
|
||||||
DarkTheme()
|
DarkTheme()
|
||||||
if (VERSION.SDK_INT >= 31)
|
if (VERSION.SDK_INT >= 31)
|
||||||
DynamicColors()
|
DynamicColors()
|
||||||
ColorBlind()
|
ColorBlind()
|
||||||
ProximitySensing()
|
ProximitySensing()
|
||||||
if (VERSION.SDK_INT >= 29)
|
|
||||||
DefaultDialer()
|
|
||||||
Debug()
|
Debug()
|
||||||
SipTrace()
|
SipTrace()
|
||||||
Reset(onRestartApp)
|
Reset(onRestartApp)
|
||||||
|
|||||||
@ -5,12 +5,13 @@ import com.tutpro.baresip.BaresipService.Companion.colorblind
|
|||||||
import com.tutpro.baresip.BaresipService.Companion.uas
|
import com.tutpro.baresip.BaresipService.Companion.uas
|
||||||
import com.tutpro.baresip.BaresipService.Companion.uasStatus
|
import com.tutpro.baresip.BaresipService.Companion.uasStatus
|
||||||
|
|
||||||
class UserAgent(val uap: Long) {
|
class UserAgent(val uap: Long, virtualAccount: Account? = null) {
|
||||||
|
|
||||||
val account = Account(Api.ua_account(uap))
|
val account = virtualAccount ?: Account(Api.ua_account(uap))
|
||||||
var status = R.drawable.circle_white
|
var status = if (uap != 0L) R.drawable.circle_white else R.drawable.circle_green
|
||||||
|
|
||||||
fun callAlloc(xCall: Long, videoMode: Int): Long {
|
fun callAlloc(xCall: Long, videoMode: Int): Long {
|
||||||
|
if (uap == 0L) return 0L
|
||||||
return Api.ua_call_alloc(uap, xCall, videoMode)
|
return Api.ua_call_alloc(uap, xCall, videoMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +50,7 @@ class UserAgent(val uap: Long) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun reRegister() {
|
fun reRegister() {
|
||||||
|
if (uap == 0L) return
|
||||||
this.status = circleYellow.getValue(colorblind)
|
this.status = circleYellow.getValue(colorblind)
|
||||||
if (this.account.regint == 0)
|
if (this.account.regint == 0)
|
||||||
Api.ua_unregister(this.uap)
|
Api.ua_unregister(this.uap)
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
package com.tutpro.baresip
|
package com.tutpro.baresip
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.KeyguardManager
|
import android.app.KeyguardManager
|
||||||
|
import android.app.role.RoleManager
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Context.ROLE_SERVICE
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
@ -26,6 +29,8 @@ import android.os.Environment
|
|||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.provider.OpenableColumns
|
import android.provider.OpenableColumns
|
||||||
|
import android.telephony.SubscriptionManager
|
||||||
|
import android.telephony.TelephonyManager
|
||||||
import android.telecom.TelecomManager
|
import android.telecom.TelecomManager
|
||||||
import android.telecom.PhoneAccountHandle
|
import android.telecom.PhoneAccountHandle
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
@ -127,6 +132,9 @@ object Utils {
|
|||||||
fun uriUserPart(uri: String): String {
|
fun uriUserPart(uri: String): String {
|
||||||
return if (uri.contains("@"))
|
return if (uri.contains("@"))
|
||||||
uri.substringAfter(":").substringBefore("@")
|
uri.substringAfter(":").substringBefore("@")
|
||||||
|
else
|
||||||
|
if (isTelUri(uri))
|
||||||
|
uri.substringAfter(":").substringBefore(";")
|
||||||
else
|
else
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
@ -180,14 +188,14 @@ object Utils {
|
|||||||
return u
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun e164Uri(uri: String, countryCode: String): String {
|
fun e164Uri(uri: String, countryCode: String): String {
|
||||||
if (countryCode == "") return uri
|
if (countryCode == "") return uri
|
||||||
val scheme = uri.take(4)
|
val scheme = uri.take(4)
|
||||||
val userPart = uriUserPart(uri)
|
val userPart = uriUserPart(uri)
|
||||||
return if (userPart.isDigitsOnly()) {
|
return if (userPart.isDigitsOnly()) {
|
||||||
when {
|
when {
|
||||||
userPart.startsWith("00") -> uri.replace("$scheme$userPart",
|
userPart.startsWith("00") -> uri.replace("$scheme$userPart",
|
||||||
scheme + userPart.substring(2))
|
scheme + "+" + userPart.substring(2))
|
||||||
userPart.startsWith("0") -> uri.replace("${scheme}0",
|
userPart.startsWith("0") -> uri.replace("${scheme}0",
|
||||||
"$scheme$countryCode")
|
"$scheme$countryCode")
|
||||||
else -> uri.replace(scheme, "$scheme$countryCode")
|
else -> uri.replace(scheme, "$scheme$countryCode")
|
||||||
@ -1332,6 +1340,57 @@ object Utils {
|
|||||||
return file
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(29)
|
||||||
|
fun pstnAccountHandle(ctx: Context): PhoneAccountHandle? {
|
||||||
|
val roleManager = ctx.getSystemService(ROLE_SERVICE) as RoleManager
|
||||||
|
if (ctx.checkSelfPermission(Manifest.permission.READ_PHONE_STATE) ==
|
||||||
|
PackageManager.PERMISSION_GRANTED &&
|
||||||
|
roleManager.isRoleHeld(RoleManager.ROLE_DIALER)) {
|
||||||
|
val tm = ctx.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
|
||||||
|
val preferredHandle: PhoneAccountHandle? = tm.userSelectedOutgoingPhoneAccount
|
||||||
|
if (preferredHandle != null)
|
||||||
|
return preferredHandle
|
||||||
|
val baresipHandle = BaresipService.getPhoneAccountHandle(ctx)
|
||||||
|
val phoneAccounts = tm.callCapablePhoneAccounts.filter { it != baresipHandle }
|
||||||
|
return if (phoneAccounts.isNotEmpty())
|
||||||
|
phoneAccounts[0]
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("HardwareIds")
|
||||||
|
fun getLine1Number(ctx: Context): String? {
|
||||||
|
try {
|
||||||
|
if (Build.VERSION.SDK_INT >= 33) {
|
||||||
|
if (ContextCompat.checkSelfPermission(ctx, Manifest.permission.READ_PHONE_NUMBERS) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
val sm = ctx.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
|
||||||
|
val number = sm.getPhoneNumber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID)
|
||||||
|
if (number != "") {
|
||||||
|
Log.i(TAG, "Retrieved SIM number via SubscriptionManager")
|
||||||
|
return number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ContextCompat.checkSelfPermission(ctx, Manifest.permission.READ_PHONE_NUMBERS) == PackageManager.PERMISSION_GRANTED ||
|
||||||
|
ContextCompat.checkSelfPermission(ctx, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
val tm = ctx.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
val number = tm.line1Number
|
||||||
|
if (number != null) {
|
||||||
|
Log.i(TAG, "Retrieved SIM number via TelephonyManager")
|
||||||
|
return number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.w(TAG, "getLine1Number failed: ${e.message}")
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
fun listFilesInDirectory(directoryPath: String): List<File> {
|
fun listFilesInDirectory(directoryPath: String): List<File> {
|
||||||
val directory = File(directoryPath)
|
val directory = File(directoryPath)
|
||||||
|
|||||||
@ -109,6 +109,7 @@
|
|||||||
</string>
|
</string>
|
||||||
<!-- Account Activity -->
|
<!-- Account Activity -->
|
||||||
<string name="account">Tili</string>
|
<string name="account">Tili</string>
|
||||||
|
<string name="not_available">Ei saatavana</string>
|
||||||
<string name="account_nickname_help">Lempinimi (jos annettu) millä tämä tili identifioidaan
|
<string name="account_nickname_help">Lempinimi (jos annettu) millä tämä tili identifioidaan
|
||||||
baresip sovelluksessa.</string>
|
baresip sovelluksessa.</string>
|
||||||
<string name="nickname">Lempinimi</string>
|
<string name="nickname">Lempinimi</string>
|
||||||
@ -215,15 +216,16 @@
|
|||||||
<string name="redirect_mode_help">Valitsee toteutetaanko puhelun uudelleenohjauspyyntö
|
<string name="redirect_mode_help">Valitsee toteutetaanko puhelun uudelleenohjauspyyntö
|
||||||
automaattisesti vai kysytäänkö vahvistusta.</string>
|
automaattisesti vai kysytäänkö vahvistusta.</string>
|
||||||
<string name="voicemail_uri">Puhepostin URI</string>
|
<string name="voicemail_uri">Puhepostin URI</string>
|
||||||
<string name="voicemain_uri_help">SIP URI, jota käytetään
|
<string name="voicemain_uri_help">SIP URI jota käytetään
|
||||||
puhepostiviestien kuunteluun. Jos URI:a ei ole annettu, tietoa
|
puhepostiviestien kuunteluun. Jos URI:a ei ole annettu, tietoa
|
||||||
mahdollista puhepostiviesteistä (Message Waiting Indications) ei tilata.
|
mahdollista puhepostiviesteistä (Message Waiting Indications) ei tilata.
|
||||||
</string>
|
</string>
|
||||||
|
<string name="voicemain_tel_uri_help">TEL URI jota käytetään puhepostiviestien kuunteluun.</string>
|
||||||
<string name="country_code">Maakoodi</string>
|
<string name="country_code">Maakoodi</string>
|
||||||
<string name="country_code_help">Tämän tilin E.164-maakoodi. Jos tulevan puhelun tai viestin
|
<string name="country_code_help">Tämän tilin E.164-maakoodi. Jos puhelun tai viestin toisen
|
||||||
From URI:n käyttäjäosa sisältää puhelinnumeron, joka ei ala \'+\' merkillä, ja jos sitä
|
osapuolen URI:n käyttäjäosa sisältää puhelinnumeron, joka ei ala \'+\' merkillä, ja jos sitä
|
||||||
ei löydy yhteystiedoista, niin tämä maakoodi lisätään numeron eteen ja etsintä tehdään
|
ei löydy yhteystiedoista, niin tämä maakoodi lisätään numeron eteen ja etsintä tehdään
|
||||||
uudelleen. Jos puhelinnumero alkaa yhdellä numerolla \'0\', niin numero \'0\'
|
uudelleen. Jos puhelinnumero alkaa yhdellä tai kahdella numerolla \'0\', niin ne
|
||||||
poistetaan ennen maakoodin lisäämistä.
|
poistetaan ennen maakoodin lisäämistä.
|
||||||
</string>
|
</string>
|
||||||
<string name="invalid_country_code">Virheellinen maakoodi \'%1$s\'</string>
|
<string name="invalid_country_code">Virheellinen maakoodi \'%1$s\'</string>
|
||||||
@ -334,9 +336,9 @@
|
|||||||
<string name="battery_optimizations_help">Ota akun käytön optimointi pois päältä,
|
<string name="battery_optimizations_help">Ota akun käytön optimointi pois päältä,
|
||||||
jos haluat vähentää todennäköisyyttä, että Android rajoittaa baresip-sovelluksen toimintaa
|
jos haluat vähentää todennäköisyyttä, että Android rajoittaa baresip-sovelluksen toimintaa
|
||||||
ja pääsyä verkkoon.</string>
|
ja pääsyä verkkoon.</string>
|
||||||
<string name="default_phone_app">Oletuspuhelinsovellus</string>
|
<string name="default_phone_app">Oletus puhelusovellus</string>
|
||||||
<string name="dialer_role_not_available">Puhelinrooli ei ole saatavana</string>
|
<string name="dialer_role_not_available">Oletus puhelusovellusrooli ei ole saatavana</string>
|
||||||
<string name="default_phone_app_help">Jos merkity, baresip on oletuspuhelinsovellus. Älä merkitse, jos laitteesi täytyy hallita myös muita kuin SIP-puheluita tai -viestejä.</string>
|
<string name="default_phone_app_help">Jos merkitty, baresip on oletus puhelusovellus.</string>
|
||||||
<string name="listen_address">Kuunteluosoite</string>
|
<string name="listen_address">Kuunteluosoite</string>
|
||||||
<string name="listen_address_help">IP-osoite ja portti muotoa
|
<string name="listen_address_help">IP-osoite ja portti muotoa
|
||||||
\'osoite:portti\', missä baresip kuuntelee sisään tulevia
|
\'osoite:portti\', missä baresip kuuntelee sisään tulevia
|
||||||
@ -435,9 +437,6 @@
|
|||||||
<string name="proximity_sensing">Läheisyyden tunnistus</string>
|
<string name="proximity_sensing">Läheisyyden tunnistus</string>
|
||||||
<string name="proximity_sensing_help">Jos merkitty, läheisyyden tunnistus on aktiivinen
|
<string name="proximity_sensing_help">Jos merkitty, läheisyyden tunnistus on aktiivinen
|
||||||
puhelun aikana.</string>
|
puhelun aikana.</string>
|
||||||
<string name="telecom">Telecom-kehys</string>
|
|
||||||
<string name="telecom_help">Käytä Android Telecom-kehystä puheluihin. Jos sinulla on audioon
|
|
||||||
liittyviä ongelmia, kokeile auttaako, kun poistat Telecom-kehyksen käytöstä.</string>
|
|
||||||
<string name="video_size">Videon kehyskoko</string>
|
<string name="video_size">Videon kehyskoko</string>
|
||||||
<string name="video_size_help">Lähetettävän videon kehyskoko (leveys x korkeus)</string>
|
<string name="video_size_help">Lähetettävän videon kehyskoko (leveys x korkeus)</string>
|
||||||
<string name="video_fps">Videokehysten lähetystaajuus</string>
|
<string name="video_fps">Videokehysten lähetystaajuus</string>
|
||||||
@ -530,6 +529,7 @@
|
|||||||
<string name="call_is_ringing">Puhelu soi</string>
|
<string name="call_is_ringing">Puhelu soi</string>
|
||||||
<string name="call_is_on_hold">Puhelu on pidossa</string>
|
<string name="call_is_on_hold">Puhelu on pidossa</string>
|
||||||
<string name="call_is_connected">Puhelu on yhdistetty</string>
|
<string name="call_is_connected">Puhelu on yhdistetty</string>
|
||||||
|
<string name="not_available">Ei saatavilla</string>
|
||||||
<string name="rec_in_call">Tallennus voidaan asettaa päälle tai pois vain silloin, kun puhelu
|
<string name="rec_in_call">Tallennus voidaan asettaa päälle tai pois vain silloin, kun puhelu
|
||||||
ei ole yhdistetty</string>
|
ei ole yhdistetty</string>
|
||||||
<string name="call_transfer">Puhelun siirto</string>
|
<string name="call_transfer">Puhelun siirto</string>
|
||||||
|
|||||||
@ -113,6 +113,7 @@
|
|||||||
</string>
|
</string>
|
||||||
<!-- Account Activity -->
|
<!-- Account Activity -->
|
||||||
<string name="account">Account</string>
|
<string name="account">Account</string>
|
||||||
|
<string name="not_available">Not available</string>
|
||||||
<string name="account_nickname_help">Nickname (if any) used to identify this account within
|
<string name="account_nickname_help">Nickname (if any) used to identify this account within
|
||||||
baresip app.</string>
|
baresip app.</string>
|
||||||
<string name="nickname">Nickname</string>
|
<string name="nickname">Nickname</string>
|
||||||
@ -209,11 +210,12 @@
|
|||||||
<string name="voicemain_uri_help">SIP URI for checking of voicemail messages. If left empty, voicemail
|
<string name="voicemain_uri_help">SIP URI for checking of voicemail messages. If left empty, voicemail
|
||||||
messages (Message Waiting Indications) are not subscribed to.
|
messages (Message Waiting Indications) are not subscribed to.
|
||||||
</string>
|
</string>
|
||||||
|
<string name="voicemain_tel_uri_help">TEL URI for checking of voicemail messages.</string>
|
||||||
<string name="country_code">Country Code</string>
|
<string name="country_code">Country Code</string>
|
||||||
<string name="country_code_help">E.164 country code of this account. If From URI userpart of
|
<string name="country_code_help">E.164 country code of this account. If peer URI userpart of
|
||||||
incoming call or message contains a telephone number that does not start with \'+\' sign and if contact
|
call or message contains a telephone number that does not start with \'+\' sign and if contact
|
||||||
lookup fails, the number is prefixed with this country code and contact lookup is
|
lookup fails, the number is prefixed with this country code and contact lookup is
|
||||||
tried again. If the telephone number starts with a single digit \'0\', digit \'0\' is removed
|
tried again. If the number starts with one or two \'0\' digits, they are removed
|
||||||
before the number is prefixed.
|
before the number is prefixed.
|
||||||
</string>
|
</string>
|
||||||
<string name="invalid_country_code">Invalid Country Code \'%1$s\'</string>
|
<string name="invalid_country_code">Invalid Country Code \'%1$s\'</string>
|
||||||
@ -320,9 +322,8 @@
|
|||||||
to reduce likelihood that Android restricts baresip\'s access to network or enters baresip
|
to reduce likelihood that Android restricts baresip\'s access to network or enters baresip
|
||||||
to standby state.</string>
|
to standby state.</string>
|
||||||
<string name="default_phone_app">Default Phone App</string>
|
<string name="default_phone_app">Default Phone App</string>
|
||||||
<string name="dialer_role_not_available">Dialer role is not available</string>
|
<string name="dialer_role_not_available">Default phone app role is not available</string>
|
||||||
<string name="default_phone_app_help">If checked, baresip is the default phone app. Do not check
|
<string name="default_phone_app_help">If checked, baresip is the default phone app.</string>
|
||||||
if your device may need to handle also other than SIP calls or messages.</string>
|
|
||||||
<string name="listen_address">Listen Address</string>
|
<string name="listen_address">Listen Address</string>
|
||||||
<string name="listen_address_help">IP address and port of form \'address:port\' at which baresip listens
|
<string name="listen_address_help">IP address and port of form \'address:port\' at which baresip listens
|
||||||
for incoming SIP requests. If IP address is an IPv6 address, it must be written inside
|
for incoming SIP requests. If IP address is an IPv6 address, it must be written inside
|
||||||
@ -408,9 +409,6 @@
|
|||||||
<string name="colorblind_help">Use colorblind friendly registration status icons</string>
|
<string name="colorblind_help">Use colorblind friendly registration status icons</string>
|
||||||
<string name="proximity_sensing">Proximity Sensing</string>
|
<string name="proximity_sensing">Proximity Sensing</string>
|
||||||
<string name="proximity_sensing_help">If checked, proximity sensing is active during calls.</string>
|
<string name="proximity_sensing_help">If checked, proximity sensing is active during calls.</string>
|
||||||
<string name="telecom">Telecom Framework</string>
|
|
||||||
<string name="telecom_help">Use Android Telecom framework for calls. If you experience audio
|
|
||||||
related issues, try if it helps when you turn Telecom Framework off.</string>
|
|
||||||
<string name="video_size">Video Frame Size</string>
|
<string name="video_size">Video Frame Size</string>
|
||||||
<string name="video_size_help">Size of transmitted video frames (width x height)</string>
|
<string name="video_size_help">Size of transmitted video frames (width x height)</string>
|
||||||
<string name="video_fps">Video Frames Per Second</string>
|
<string name="video_fps">Video Frames Per Second</string>
|
||||||
@ -480,6 +478,7 @@
|
|||||||
<string name="accept">Accept</string>
|
<string name="accept">Accept</string>
|
||||||
<string name="deny">Deny</string>
|
<string name="deny">Deny</string>
|
||||||
<string name="sip_uri" translatable="false">SIP URI</string>
|
<string name="sip_uri" translatable="false">SIP URI</string>
|
||||||
|
<string name="tel_uri" translatable="false">TEL URI</string>
|
||||||
<string name="add">Add</string>
|
<string name="add">Add</string>
|
||||||
<string name="delete">Delete</string>
|
<string name="delete">Delete</string>
|
||||||
<string name="edit">Edit</string>
|
<string name="edit">Edit</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user