730 lines
27 KiB
Kotlin
730 lines
27 KiB
Kotlin
package com.tutpro.baresip
|
|
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.media.RingtoneManager
|
|
import android.net.Uri
|
|
import android.os.Build.VERSION
|
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
|
import androidx.activity.result.ActivityResult
|
|
import androidx.activity.result.contract.ActivityResultContracts
|
|
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.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.statusBarsPadding
|
|
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.core.net.toUri
|
|
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
|
|
|
|
enum class Result {
|
|
OK, ERROR, RESTART
|
|
}
|
|
|
|
fun NavGraphBuilder.audioScreenRoute(
|
|
navController: NavController,
|
|
) {
|
|
composable("audio") {
|
|
val ctx = LocalContext.current
|
|
AudioScreen(
|
|
onBack = { navController.navigateUp() },
|
|
checkOnClick = {
|
|
when (checkOnClick(ctx)) {
|
|
Result.OK -> navController.navigateUp()
|
|
Result.RESTART -> {
|
|
navController.previousBackStackEntry
|
|
?.savedStateHandle
|
|
?.set("audio_settings_result", true)
|
|
navController.navigateUp()
|
|
}
|
|
Result.ERROR -> {}
|
|
}
|
|
},
|
|
)
|
|
}
|
|
}
|
|
|
|
@OptIn(ExperimentalMaterial3Api::class)
|
|
@Composable
|
|
private fun AudioScreen(onBack: () -> Unit, checkOnClick: () -> Unit) {
|
|
Scaffold(
|
|
modifier = Modifier.fillMaxSize().imePadding(),
|
|
containerColor = MaterialTheme.colorScheme.background,
|
|
topBar = {
|
|
Column(modifier = Modifier.background(MaterialTheme.colorScheme.background)) {
|
|
Spacer(Modifier.statusBarsPadding())
|
|
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 newRingtoneUri = ""
|
|
|
|
private var save = false
|
|
|
|
private val alertTitle = mutableStateOf("")
|
|
private val alertMessage = mutableStateOf("")
|
|
private val showAlert = mutableStateOf(false)
|
|
|
|
@Composable
|
|
private fun AudioContent(contentPadding: PaddingValues) {
|
|
|
|
oldSpeakerPhone = Config.variable("speaker_phone") == "yes"
|
|
newSpeakerPhone = oldSpeakerPhone
|
|
oldAudioModules = Config.variables("module")
|
|
for (module in Config.audioModules)
|
|
newAudioModules[module] = oldAudioModules.contains("${module}.so")
|
|
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,
|
|
lastButtonText = 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),
|
|
) {
|
|
Ringtone()
|
|
ToneCountry()
|
|
SpeakerPhone()
|
|
CallVolume()
|
|
MicGain()
|
|
AudioModules()
|
|
OpusBitRate()
|
|
OpusPacketLoss()
|
|
AudioDelay()
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun Ringtone() {
|
|
val ringToneTitle = stringResource(R.string.ringtone)
|
|
val selectRingToneMessage = stringResource(R.string.select_ringtone)
|
|
val ctx = LocalContext.current
|
|
var ringtoneUri by remember {
|
|
mutableStateOf(if (Preferences(ctx).ringtoneUri == "")
|
|
RingtoneManager.getActualDefaultRingtoneUri(ctx, RingtoneManager.TYPE_RINGTONE).toString()
|
|
else
|
|
Preferences(ctx).ringtoneUri!!)
|
|
}
|
|
newRingtoneUri = ringtoneUri
|
|
val launcher = rememberLauncherForActivityResult(
|
|
ActivityResultContracts.StartActivityForResult()
|
|
) { result: ActivityResult ->
|
|
if (result.resultCode == android.app.Activity.RESULT_OK) {
|
|
val uri: Uri? = if (VERSION.SDK_INT >= 33)
|
|
result.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, Uri::class.java)
|
|
else
|
|
@Suppress("DEPRECATION")
|
|
result.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
|
if (uri != null) {
|
|
ringtoneUri = uri.toString()
|
|
newRingtoneUri = ringtoneUri
|
|
}
|
|
}
|
|
}
|
|
Row(
|
|
Modifier.fillMaxWidth(),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.Start
|
|
) {
|
|
Text(
|
|
text = ringToneTitle,
|
|
modifier = Modifier
|
|
.weight(1f)
|
|
.clickable {
|
|
val intent = Intent(RingtoneManager.ACTION_RINGTONE_PICKER)
|
|
intent.putExtra(
|
|
RingtoneManager.EXTRA_RINGTONE_TYPE,
|
|
RingtoneManager.TYPE_RINGTONE
|
|
)
|
|
intent.putExtra(
|
|
RingtoneManager.EXTRA_RINGTONE_TITLE,
|
|
selectRingToneMessage
|
|
)
|
|
intent.putExtra(
|
|
RingtoneManager.EXTRA_RINGTONE_EXISTING_URI,
|
|
ringtoneUri.toUri()
|
|
)
|
|
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false)
|
|
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true)
|
|
launcher.launch(intent)
|
|
},
|
|
fontSize = 18.sp,
|
|
)
|
|
}
|
|
}
|
|
|
|
@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 {
|
|
val index = countryValues.indexOf(BaresipService.toneCountry)
|
|
mutableIntStateOf(if (index != -1) index else countryValues.indexOf("us"))
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@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(oldSpeakerPhone) }
|
|
Switch(
|
|
checked = speakerPhone,
|
|
onCheckedChange = {
|
|
speakerPhone = it
|
|
newSpeakerPhone = speakerPhone
|
|
}
|
|
)
|
|
}
|
|
}
|
|
|
|
@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 {
|
|
val index = volValues.indexOf(BaresipService.callVolume)
|
|
mutableIntStateOf(if (index != -1) index else 0)
|
|
}
|
|
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 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, bottom = 12.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)
|
|
)
|
|
}
|
|
}
|
|
|
|
private fun checkOnClick(ctx: Context): Result {
|
|
|
|
var restart = false
|
|
save = false
|
|
|
|
if (Preferences(ctx).ringtoneUri != newRingtoneUri) {
|
|
Preferences(ctx).ringtoneUri = newRingtoneUri
|
|
BaresipService.rt = RingtoneManager.getRingtone(ctx, newRingtoneUri.toUri())
|
|
}
|
|
|
|
|
|
if (BaresipService.toneCountry != newToneCountry) {
|
|
BaresipService.toneCountry = newToneCountry
|
|
Config.replaceVariable("tone_country", newToneCountry)
|
|
save = true
|
|
}
|
|
|
|
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 Result.ERROR
|
|
}
|
|
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 Result.ERROR
|
|
}
|
|
Config.addVariable("module", "augain.so")
|
|
}
|
|
Config.replaceVariable("augain", gain)
|
|
Api.cmd_exec("augain $gain")
|
|
}
|
|
save = true
|
|
}
|
|
}
|
|
|
|
if (newSpeakerPhone != oldSpeakerPhone) {
|
|
Config.replaceVariable("speaker_phone", if (newSpeakerPhone) "yes" else "no")
|
|
BaresipService.speakerPhoneAuto = newSpeakerPhone
|
|
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 Result.ERROR
|
|
}
|
|
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 Result.ERROR
|
|
}
|
|
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 Result.ERROR
|
|
}
|
|
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 Result.ERROR
|
|
}
|
|
Config.replaceVariable("audio_delay", audioDelay)
|
|
BaresipService.audioDelay = audioDelay.toLong()
|
|
save = true
|
|
}
|
|
|
|
if (save) Config.save()
|
|
|
|
return if (restart) Result.RESTART else Result.OK
|
|
}
|
|
|
|
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)
|
|
}
|