658 lines
24 KiB
Kotlin
658 lines
24 KiB
Kotlin
package com.tutpro.baresip
|
|
|
|
import android.content.Context
|
|
import androidx.compose.foundation.background
|
|
import androidx.compose.foundation.clickable
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
import androidx.compose.foundation.layout.Box
|
|
import androidx.compose.foundation.layout.Column
|
|
import androidx.compose.foundation.layout.PaddingValues
|
|
import androidx.compose.foundation.layout.Row
|
|
import androidx.compose.foundation.layout.Spacer
|
|
import androidx.compose.foundation.layout.WindowInsets
|
|
import androidx.compose.foundation.layout.asPaddingValues
|
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
import androidx.compose.foundation.layout.imePadding
|
|
import androidx.compose.foundation.layout.padding
|
|
import androidx.compose.foundation.layout.size
|
|
import androidx.compose.foundation.layout.statusBars
|
|
import androidx.compose.foundation.rememberScrollState
|
|
import androidx.compose.foundation.text.KeyboardOptions
|
|
import androidx.compose.foundation.verticalScroll
|
|
import androidx.compose.material.icons.Icons
|
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
|
import androidx.compose.material.icons.filled.ArrowDropDown
|
|
import androidx.compose.material.icons.filled.Check
|
|
import androidx.compose.material3.DropdownMenu
|
|
import androidx.compose.material3.DropdownMenuItem
|
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
import androidx.compose.material3.HorizontalDivider
|
|
import androidx.compose.material3.Icon
|
|
import androidx.compose.material3.IconButton
|
|
import androidx.compose.material3.MaterialTheme
|
|
import androidx.compose.material3.OutlinedTextField
|
|
import androidx.compose.material3.Scaffold
|
|
import androidx.compose.material3.Switch
|
|
import androidx.compose.material3.Text
|
|
import androidx.compose.material3.TopAppBar
|
|
import androidx.compose.material3.TopAppBarDefaults
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.getValue
|
|
import androidx.compose.runtime.mutableIntStateOf
|
|
import androidx.compose.runtime.mutableStateOf
|
|
import androidx.compose.runtime.remember
|
|
import androidx.compose.runtime.setValue
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.platform.LocalContext
|
|
import androidx.compose.ui.res.stringResource
|
|
import androidx.compose.ui.text.font.FontWeight
|
|
import androidx.compose.ui.text.input.KeyboardType
|
|
import androidx.compose.ui.unit.dp
|
|
import androidx.compose.ui.unit.sp
|
|
import androidx.navigation.NavController
|
|
import androidx.navigation.NavGraphBuilder
|
|
import androidx.navigation.compose.composable
|
|
import com.tutpro.baresip.CustomElements.AlertDialog
|
|
import com.tutpro.baresip.CustomElements.verticalScrollbar
|
|
|
|
fun NavGraphBuilder.audioScreenRoute(
|
|
navController: NavController,
|
|
) {
|
|
composable("audio") {
|
|
val ctx = LocalContext.current
|
|
AudioScreen(
|
|
onBack = {
|
|
navController.navigateUp()
|
|
},
|
|
checkOnClick = {
|
|
val result = checkOnClick(ctx) // Your existing logic that returns true/false
|
|
navController.previousBackStackEntry
|
|
?.savedStateHandle
|
|
?.set("audio_settings_result", result)
|
|
navController.navigateUp()
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
@Composable
|
|
private fun AudioScreen(
|
|
onBack: () -> Unit,
|
|
checkOnClick: () -> Unit,
|
|
) {
|
|
Scaffold(
|
|
modifier = Modifier.fillMaxSize().imePadding(),
|
|
containerColor = MaterialTheme.colorScheme.background,
|
|
topBar = {
|
|
Column(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.background(MaterialTheme.colorScheme.background)
|
|
.padding(
|
|
top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding()
|
|
)
|
|
) {
|
|
TopAppBar(
|
|
title = {
|
|
Text(
|
|
text = stringResource(R.string.audio_settings),
|
|
fontWeight = FontWeight.Bold
|
|
)
|
|
},
|
|
colors = TopAppBarDefaults.topAppBarColors(
|
|
containerColor = MaterialTheme.colorScheme.primary,
|
|
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary,
|
|
titleContentColor = MaterialTheme.colorScheme.onPrimary,
|
|
actionIconContentColor = MaterialTheme.colorScheme.onPrimary
|
|
),
|
|
navigationIcon = {
|
|
IconButton(onClick = onBack) {
|
|
Icon(
|
|
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
|
contentDescription = null,
|
|
)
|
|
}
|
|
},
|
|
windowInsets = WindowInsets(0, 0, 0, 0),
|
|
actions = {
|
|
IconButton(onClick = checkOnClick) {
|
|
Icon(
|
|
imageVector = Icons.Filled.Check,
|
|
contentDescription = "Check"
|
|
)
|
|
}
|
|
},
|
|
)
|
|
}
|
|
}
|
|
) { contentPadding ->
|
|
AudioContent(contentPadding)
|
|
}
|
|
}
|
|
|
|
private var newCallVolume = BaresipService.callVolume
|
|
private var oldMicGain = ""
|
|
private var newMicGain = ""
|
|
private var oldSpeakerPhone = BaresipService.speakerPhone
|
|
private var newSpeakerPhone = oldSpeakerPhone
|
|
private var oldAudioModules = ArrayList<String>()
|
|
private var newAudioModules = mutableMapOf<String, Boolean>()
|
|
private var oldOpusBitrate = ""
|
|
private var newOpusBitrate = oldOpusBitrate
|
|
private var oldOpusPacketLoss = ""
|
|
private var newOpusPacketLoss = oldOpusPacketLoss
|
|
private var newAudioDelay = BaresipService.audioDelay.toString()
|
|
private var newToneCountry = BaresipService.toneCountry
|
|
|
|
private var save = false
|
|
|
|
private val alertTitle = mutableStateOf("")
|
|
private val alertMessage = mutableStateOf("")
|
|
private val showAlert = mutableStateOf(false)
|
|
|
|
@Composable
|
|
private fun AudioContent(contentPadding: PaddingValues) {
|
|
|
|
oldAudioModules = Config.variables("module")
|
|
oldOpusBitrate = Config.variable("opus_bitrate")
|
|
oldOpusPacketLoss = Config.variable("opus_packet_loss")
|
|
if (!BaresipService.agcAvailable)
|
|
oldMicGain = Config.variable("augain")
|
|
|
|
if (showAlert.value) {
|
|
AlertDialog(
|
|
showDialog = showAlert,
|
|
title = alertTitle.value,
|
|
message = alertMessage.value,
|
|
positiveButtonText = stringResource(R.string.ok),
|
|
)
|
|
}
|
|
|
|
val scrollState = rememberScrollState()
|
|
|
|
Column(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.padding(contentPadding)
|
|
.padding(top = 8.dp, bottom = 8.dp, start = 16.dp, end = 4.dp)
|
|
.verticalScrollbar(scrollState)
|
|
.verticalScroll(state = scrollState),
|
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
|
) {
|
|
CallVolume()
|
|
MicGain()
|
|
SpeakerPhone()
|
|
AudioModules()
|
|
OpusBitRate()
|
|
OpusPacketLoss()
|
|
AudioDelay()
|
|
ToneCountry()
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun CallVolume() {
|
|
Row(
|
|
Modifier.fillMaxWidth().padding(end=10.dp),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.Start
|
|
) {
|
|
val defaultCallVolumeTitle = stringResource(R.string.default_call_volume)
|
|
val defaultCallVolumeHelp = stringResource(R.string.default_call_volume_help)
|
|
Text(text = defaultCallVolumeTitle,
|
|
modifier = Modifier.weight(1f)
|
|
.clickable {
|
|
alertTitle.value = defaultCallVolumeTitle
|
|
alertMessage.value = defaultCallVolumeHelp
|
|
showAlert.value = true
|
|
},
|
|
fontSize = 18.sp)
|
|
val isDropDownExpanded = remember {
|
|
mutableStateOf(false)
|
|
}
|
|
val volNames = listOf("--", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10")
|
|
val volValues = listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
|
val itemPosition = remember {
|
|
mutableIntStateOf(volValues.indexOf(BaresipService.callVolume))
|
|
}
|
|
Box {
|
|
Row(
|
|
horizontalArrangement = Arrangement.Center,
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
modifier = Modifier.clickable {
|
|
isDropDownExpanded.value = true
|
|
}
|
|
) {
|
|
Text(text = volNames[itemPosition.intValue])
|
|
Icon(
|
|
imageVector = Icons.Filled.ArrowDropDown,
|
|
contentDescription = null,
|
|
modifier = Modifier.size(36.dp)
|
|
)
|
|
}
|
|
DropdownMenu(
|
|
expanded = isDropDownExpanded.value,
|
|
onDismissRequest = {
|
|
isDropDownExpanded.value = false
|
|
}) {
|
|
volNames.forEachIndexed { index, vol ->
|
|
DropdownMenuItem(text = {
|
|
Text(text = vol)
|
|
},
|
|
onClick = {
|
|
isDropDownExpanded.value = false
|
|
itemPosition.intValue = index
|
|
newCallVolume = volValues[index]
|
|
})
|
|
if (index < 10)
|
|
HorizontalDivider(thickness = 1.dp)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun MicGain() {
|
|
if (!BaresipService.agcAvailable)
|
|
Row(
|
|
Modifier.fillMaxWidth().padding(end = 10.dp),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.Start
|
|
) {
|
|
val microphoneGainTitle = stringResource(R.string.microphone_gain)
|
|
val microphoneGainHelp = stringResource(R.string.microphone_gain_help)
|
|
var micGain by remember { mutableStateOf(oldMicGain) }
|
|
newMicGain = micGain
|
|
OutlinedTextField(
|
|
value = micGain,
|
|
placeholder = { Text(microphoneGainTitle) },
|
|
onValueChange = {
|
|
micGain = it
|
|
newMicGain = micGain
|
|
},
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.clickable {
|
|
alertTitle.value = microphoneGainTitle
|
|
alertMessage.value = microphoneGainHelp
|
|
showAlert.value = true
|
|
},
|
|
textStyle = androidx.compose.ui.text.TextStyle(fontSize = 18.sp),
|
|
label = { Text(microphoneGainTitle) },
|
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
|
|
)
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun SpeakerPhone() {
|
|
Row(
|
|
Modifier.fillMaxWidth().padding(end=10.dp),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.Start
|
|
) {
|
|
val speakerPhoneTitle = stringResource(R.string.speaker_phone)
|
|
val speakerPhoneHelp = stringResource(R.string.speaker_phone_help)
|
|
Text(text = speakerPhoneTitle,
|
|
modifier = Modifier.weight(1f)
|
|
.clickable {
|
|
alertTitle.value = speakerPhoneTitle
|
|
alertMessage.value = speakerPhoneHelp
|
|
showAlert.value = true
|
|
},
|
|
fontSize = 18.sp)
|
|
var speakerPhone by remember { mutableStateOf(BaresipService.speakerPhone) }
|
|
Switch(
|
|
checked = speakerPhone,
|
|
onCheckedChange = {
|
|
speakerPhone = it
|
|
newSpeakerPhone = speakerPhone
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun AudioModules() {
|
|
Column(
|
|
modifier = Modifier.fillMaxWidth(),
|
|
horizontalAlignment = Alignment.Start,
|
|
) {
|
|
val audioModulesTitle = stringResource(R.string.audio_modules_title)
|
|
val audioModulesHelp = stringResource(R.string.audio_modules_help)
|
|
Text(text = audioModulesTitle,
|
|
fontSize = 18.sp,
|
|
modifier = Modifier.clickable {
|
|
alertTitle.value = audioModulesTitle
|
|
alertMessage.value = audioModulesHelp
|
|
showAlert.value = true
|
|
})
|
|
for (module in Config.audioModules) {
|
|
Row(horizontalArrangement = Arrangement.Start,
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
modifier = Modifier.padding(start = 18.dp, end = 10.dp)
|
|
) {
|
|
Text(text = String.format(stringResource(R.string.bullet_item), module), fontSize = 18.sp)
|
|
Spacer(modifier = Modifier.weight(1f))
|
|
var checked by remember { mutableStateOf(oldAudioModules.contains("${module}.so")) }
|
|
Switch(
|
|
checked = checked,
|
|
onCheckedChange = {
|
|
checked = it
|
|
newAudioModules[module] = checked
|
|
}
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun OpusBitRate() {
|
|
Row(
|
|
Modifier.fillMaxWidth().padding(end = 10.dp),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.Start
|
|
) {
|
|
val opusBitRateTitle = stringResource(R.string.opus_bit_rate)
|
|
val opusBitRateHelp = stringResource(R.string.opus_bit_rate_help)
|
|
var opusBitrate by remember { mutableStateOf(oldOpusBitrate) }
|
|
newOpusBitrate = opusBitrate
|
|
OutlinedTextField(
|
|
value = opusBitrate,
|
|
placeholder = { Text(opusBitRateTitle) },
|
|
onValueChange = {
|
|
opusBitrate = it
|
|
newOpusBitrate = opusBitrate
|
|
},
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.clickable {
|
|
alertTitle.value = opusBitRateTitle
|
|
alertMessage.value = opusBitRateHelp
|
|
showAlert.value = true
|
|
},
|
|
textStyle = androidx.compose.ui.text.TextStyle(fontSize = 18.sp),
|
|
label = { Text(opusBitRateTitle) },
|
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
|
|
)
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun OpusPacketLoss() {
|
|
Row(
|
|
Modifier.fillMaxWidth().padding(end = 10.dp, top = 8.dp),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.Start
|
|
) {
|
|
val opusPacketLossTitle = stringResource(R.string.opus_packet_loss)
|
|
val opusPacketLossHelp = stringResource(R.string.opus_packet_loss_help)
|
|
var opusPacketLoss by remember { mutableStateOf(oldOpusPacketLoss) }
|
|
newOpusPacketLoss = opusPacketLoss
|
|
OutlinedTextField(
|
|
value = opusPacketLoss,
|
|
placeholder = { Text(opusPacketLossTitle) },
|
|
onValueChange = {
|
|
opusPacketLoss = it
|
|
newOpusPacketLoss = opusPacketLoss
|
|
},
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.clickable {
|
|
alertTitle.value = opusPacketLossTitle
|
|
alertMessage.value = opusPacketLossHelp
|
|
showAlert.value = true
|
|
},
|
|
textStyle = androidx.compose.ui.text.TextStyle(fontSize = 18.sp),
|
|
label = { Text(opusPacketLossTitle) },
|
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
|
|
)
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun AudioDelay() {
|
|
Row(
|
|
Modifier.fillMaxWidth().padding(end = 10.dp, top = 8.dp),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.Start
|
|
) {
|
|
val audioDelayTitle = stringResource(R.string.audio_delay)
|
|
val audioDelayHelp = stringResource(R.string.audio_delay_help)
|
|
var audioDelay by remember { mutableStateOf(BaresipService.audioDelay.toString()) }
|
|
newAudioDelay = audioDelay
|
|
OutlinedTextField(
|
|
value = audioDelay,
|
|
placeholder = { Text(audioDelayTitle) },
|
|
onValueChange = {
|
|
audioDelay = it
|
|
newAudioDelay = audioDelay
|
|
},
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.clickable {
|
|
alertTitle.value = audioDelayTitle
|
|
alertMessage.value = audioDelayHelp
|
|
showAlert.value = true
|
|
},
|
|
textStyle = androidx.compose.ui.text.TextStyle(fontSize = 18.sp),
|
|
label = { Text(audioDelayTitle) },
|
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
|
|
)
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun ToneCountry() {
|
|
Row(
|
|
Modifier.fillMaxWidth().padding(end=10.dp),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.Start
|
|
) {
|
|
val toneCountryTitle = stringResource(R.string.tone_country)
|
|
val toneCountryHelp = stringResource(R.string.tone_country_help)
|
|
Text(text = toneCountryTitle,
|
|
modifier = Modifier.weight(1f)
|
|
.clickable {
|
|
alertTitle.value = toneCountryTitle
|
|
alertMessage.value = toneCountryHelp
|
|
showAlert.value = true
|
|
},
|
|
fontSize = 18.sp)
|
|
val isDropDownExpanded = remember {
|
|
mutableStateOf(false)
|
|
}
|
|
val countryNames = arrayListOf("BG", "BR", "DE", "CZ", "ES", "FI", "FR", "GB", "JP", "NO", "NZ", "SE", "RU", "US")
|
|
val countryValues = arrayListOf("bg", "br", "de", "cz", "es", "fi", "fr", "uk", "jp", "no", "nz", "se", "ru", "us")
|
|
val itemPosition = remember {
|
|
mutableIntStateOf(countryValues.indexOf(BaresipService.toneCountry))
|
|
}
|
|
Box {
|
|
Row(
|
|
horizontalArrangement = Arrangement.Center,
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
modifier = Modifier.clickable {
|
|
isDropDownExpanded.value = true
|
|
}
|
|
) {
|
|
Text(text = countryNames[itemPosition.intValue])
|
|
Icon(
|
|
imageVector = Icons.Filled.ArrowDropDown,
|
|
contentDescription = null,
|
|
modifier = Modifier.size(36.dp)
|
|
)
|
|
}
|
|
DropdownMenu(
|
|
expanded = isDropDownExpanded.value,
|
|
onDismissRequest = {
|
|
isDropDownExpanded.value = false
|
|
}) {
|
|
countryNames.forEachIndexed { index, name ->
|
|
DropdownMenuItem(text = {
|
|
Text(text = name)
|
|
},
|
|
onClick = {
|
|
isDropDownExpanded.value = false
|
|
itemPosition.intValue = index
|
|
newToneCountry = countryValues[index]
|
|
})
|
|
if (index < 10)
|
|
HorizontalDivider(thickness = 1.dp)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun checkOnClick(ctx: Context): Boolean {
|
|
|
|
var restart = false
|
|
|
|
if (BaresipService.callVolume != newCallVolume) {
|
|
BaresipService.callVolume = newCallVolume
|
|
Config.replaceVariable("call_volume", newCallVolume.toString())
|
|
save = true
|
|
}
|
|
|
|
if (!BaresipService.agcAvailable) {
|
|
var gain = newMicGain.trim()
|
|
if (!gain.contains("."))
|
|
gain = "$gain.0"
|
|
if (gain != oldMicGain) {
|
|
if (!checkMicGain(gain)) {
|
|
alertTitle.value = ctx.getString(R.string.notice)
|
|
alertMessage.value = "${ctx.getString(R.string.invalid_microphone_gain)}: $gain."
|
|
showAlert.value = true
|
|
return false
|
|
}
|
|
if (gain == "1.0") {
|
|
Api.module_unload("augain")
|
|
Config.removeVariableValue("module", "augain.so")
|
|
Config.replaceVariable("augain", "1.0")
|
|
} else {
|
|
if (oldMicGain == "1.0") {
|
|
if (Api.module_load("augain") != 0) {
|
|
alertTitle.value = ctx.getString(R.string.error)
|
|
alertMessage.value = ctx.getString(R.string.failed_to_load_module) + ": augain.so"
|
|
showAlert.value = true
|
|
return false
|
|
}
|
|
Config.addVariable("module", "augain.so")
|
|
}
|
|
Config.replaceVariable("augain", gain)
|
|
Api.cmd_exec("augain $gain")
|
|
}
|
|
save = true
|
|
}
|
|
}
|
|
|
|
if (newSpeakerPhone != BaresipService.speakerPhone) {
|
|
BaresipService.speakerPhone = newSpeakerPhone
|
|
Config.replaceVariable("speaker_phone",
|
|
if (BaresipService.speakerPhone) "yes" else "no")
|
|
save = true
|
|
}
|
|
|
|
for (module in Config.audioModules) {
|
|
if (newAudioModules[module] != null) {
|
|
if (newAudioModules[module]!!) {
|
|
if (!oldAudioModules.contains("${module}.so")) {
|
|
if (Api.module_load("${module}.so") != 0) {
|
|
alertTitle.value = ctx.getString(R.string.error)
|
|
alertMessage.value = "${ctx.getString(R.string.failed_to_load_module)}: ${module}.so"
|
|
showAlert.value = true
|
|
return false
|
|
}
|
|
Config.addVariable("module", "${module}.so")
|
|
save = true
|
|
}
|
|
} else if (oldAudioModules.contains("${module}.so")) {
|
|
Api.module_unload("${module}.so")
|
|
Config.removeVariableValue("module", "${module}.so")
|
|
for (ua in BaresipService.uas.value)
|
|
ua.account.removeAudioCodecs(module)
|
|
Account.saveAccounts()
|
|
save = true
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newOpusBitrate != oldOpusBitrate) {
|
|
if (!checkOpusBitRate(newOpusBitrate)) {
|
|
alertTitle.value = ctx.getString(R.string.notice)
|
|
alertMessage.value = "${ctx.getString(R.string.invalid_opus_bitrate)}: $newOpusBitrate."
|
|
showAlert.value = true
|
|
return false
|
|
}
|
|
Config.replaceVariable("opus_bitrate", newOpusBitrate)
|
|
restart = true
|
|
save = true
|
|
}
|
|
|
|
if (newOpusPacketLoss != oldOpusPacketLoss) {
|
|
if (!checkOpusPacketLoss(newOpusPacketLoss)) {
|
|
alertTitle.value = ctx.getString(R.string.notice)
|
|
alertMessage.value = "${ctx.getString(R.string.invalid_opus_packet_loss)}: $newOpusPacketLoss"
|
|
showAlert.value = true
|
|
return false
|
|
}
|
|
Config.replaceVariable("opus_packet_loss", newOpusPacketLoss)
|
|
restart = true
|
|
save = true
|
|
}
|
|
|
|
val audioDelay = newAudioDelay.trim()
|
|
if (audioDelay != BaresipService.audioDelay.toString()) {
|
|
if (!checkAudioDelay(audioDelay)) {
|
|
alertTitle.value = ctx.getString(R.string.notice)
|
|
alertMessage.value = String.format(ctx.getString(R.string.invalid_audio_delay), audioDelay)
|
|
showAlert.value = true
|
|
return false
|
|
}
|
|
Config.replaceVariable("audio_delay", audioDelay)
|
|
BaresipService.audioDelay = audioDelay.toLong()
|
|
save = true
|
|
}
|
|
|
|
if (BaresipService.toneCountry != newToneCountry) {
|
|
BaresipService.toneCountry = newToneCountry
|
|
Config.replaceVariable("tone_country", newToneCountry)
|
|
save = true
|
|
}
|
|
|
|
if (save) Config.save()
|
|
|
|
return restart
|
|
}
|
|
|
|
private fun checkMicGain(micGain: String): Boolean {
|
|
val number =
|
|
try {
|
|
micGain.toDouble()
|
|
} catch (_: NumberFormatException) {
|
|
return false
|
|
}
|
|
return number >= 1.0
|
|
}
|
|
|
|
private fun checkOpusBitRate(opusBitRate: String): Boolean {
|
|
val number = opusBitRate.toIntOrNull() ?: return false
|
|
return (number >= 6000) && (number <= 510000)
|
|
}
|
|
|
|
private fun checkOpusPacketLoss(opusPacketLoss: String): Boolean {
|
|
val number = opusPacketLoss.toIntOrNull() ?: return false
|
|
return (number >= 0) && (number <= 100)
|
|
}
|
|
|
|
private fun checkAudioDelay(audioDelay: String): Boolean {
|
|
val number = audioDelay.toIntOrNull() ?: return false
|
|
return (number >= 100) && (number <= 3000)
|
|
}
|
|
|