Added iLBC audio codec
This commit is contained in:
@@ -34,6 +34,10 @@ add_library(lib_g729 STATIC IMPORTED)
|
|||||||
set_target_properties(lib_g729 PROPERTIES IMPORTED_LOCATION
|
set_target_properties(lib_g729 PROPERTIES IMPORTED_LOCATION
|
||||||
${distribution_DIR}/g729/lib/${ANDROID_ABI}/libbcg729.a)
|
${distribution_DIR}/g729/lib/${ANDROID_ABI}/libbcg729.a)
|
||||||
|
|
||||||
|
add_library(lib_ilbc STATIC IMPORTED)
|
||||||
|
set_target_properties(lib_ilbc PROPERTIES IMPORTED_LOCATION
|
||||||
|
${distribution_DIR}/ilbc/lib/${ANDROID_ABI}/libilbc.a)
|
||||||
|
|
||||||
add_library(lib_codec2 STATIC IMPORTED)
|
add_library(lib_codec2 STATIC IMPORTED)
|
||||||
set_target_properties(lib_codec2 PROPERTIES IMPORTED_LOCATION
|
set_target_properties(lib_codec2 PROPERTIES IMPORTED_LOCATION
|
||||||
${distribution_DIR}/codec2/lib/${ANDROID_ABI}/libcodec2.a)
|
${distribution_DIR}/codec2/lib/${ANDROID_ABI}/libcodec2.a)
|
||||||
@@ -83,6 +87,7 @@ target_link_libraries(
|
|||||||
lib_g722
|
lib_g722
|
||||||
lib_g722_1
|
lib_g722_1
|
||||||
lib_g729
|
lib_g729
|
||||||
|
lib_ilbc
|
||||||
lib_codec2
|
lib_codec2
|
||||||
lib_amrnb
|
lib_amrnb
|
||||||
lib_amrwb
|
lib_amrwb
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.tutpro.baresip
|
package com.tutpro.baresip
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.media.RingtoneManager
|
import android.media.RingtoneManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
@@ -44,11 +43,11 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -58,6 +57,7 @@ import androidx.compose.ui.text.input.KeyboardType
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
@@ -71,10 +71,12 @@ enum class Result {
|
|||||||
fun NavGraphBuilder.audioScreenRoute(navController: NavController) {
|
fun NavGraphBuilder.audioScreenRoute(navController: NavController) {
|
||||||
composable("audio") {
|
composable("audio") {
|
||||||
val ctx = LocalContext.current
|
val ctx = LocalContext.current
|
||||||
|
val audioViewModel = viewModel<AudioViewModel>()
|
||||||
AudioScreen(
|
AudioScreen(
|
||||||
|
viewModel = audioViewModel,
|
||||||
onBack = { navController.navigateUp() },
|
onBack = { navController.navigateUp() },
|
||||||
checkOnClick = {
|
checkOnClick = {
|
||||||
when (checkOnClick(ctx)) {
|
when (audioViewModel.saveSettings(ctx)) {
|
||||||
Result.OK -> navController.navigateUp()
|
Result.OK -> navController.navigateUp()
|
||||||
Result.RESTART -> {
|
Result.RESTART -> {
|
||||||
navController.previousBackStackEntry
|
navController.previousBackStackEntry
|
||||||
@@ -91,7 +93,16 @@ fun NavGraphBuilder.audioScreenRoute(navController: NavController) {
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun AudioScreen(onBack: () -> Unit, checkOnClick: () -> Unit) {
|
private fun AudioScreen(
|
||||||
|
viewModel: AudioViewModel,
|
||||||
|
onBack: () -> Unit,
|
||||||
|
checkOnClick: () -> Unit
|
||||||
|
) {
|
||||||
|
val ctx = LocalContext.current
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
viewModel.loadSettings(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize().imePadding(),
|
modifier = Modifier.fillMaxSize().imePadding(),
|
||||||
containerColor = MaterialTheme.colorScheme.background,
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
@@ -126,42 +137,16 @@ private fun AudioScreen(onBack: () -> Unit, checkOnClick: () -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
contentPadding -> AudioContent(contentPadding)
|
contentPadding -> AudioContent(viewModel, 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 alertTitle = mutableStateOf("")
|
||||||
private val alertMessage = mutableStateOf("")
|
private val alertMessage = mutableStateOf("")
|
||||||
private val showAlert = mutableStateOf(false)
|
private val showAlert = mutableStateOf(false)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun AudioContent(contentPadding: PaddingValues) {
|
private fun AudioContent(viewModel: AudioViewModel, 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)
|
if (showAlert.value)
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
@@ -177,37 +162,30 @@ private fun AudioContent(contentPadding: PaddingValues) {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(contentPadding)
|
.padding(contentPadding)
|
||||||
.padding(top = 8.dp, bottom = 8.dp, start = 16.dp, end = 4.dp)
|
.padding(top = 16.dp, bottom = 8.dp, start = 16.dp, end = 4.dp)
|
||||||
.verticalScrollbar(scrollState)
|
.verticalScrollbar(scrollState)
|
||||||
.verticalScroll(state = scrollState),
|
.verticalScroll(state = scrollState),
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
) {
|
) {
|
||||||
Ringtone()
|
Ringtone(viewModel)
|
||||||
ToneCountry()
|
ToneCountry(viewModel)
|
||||||
SpeakerPhone()
|
SpeakerPhone(viewModel)
|
||||||
CallVolume()
|
CallVolume(viewModel)
|
||||||
MicGain()
|
MicGain(viewModel)
|
||||||
AudioModules()
|
AudioModules(viewModel)
|
||||||
OpusBitRate()
|
OpusBitRate(viewModel)
|
||||||
OpusPacketLoss()
|
OpusPacketLoss(viewModel)
|
||||||
AudioDelay()
|
IlbcMode(viewModel)
|
||||||
|
AudioDelay(viewModel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun Ringtone() {
|
private fun Ringtone(viewModel: AudioViewModel) {
|
||||||
val ringToneTitle = stringResource(R.string.ringtone)
|
val ringToneTitle = stringResource(R.string.ringtone)
|
||||||
val selectRingToneMessage = stringResource(R.string.select_ringtone)
|
val selectRingToneMessage = stringResource(R.string.select_ringtone)
|
||||||
val ctx = LocalContext.current
|
val ringtoneUri by viewModel.ringtoneUri.collectAsState()
|
||||||
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(
|
val launcher = rememberLauncherForActivityResult(
|
||||||
ActivityResultContracts.StartActivityForResult()
|
ActivityResultContracts.StartActivityForResult()
|
||||||
) { result: ActivityResult ->
|
) { result: ActivityResult ->
|
||||||
@@ -218,8 +196,7 @@ private fun Ringtone() {
|
|||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
result.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
result.data?.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI)
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
ringtoneUri = uri.toString()
|
viewModel.ringtoneUri.value = uri.toString()
|
||||||
newRingtoneUri = ringtoneUri
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,7 +233,7 @@ private fun Ringtone() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ToneCountry() {
|
private fun ToneCountry(viewModel: AudioViewModel) {
|
||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth().padding(end = 10.dp),
|
Modifier.fillMaxWidth().padding(end = 10.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@@ -275,20 +252,19 @@ private fun ToneCountry() {
|
|||||||
},
|
},
|
||||||
fontSize = 18.sp
|
fontSize = 18.sp
|
||||||
)
|
)
|
||||||
|
val currentToneCountry by viewModel.toneCountry.collectAsState()
|
||||||
val isDropDownExpanded = remember { mutableStateOf(false) }
|
val isDropDownExpanded = remember { mutableStateOf(false) }
|
||||||
val countryNames = arrayListOf("BG", "BR", "DE", "CZ", "ES", "FI", "FR", "GB", "JP", "NO", "NZ", "SE", "RU", "US")
|
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 countryValues = arrayListOf("bg", "br", "de", "cz", "es", "fi", "fr", "uk", "jp", "no", "nz", "se", "ru", "us")
|
||||||
val itemPosition = remember {
|
val itemPosition = countryValues.indexOf(currentToneCountry).let { if (it != -1) it else countryValues.indexOf("us") }
|
||||||
val index = countryValues.indexOf(BaresipService.toneCountry)
|
|
||||||
mutableIntStateOf(if (index != -1) index else countryValues.indexOf("us"))
|
|
||||||
}
|
|
||||||
Box {
|
Box {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.clickable { isDropDownExpanded.value = true }
|
modifier = Modifier.clickable { isDropDownExpanded.value = true }
|
||||||
) {
|
) {
|
||||||
Text(text = countryNames[itemPosition.intValue])
|
Text(text = countryNames[itemPosition])
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.ArrowDropDown,
|
imageVector = Icons.Filled.ArrowDropDown,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
@@ -304,8 +280,7 @@ private fun ToneCountry() {
|
|||||||
text = { Text(text = name) },
|
text = { Text(text = name) },
|
||||||
onClick = {
|
onClick = {
|
||||||
isDropDownExpanded.value = false
|
isDropDownExpanded.value = false
|
||||||
itemPosition.intValue = index
|
viewModel.toneCountry.value = countryValues[index]
|
||||||
newToneCountry = countryValues[index]
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if (index < 10)
|
if (index < 10)
|
||||||
@@ -317,7 +292,7 @@ private fun ToneCountry() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SpeakerPhone() {
|
private fun SpeakerPhone(viewModel: AudioViewModel) {
|
||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth().padding(end=10.dp),
|
Modifier.fillMaxWidth().padding(end=10.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@@ -335,19 +310,18 @@ private fun SpeakerPhone() {
|
|||||||
},
|
},
|
||||||
fontSize = 18.sp
|
fontSize = 18.sp
|
||||||
)
|
)
|
||||||
var speakerPhone by remember { mutableStateOf(oldSpeakerPhone) }
|
val speakerPhone by viewModel.speakerPhone.collectAsState()
|
||||||
Switch(
|
Switch(
|
||||||
checked = speakerPhone,
|
checked = speakerPhone,
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
speakerPhone = it
|
viewModel.speakerPhone.value = it
|
||||||
newSpeakerPhone = speakerPhone
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun CallVolume() {
|
private fun CallVolume(viewModel: AudioViewModel) {
|
||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth().padding(end=10.dp),
|
Modifier.fillMaxWidth().padding(end=10.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@@ -365,20 +339,19 @@ private fun CallVolume() {
|
|||||||
},
|
},
|
||||||
fontSize = 18.sp
|
fontSize = 18.sp
|
||||||
)
|
)
|
||||||
|
val currentCallVolume by viewModel.callVolume.collectAsState()
|
||||||
val isDropDownExpanded = remember { mutableStateOf(false) }
|
val isDropDownExpanded = remember { mutableStateOf(false) }
|
||||||
val volNames = listOf("--", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10")
|
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 volValues = listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
||||||
val itemPosition = remember {
|
val itemPosition = volValues.indexOf(currentCallVolume).let { if (it != -1) it else 0 }
|
||||||
val index = volValues.indexOf(BaresipService.callVolume)
|
|
||||||
mutableIntStateOf(if (index != -1) index else 0)
|
|
||||||
}
|
|
||||||
Box {
|
Box {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.clickable { isDropDownExpanded.value = true }
|
modifier = Modifier.clickable { isDropDownExpanded.value = true }
|
||||||
) {
|
) {
|
||||||
Text(text = volNames[itemPosition.intValue])
|
Text(text = volNames[itemPosition])
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.ArrowDropDown,
|
imageVector = Icons.Filled.ArrowDropDown,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
@@ -394,8 +367,7 @@ private fun CallVolume() {
|
|||||||
text = { Text(text = vol) },
|
text = { Text(text = vol) },
|
||||||
onClick = {
|
onClick = {
|
||||||
isDropDownExpanded.value = false
|
isDropDownExpanded.value = false
|
||||||
itemPosition.intValue = index
|
viewModel.callVolume.value = volValues[index]
|
||||||
newCallVolume = volValues[index]
|
|
||||||
})
|
})
|
||||||
if (index < 10)
|
if (index < 10)
|
||||||
HorizontalDivider(thickness = 1.dp)
|
HorizontalDivider(thickness = 1.dp)
|
||||||
@@ -406,7 +378,7 @@ private fun CallVolume() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MicGain() {
|
private fun MicGain(viewModel: AudioViewModel) {
|
||||||
if (!BaresipService.agcAvailable)
|
if (!BaresipService.agcAvailable)
|
||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth().padding(end = 10.dp),
|
Modifier.fillMaxWidth().padding(end = 10.dp),
|
||||||
@@ -415,15 +387,11 @@ private fun MicGain() {
|
|||||||
) {
|
) {
|
||||||
val microphoneGainTitle = stringResource(R.string.microphone_gain)
|
val microphoneGainTitle = stringResource(R.string.microphone_gain)
|
||||||
val microphoneGainHelp = stringResource(R.string.microphone_gain_help)
|
val microphoneGainHelp = stringResource(R.string.microphone_gain_help)
|
||||||
var micGain by remember { mutableStateOf(oldMicGain) }
|
val micGain by viewModel.micGain.collectAsState()
|
||||||
newMicGain = micGain
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = micGain,
|
value = micGain,
|
||||||
placeholder = { Text(microphoneGainTitle) },
|
placeholder = { Text(microphoneGainTitle) },
|
||||||
onValueChange = {
|
onValueChange = { viewModel.micGain.value = it },
|
||||||
micGain = it
|
|
||||||
newMicGain = micGain
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable {
|
.clickable {
|
||||||
@@ -439,7 +407,8 @@ private fun MicGain() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun AudioModules() {
|
private fun AudioModules(viewModel: AudioViewModel) {
|
||||||
|
val audioModules by viewModel.audioModules.collectAsState()
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalAlignment = Alignment.Start,
|
horizontalAlignment = Alignment.Start,
|
||||||
@@ -454,27 +423,29 @@ private fun AudioModules() {
|
|||||||
alertMessage.value = audioModulesHelp
|
alertMessage.value = audioModulesHelp
|
||||||
showAlert.value = true
|
showAlert.value = true
|
||||||
})
|
})
|
||||||
for (module in Config.audioModules)
|
for (module in Config.audioModules) {
|
||||||
Row(horizontalArrangement = Arrangement.Start,
|
Row(horizontalArrangement = Arrangement.Start,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.padding(start = 18.dp, end = 10.dp)
|
modifier = Modifier.padding(start = 18.dp, end = 10.dp)
|
||||||
) {
|
) {
|
||||||
Text(text = String.format(stringResource(R.string.bullet_item), module), fontSize = 18.sp)
|
Text(text = String.format(stringResource(R.string.bullet_item), module), fontSize = 18.sp)
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
var checked by remember { mutableStateOf(oldAudioModules.contains("${module}.so")) }
|
val checked = audioModules[module] ?: false
|
||||||
Switch(
|
Switch(
|
||||||
checked = checked,
|
checked = checked,
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
checked = it
|
val newMap = audioModules.toMutableMap()
|
||||||
newAudioModules[module] = checked
|
newMap[module] = it
|
||||||
|
viewModel.audioModules.value = newMap
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun OpusBitRate() {
|
private fun OpusBitRate(viewModel: AudioViewModel) {
|
||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth().padding(end = 10.dp),
|
Modifier.fillMaxWidth().padding(end = 10.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@@ -482,15 +453,11 @@ private fun OpusBitRate() {
|
|||||||
) {
|
) {
|
||||||
val opusBitRateTitle = stringResource(R.string.opus_bit_rate)
|
val opusBitRateTitle = stringResource(R.string.opus_bit_rate)
|
||||||
val opusBitRateHelp = stringResource(R.string.opus_bit_rate_help)
|
val opusBitRateHelp = stringResource(R.string.opus_bit_rate_help)
|
||||||
var opusBitrate by remember { mutableStateOf(oldOpusBitrate) }
|
val opusBitrate by viewModel.opusBitrate.collectAsState()
|
||||||
newOpusBitrate = opusBitrate
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = opusBitrate,
|
value = opusBitrate,
|
||||||
placeholder = { Text(opusBitRateTitle) },
|
placeholder = { Text(opusBitRateTitle) },
|
||||||
onValueChange = {
|
onValueChange = { viewModel.opusBitrate.value = it },
|
||||||
opusBitrate = it
|
|
||||||
newOpusBitrate = opusBitrate
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable {
|
.clickable {
|
||||||
@@ -506,7 +473,7 @@ private fun OpusBitRate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun OpusPacketLoss() {
|
private fun OpusPacketLoss(viewModel: AudioViewModel) {
|
||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth().padding(end = 10.dp, top = 8.dp),
|
Modifier.fillMaxWidth().padding(end = 10.dp, top = 8.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@@ -514,15 +481,11 @@ private fun OpusPacketLoss() {
|
|||||||
) {
|
) {
|
||||||
val opusPacketLossTitle = stringResource(R.string.opus_packet_loss)
|
val opusPacketLossTitle = stringResource(R.string.opus_packet_loss)
|
||||||
val opusPacketLossHelp = stringResource(R.string.opus_packet_loss_help)
|
val opusPacketLossHelp = stringResource(R.string.opus_packet_loss_help)
|
||||||
var opusPacketLoss by remember { mutableStateOf(oldOpusPacketLoss) }
|
val opusPacketLoss by viewModel.opusPacketLoss.collectAsState()
|
||||||
newOpusPacketLoss = opusPacketLoss
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = opusPacketLoss,
|
value = opusPacketLoss,
|
||||||
placeholder = { Text(opusPacketLossTitle) },
|
placeholder = { Text(opusPacketLossTitle) },
|
||||||
onValueChange = {
|
onValueChange = { viewModel.opusPacketLoss.value = it },
|
||||||
opusPacketLoss = it
|
|
||||||
newOpusPacketLoss = opusPacketLoss
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable {
|
.clickable {
|
||||||
@@ -538,7 +501,66 @@ private fun OpusPacketLoss() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun AudioDelay() {
|
private fun IlbcMode(viewModel: AudioViewModel) {
|
||||||
|
Row(
|
||||||
|
Modifier.fillMaxWidth().padding(end = 10.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Start
|
||||||
|
) {
|
||||||
|
val ilbcModeTitle = stringResource(R.string.ilbc_mode)
|
||||||
|
val ilbcModeHelp = stringResource(R.string.ilbc_mode_help)
|
||||||
|
Text(
|
||||||
|
text = ilbcModeTitle,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.clickable {
|
||||||
|
alertTitle.value = ilbcModeTitle
|
||||||
|
alertMessage.value = ilbcModeHelp
|
||||||
|
showAlert.value = true
|
||||||
|
},
|
||||||
|
fontSize = 18.sp
|
||||||
|
)
|
||||||
|
val currentIlbcMode by viewModel.ilbcMode.collectAsState()
|
||||||
|
val isDropDownExpanded = remember { mutableStateOf(false) }
|
||||||
|
val modeNames = listOf("20ms", "30ms")
|
||||||
|
val modeValues = listOf("20", "30")
|
||||||
|
val itemPosition = modeValues.indexOf(currentIlbcMode).let { if (it != -1) it else 1 }
|
||||||
|
|
||||||
|
Box {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.clickable { isDropDownExpanded.value = true }
|
||||||
|
) {
|
||||||
|
Text(text = modeNames[itemPosition])
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.ArrowDropDown,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(36.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = isDropDownExpanded.value,
|
||||||
|
onDismissRequest = { isDropDownExpanded.value = false }
|
||||||
|
) {
|
||||||
|
modeNames.forEachIndexed { index, name ->
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(text = name) },
|
||||||
|
onClick = {
|
||||||
|
isDropDownExpanded.value = false
|
||||||
|
viewModel.ilbcMode.value = modeValues[index]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (index < modeNames.size - 1)
|
||||||
|
HorizontalDivider(thickness = 1.dp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun AudioDelay(viewModel: AudioViewModel) {
|
||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth().padding(end = 10.dp, top = 8.dp, bottom = 12.dp),
|
Modifier.fillMaxWidth().padding(end = 10.dp, top = 8.dp, bottom = 12.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@@ -546,15 +568,11 @@ private fun AudioDelay() {
|
|||||||
) {
|
) {
|
||||||
val audioDelayTitle = stringResource(R.string.audio_delay)
|
val audioDelayTitle = stringResource(R.string.audio_delay)
|
||||||
val audioDelayHelp = stringResource(R.string.audio_delay_help)
|
val audioDelayHelp = stringResource(R.string.audio_delay_help)
|
||||||
var audioDelay by remember { mutableStateOf(BaresipService.audioDelay.toString()) }
|
val currentAudioDelay by viewModel.audioDelay.collectAsState()
|
||||||
newAudioDelay = audioDelay
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = audioDelay,
|
value = currentAudioDelay,
|
||||||
placeholder = { Text(audioDelayTitle) },
|
placeholder = { Text(audioDelayTitle) },
|
||||||
onValueChange = {
|
onValueChange = { viewModel.audioDelay.value = it },
|
||||||
audioDelay = it
|
|
||||||
newAudioDelay = audioDelay
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable {
|
.clickable {
|
||||||
@@ -568,155 +586,3 @@ private fun AudioDelay() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,194 @@
|
|||||||
|
package com.tutpro.baresip
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.media.RingtoneManager
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
||||||
|
class AudioViewModel : ViewModel() {
|
||||||
|
|
||||||
|
val speakerPhone = MutableStateFlow(false)
|
||||||
|
val callVolume = MutableStateFlow(0)
|
||||||
|
val micGain = MutableStateFlow("")
|
||||||
|
val audioModules = MutableStateFlow(mutableMapOf<String, Boolean>())
|
||||||
|
val opusBitrate = MutableStateFlow("")
|
||||||
|
val opusPacketLoss = MutableStateFlow("")
|
||||||
|
val ilbcMode = MutableStateFlow("")
|
||||||
|
val audioDelay = MutableStateFlow("")
|
||||||
|
val toneCountry = MutableStateFlow("")
|
||||||
|
val ringtoneUri = MutableStateFlow("")
|
||||||
|
|
||||||
|
private var isLoaded = false
|
||||||
|
var oldSpeakerPhone = false
|
||||||
|
var oldCallVolume = 0
|
||||||
|
var oldMicGain = ""
|
||||||
|
var oldAudioModules = ArrayList<String>()
|
||||||
|
var oldOpusBitrate = ""
|
||||||
|
var oldOpusPacketLoss = ""
|
||||||
|
var oldIlbcMode = ""
|
||||||
|
var oldAudioDelay = ""
|
||||||
|
var oldToneCountry = ""
|
||||||
|
var oldRingtoneUri = ""
|
||||||
|
|
||||||
|
fun loadSettings(ctx: Context) {
|
||||||
|
if (isLoaded || !Config.isInitialized()) return else isLoaded = true
|
||||||
|
|
||||||
|
oldSpeakerPhone = Config.variable("speaker_phone") == "yes"
|
||||||
|
speakerPhone.value = oldSpeakerPhone
|
||||||
|
|
||||||
|
oldCallVolume = BaresipService.callVolume
|
||||||
|
callVolume.value = oldCallVolume
|
||||||
|
|
||||||
|
if (!BaresipService.agcAvailable) {
|
||||||
|
oldMicGain = Config.variable("augain")
|
||||||
|
micGain.value = oldMicGain
|
||||||
|
}
|
||||||
|
|
||||||
|
oldAudioModules = Config.variables("module")
|
||||||
|
val modulesMap = mutableMapOf<String, Boolean>()
|
||||||
|
for (module in Config.audioModules)
|
||||||
|
modulesMap[module] = oldAudioModules.contains("${module}.so")
|
||||||
|
|
||||||
|
audioModules.value = modulesMap
|
||||||
|
|
||||||
|
oldOpusBitrate = Config.variable("opus_bitrate")
|
||||||
|
opusBitrate.value = oldOpusBitrate
|
||||||
|
|
||||||
|
oldOpusPacketLoss = Config.variable("opus_packet_loss")
|
||||||
|
opusPacketLoss.value = oldOpusPacketLoss
|
||||||
|
|
||||||
|
oldIlbcMode = Config.variable("ilbc_mode")
|
||||||
|
ilbcMode.value = oldIlbcMode
|
||||||
|
|
||||||
|
oldAudioDelay = Config.variable("audio_delay")
|
||||||
|
if (oldAudioDelay == "") oldAudioDelay = BaresipService.audioDelay.toString()
|
||||||
|
audioDelay.value = oldAudioDelay
|
||||||
|
|
||||||
|
oldToneCountry = BaresipService.toneCountry
|
||||||
|
toneCountry.value = oldToneCountry
|
||||||
|
|
||||||
|
oldRingtoneUri = Preferences(ctx).ringtoneUri ?: ""
|
||||||
|
ringtoneUri.value = oldRingtoneUri
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveSettings(ctx: Context): Result {
|
||||||
|
var restart = false
|
||||||
|
var save = false
|
||||||
|
|
||||||
|
if (Preferences(ctx).ringtoneUri != ringtoneUri.value) {
|
||||||
|
Preferences(ctx).ringtoneUri = ringtoneUri.value
|
||||||
|
BaresipService.rt = RingtoneManager.getRingtone(ctx, ringtoneUri.value.toUri())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BaresipService.toneCountry != toneCountry.value) {
|
||||||
|
BaresipService.toneCountry = toneCountry.value
|
||||||
|
Config.replaceVariable("tone_country", toneCountry.value)
|
||||||
|
save = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BaresipService.callVolume != callVolume.value) {
|
||||||
|
BaresipService.callVolume = callVolume.value
|
||||||
|
Config.replaceVariable("call_volume", callVolume.value.toString())
|
||||||
|
save = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!BaresipService.agcAvailable) {
|
||||||
|
var gain = micGain.value.trim()
|
||||||
|
if (gain.isNotEmpty() && !gain.contains(".")) gain = "$gain.0"
|
||||||
|
if (gain != oldMicGain) {
|
||||||
|
if (!checkMicGain(gain)) 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) return Result.ERROR
|
||||||
|
Config.addVariable("module", "augain.so")
|
||||||
|
}
|
||||||
|
Config.replaceVariable("augain", gain)
|
||||||
|
Api.cmd_exec("augain $gain")
|
||||||
|
}
|
||||||
|
save = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (speakerPhone.value != oldSpeakerPhone) {
|
||||||
|
Config.replaceVariable("speaker_phone", if (speakerPhone.value) "yes" else "no")
|
||||||
|
BaresipService.speakerPhoneAuto = speakerPhone.value
|
||||||
|
save = true
|
||||||
|
}
|
||||||
|
|
||||||
|
for (module in Config.audioModules) {
|
||||||
|
val enabled = audioModules.value[module] ?: false
|
||||||
|
if (enabled != oldAudioModules.contains("${module}.so")) {
|
||||||
|
if (enabled) {
|
||||||
|
if (Api.module_load("${module}.so") != 0) return Result.ERROR
|
||||||
|
Config.addVariable("module", "${module}.so")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
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 (opusBitrate.value != oldOpusBitrate) {
|
||||||
|
if (!checkOpusBitRate(opusBitrate.value)) return Result.ERROR
|
||||||
|
Config.replaceVariable("opus_bitrate", opusBitrate.value)
|
||||||
|
restart = true
|
||||||
|
save = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opusPacketLoss.value != oldOpusPacketLoss) {
|
||||||
|
if (!checkOpusPacketLoss(opusPacketLoss.value)) return Result.ERROR
|
||||||
|
Config.replaceVariable("opus_packet_loss", opusPacketLoss.value)
|
||||||
|
restart = true
|
||||||
|
save = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ilbcMode.value != oldIlbcMode) {
|
||||||
|
Config.replaceVariable("ilbc_mode", ilbcMode.value)
|
||||||
|
restart = true
|
||||||
|
save = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val delay = audioDelay.value.trim()
|
||||||
|
if (delay != oldAudioDelay) {
|
||||||
|
if (!checkAudioDelay(delay)) return Result.ERROR
|
||||||
|
Config.replaceVariable("audio_delay", delay)
|
||||||
|
BaresipService.audioDelay = delay.toLong()
|
||||||
|
save = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (save) Config.save()
|
||||||
|
|
||||||
|
return if (restart) Result.RESTART else Result.OK
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkMicGain(micGain: String): Boolean {
|
||||||
|
val number = micGain.toDoubleOrNull() ?: 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ import java.nio.charset.StandardCharsets
|
|||||||
object Config {
|
object Config {
|
||||||
|
|
||||||
private val configPath = BaresipService.filesPath + "/config"
|
private val configPath = BaresipService.filesPath + "/config"
|
||||||
val audioModules = listOf("opus", "amr", "libg722", "g7221", "g729", "codec2", "g711")
|
val audioModules = listOf("opus", "amr", "ilbc", "libg722", "g7221", "g729", "codec2", "g711")
|
||||||
private lateinit var config: String
|
private lateinit var config: String
|
||||||
private lateinit var previousConfig: String
|
private lateinit var previousConfig: String
|
||||||
private lateinit var previousLines: List<String>
|
private lateinit var previousLines: List<String>
|
||||||
@@ -213,7 +213,8 @@ object Config {
|
|||||||
val previousModules = previousVariables("module")
|
val previousModules = previousVariables("module")
|
||||||
for (module in audioModules)
|
for (module in audioModules)
|
||||||
if ("${module}.so" in previousModules ||
|
if ("${module}.so" in previousModules ||
|
||||||
(module == "libg722" && "g722.so" in previousModules))
|
(module == "libg722" && "g722.so" in previousModules) ||
|
||||||
|
module == "ilbc")
|
||||||
config = "${config}module ${module}.so\n"
|
config = "${config}module ${module}.so\n"
|
||||||
|
|
||||||
Utils.aecAgcNsCheck()
|
Utils.aecAgcNsCheck()
|
||||||
@@ -236,6 +237,12 @@ object Config {
|
|||||||
else
|
else
|
||||||
"${config}opus_packet_loss $opusPacketLoss\n"
|
"${config}opus_packet_loss $opusPacketLoss\n"
|
||||||
|
|
||||||
|
val ilbcMode = previousVariable("ilbc_mode")
|
||||||
|
config = if (ilbcMode == "")
|
||||||
|
"${config}ilbc_mode 30\n"
|
||||||
|
else
|
||||||
|
"${config}ilbc_mode $ilbcMode\n"
|
||||||
|
|
||||||
val audioDelay = previousVariable("audio_delay")
|
val audioDelay = previousVariable("audio_delay")
|
||||||
if (audioDelay != "") {
|
if (audioDelay != "") {
|
||||||
config = "${config}audio_delay $audioDelay\n"
|
config = "${config}audio_delay $audioDelay\n"
|
||||||
|
|||||||
@@ -89,9 +89,7 @@ import java.io.FileInputStream
|
|||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
|
||||||
private var restart = false
|
|
||||||
private val showRestartDialog = mutableStateOf(false)
|
private val showRestartDialog = mutableStateOf(false)
|
||||||
private var save = false
|
|
||||||
|
|
||||||
fun NavGraphBuilder.settingsScreenRoute(
|
fun NavGraphBuilder.settingsScreenRoute(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
@@ -105,14 +103,14 @@ fun NavGraphBuilder.settingsScreenRoute(
|
|||||||
navController = navController,
|
navController = navController,
|
||||||
settingsViewModel = viewModel,
|
settingsViewModel = viewModel,
|
||||||
onBack = {
|
onBack = {
|
||||||
if (restart)
|
if (viewModel.restart)
|
||||||
showRestartDialog.value = true
|
showRestartDialog.value = true
|
||||||
else
|
else
|
||||||
navController.navigateUp()
|
navController.navigateUp()
|
||||||
},
|
},
|
||||||
checkOnClick = {
|
checkOnClick = {
|
||||||
if (checkOnClick(ctx, viewModel)) {
|
if (checkOnClick(ctx, viewModel)) {
|
||||||
if (restart)
|
if (viewModel.restart)
|
||||||
showRestartDialog.value = true
|
showRestartDialog.value = true
|
||||||
else
|
else
|
||||||
navController.navigateUp()
|
navController.navigateUp()
|
||||||
@@ -142,19 +140,23 @@ private fun SettingsScreen(
|
|||||||
?.observeAsState()
|
?.observeAsState()
|
||||||
|
|
||||||
LaunchedEffect(audioResult?.value) {
|
LaunchedEffect(audioResult?.value) {
|
||||||
|
Log.d(TAG, "audio_settings_result observed: ${audioResult?.value}")
|
||||||
if (audioResult?.value == true) {
|
if (audioResult?.value == true) {
|
||||||
Log.d(TAG, "Got result from AudioSettings: true")
|
Log.d(TAG, "Got result from AudioSettings: true")
|
||||||
restart = true
|
settingsViewModel.restart = true
|
||||||
|
showRestartDialog.value = true
|
||||||
navController.currentBackStackEntry
|
navController.currentBackStackEntry
|
||||||
?.savedStateHandle
|
?.savedStateHandle
|
||||||
?.remove<Boolean>("audio_settings_result")
|
?.remove<Boolean>("audio_settings_result")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(null) {
|
LaunchedEffect(Unit) {
|
||||||
save = false
|
if (!settingsViewModel.restart) {
|
||||||
restart = false
|
settingsViewModel.save = false
|
||||||
|
settingsViewModel.restart = false
|
||||||
settingsViewModel.loadSettings(ctx)
|
settingsViewModel.loadSettings(ctx)
|
||||||
|
}
|
||||||
areSettingsLoaded = true
|
areSettingsLoaded = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,8 +504,8 @@ private fun SettingsContent(
|
|||||||
inputStream.close()
|
inputStream.close()
|
||||||
Config.replaceVariable("sip_certificate", certPath)
|
Config.replaceVariable("sip_certificate", certPath)
|
||||||
viewModel.tlsCertificateFile.value = true
|
viewModel.tlsCertificateFile.value = true
|
||||||
save = true
|
viewModel.save = true
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
} catch (e: Error) {
|
} catch (e: Error) {
|
||||||
alertTitle.value = errorTitleText
|
alertTitle.value = errorTitleText
|
||||||
alertMessage.value = readCertError + ": " + e.message
|
alertMessage.value = readCertError + ": " + e.message
|
||||||
@@ -561,8 +563,8 @@ private fun SettingsContent(
|
|||||||
Utils.putFileContents(certPath, content)
|
Utils.putFileContents(certPath, content)
|
||||||
Config.replaceVariable("sip_certificate", certPath)
|
Config.replaceVariable("sip_certificate", certPath)
|
||||||
viewModel.tlsCertificateFile.value = true
|
viewModel.tlsCertificateFile.value = true
|
||||||
save = true
|
viewModel.save = true
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
shouldShowRequestPermissionRationale(activity, permission) ->
|
shouldShowRequestPermissionRationale(activity, permission) ->
|
||||||
showAlertDialog.value = true
|
showAlertDialog.value = true
|
||||||
@@ -575,8 +577,8 @@ private fun SettingsContent(
|
|||||||
else {
|
else {
|
||||||
Config.removeVariable("sip_certificate")
|
Config.removeVariable("sip_certificate")
|
||||||
Utils.deleteFile(File(BaresipService.filesPath + "/cert.pem"))
|
Utils.deleteFile(File(BaresipService.filesPath + "/cert.pem"))
|
||||||
save = true
|
viewModel.save = true
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -645,7 +647,7 @@ private fun SettingsContent(
|
|||||||
val inputStream = ctx.contentResolver.openInputStream(uri) as FileInputStream
|
val inputStream = ctx.contentResolver.openInputStream(uri) as FileInputStream
|
||||||
caCertsFile.copyInputStreamToFile(inputStream)
|
caCertsFile.copyInputStreamToFile(inputStream)
|
||||||
inputStream.close()
|
inputStream.close()
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
} catch (e: Error) {
|
} catch (e: Error) {
|
||||||
alertTitle.value = errorTitleText
|
alertTitle.value = errorTitleText
|
||||||
alertMessage.value = readCaCertsError + ": " + e.message
|
alertMessage.value = readCaCertsError + ": " + e.message
|
||||||
@@ -696,7 +698,7 @@ private fun SettingsContent(
|
|||||||
}
|
}
|
||||||
File(BaresipService.filesPath + "/ca_certs.crt").writeBytes(content)
|
File(BaresipService.filesPath + "/ca_certs.crt").writeBytes(content)
|
||||||
viewModel.caFile.value = true
|
viewModel.caFile.value = true
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
shouldShowRequestPermissionRationale(activity, permission) -> {
|
shouldShowRequestPermissionRationale(activity, permission) -> {
|
||||||
dialogTitle.value = noticeTitleText
|
dialogTitle.value = noticeTitleText
|
||||||
@@ -716,7 +718,7 @@ private fun SettingsContent(
|
|||||||
Utils.selectInputFile(caCertsRequest)
|
Utils.selectInputFile(caCertsRequest)
|
||||||
else {
|
else {
|
||||||
Utils.deleteFile(File(BaresipService.filesPath + "/ca_certs.crt"))
|
Utils.deleteFile(File(BaresipService.filesPath + "/ca_certs.crt"))
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -991,7 +993,7 @@ private fun SettingsContent(
|
|||||||
if (results[Manifest.permission.READ_PHONE_NUMBERS] == true)
|
if (results[Manifest.permission.READ_PHONE_NUMBERS] == true)
|
||||||
Log.d(TAG, "READ_PHONE_NUMBERS permission granted")
|
Log.d(TAG, "READ_PHONE_NUMBERS permission granted")
|
||||||
BaresipService.instance?.addMobileUserAgent()
|
BaresipService.instance?.addMobileUserAgent()
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val dialerRoleRequest = rememberLauncherForActivityResult(
|
val dialerRoleRequest = rememberLauncherForActivityResult(
|
||||||
@@ -1006,14 +1008,14 @@ private fun SettingsContent(
|
|||||||
)
|
)
|
||||||
if (Utils.checkPermissions(ctx, permissions)) {
|
if (Utils.checkPermissions(ctx, permissions)) {
|
||||||
BaresipService.instance?.addMobileUserAgent()
|
BaresipService.instance?.addMobileUserAgent()
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
requestPermissionLauncher.launch(permissions)
|
requestPermissionLauncher.launch(permissions)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
BaresipService.instance?.addMobileUserAgent()
|
BaresipService.instance?.addMobileUserAgent()
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
Switch(
|
Switch(
|
||||||
checked = defaultDialer,
|
checked = defaultDialer,
|
||||||
@@ -1037,7 +1039,7 @@ private fun SettingsContent(
|
|||||||
Log.e(TAG, "ActivityNotFound exception: ${e.message}")
|
Log.e(TAG, "ActivityNotFound exception: ${e.message}")
|
||||||
}
|
}
|
||||||
BaresipService.instance?.addMobileUserAgent()
|
BaresipService.instance?.addMobileUserAgent()
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -1067,7 +1069,7 @@ private fun SettingsContent(
|
|||||||
Config.replaceVariable("mobile_account", if (it) "yes" else "no")
|
Config.replaceVariable("mobile_account", if (it) "yes" else "no")
|
||||||
Config.save()
|
Config.save()
|
||||||
BaresipService.instance?.addMobileUserAgent()
|
BaresipService.instance?.addMobileUserAgent()
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1105,7 +1107,7 @@ private fun SettingsContent(
|
|||||||
) { _ ->
|
) { _ ->
|
||||||
val isHeld = roleManager.isRoleHeld(RoleManager.ROLE_SMS)
|
val isHeld = roleManager.isRoleHeld(RoleManager.ROLE_SMS)
|
||||||
viewModel.defaultMessaging.value = isHeld
|
viewModel.defaultMessaging.value = isHeld
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
Switch(
|
Switch(
|
||||||
checked = defaultMessaging,
|
checked = defaultMessaging,
|
||||||
@@ -1280,7 +1282,7 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean {
|
|||||||
|
|
||||||
if ((Config.variable("auto_start") == "yes") != viewModel.autoStart.value) {
|
if ((Config.variable("auto_start") == "yes") != viewModel.autoStart.value) {
|
||||||
Config.replaceVariable("auto_start", if (viewModel.autoStart.value) "yes" else "no")
|
Config.replaceVariable("auto_start", if (viewModel.autoStart.value) "yes" else "no")
|
||||||
save = true
|
viewModel.save = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val listenAddr = viewModel.listenAddress.value.trim()
|
val listenAddr = viewModel.listenAddress.value.trim()
|
||||||
@@ -1292,14 +1294,14 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
Config.replaceVariable("sip_listen", listenAddr)
|
Config.replaceVariable("sip_listen", listenAddr)
|
||||||
save = true
|
viewModel.save = true
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.variable("net_af").lowercase() != viewModel.addressFamily.value) {
|
if (Config.variable("net_af").lowercase() != viewModel.addressFamily.value) {
|
||||||
Config.replaceVariable("net_af", viewModel.addressFamily.value)
|
Config.replaceVariable("net_af", viewModel.addressFamily.value)
|
||||||
save = true
|
viewModel.save = true
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val transportProtocols = viewModel.transportProtocols.value
|
val transportProtocols = viewModel.transportProtocols.value
|
||||||
@@ -1315,8 +1317,8 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean {
|
|||||||
Config.removeVariable("sip_transports")
|
Config.removeVariable("sip_transports")
|
||||||
if (transportProtocols.isNotEmpty())
|
if (transportProtocols.isNotEmpty())
|
||||||
Config.replaceVariable("sip_transports", transportProtocols)
|
Config.replaceVariable("sip_transports", transportProtocols)
|
||||||
save = true
|
viewModel.save = true
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val dnsServers = addMissingPorts(viewModel.dnsServers.value
|
val dnsServers = addMissingPorts(viewModel.dnsServers.value
|
||||||
@@ -1345,13 +1347,13 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean {
|
|||||||
Config.updateDnsServers(BaresipService.dnsServers)
|
Config.updateDnsServers(BaresipService.dnsServers)
|
||||||
}
|
}
|
||||||
// Api.net_dns_debug()
|
// Api.net_dns_debug()
|
||||||
save = true
|
viewModel.save = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Config.variable("sip_verify_server") == "yes") != viewModel.verifyServer.value) {
|
if ((Config.variable("sip_verify_server") == "yes") != viewModel.verifyServer.value) {
|
||||||
Config.replaceVariable("sip_verify_server", if (viewModel.verifyServer.value) "yes" else "no")
|
Config.replaceVariable("sip_verify_server", if (viewModel.verifyServer.value) "yes" else "no")
|
||||||
Api.config_verify_server_set(viewModel.verifyServer.value)
|
Api.config_verify_server_set(viewModel.verifyServer.value)
|
||||||
save = true
|
viewModel.save = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val userAgent = viewModel.userAgent.value.trim()
|
val userAgent = viewModel.userAgent.value.trim()
|
||||||
@@ -1367,14 +1369,14 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean {
|
|||||||
Config.replaceVariable("user_agent", userAgent)
|
Config.replaceVariable("user_agent", userAgent)
|
||||||
else
|
else
|
||||||
Config.removeVariable("user_agent")
|
Config.removeVariable("user_agent")
|
||||||
save = true
|
viewModel.save = true
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((Config.variable("sip_cuser_random") == "yes") != viewModel.uniqueContactUri.value) {
|
if ((Config.variable("sip_cuser_random") == "yes") != viewModel.uniqueContactUri.value) {
|
||||||
Config.replaceVariable("sip_cuser_random", if (viewModel.uniqueContactUri.value) "yes" else "no")
|
Config.replaceVariable("sip_cuser_random", if (viewModel.uniqueContactUri.value) "yes" else "no")
|
||||||
save = true
|
viewModel.save = true
|
||||||
restart = true
|
viewModel.restart = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val darkTheme = viewModel.darkTheme.value
|
val darkTheme = viewModel.darkTheme.value
|
||||||
@@ -1387,14 +1389,14 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean {
|
|||||||
BaresipService.darkTheme.value = darkTheme
|
BaresipService.darkTheme.value = darkTheme
|
||||||
AppCompatDelegate.setDefaultNightMode(newDisplayTheme)
|
AppCompatDelegate.setDefaultNightMode(newDisplayTheme)
|
||||||
Config.replaceVariable("dark_theme", if (darkTheme) "yes" else "no")
|
Config.replaceVariable("dark_theme", if (darkTheme) "yes" else "no")
|
||||||
save = true
|
viewModel.save = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val dynamicColors = viewModel.dynamicColors.value
|
val dynamicColors = viewModel.dynamicColors.value
|
||||||
if (BaresipService.dynamicColors.value != dynamicColors) {
|
if (BaresipService.dynamicColors.value != dynamicColors) {
|
||||||
BaresipService.dynamicColors.value = dynamicColors
|
BaresipService.dynamicColors.value = dynamicColors
|
||||||
Config.replaceVariable("dynamic_colors", if (dynamicColors) "yes" else "no")
|
Config.replaceVariable("dynamic_colors", if (dynamicColors) "yes" else "no")
|
||||||
save = true
|
viewModel.save = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val colorblind = viewModel.colorblind.value
|
val colorblind = viewModel.colorblind.value
|
||||||
@@ -1405,14 +1407,14 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean {
|
|||||||
val baresipService = Intent(ctx, BaresipService::class.java)
|
val baresipService = Intent(ctx, BaresipService::class.java)
|
||||||
baresipService.action = "Update Notification"
|
baresipService.action = "Update Notification"
|
||||||
ContextCompat.startForegroundService(ctx, baresipService)
|
ContextCompat.startForegroundService(ctx, baresipService)
|
||||||
save = true
|
viewModel.save = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val proximitySensing = viewModel.proximitySensing.value
|
val proximitySensing = viewModel.proximitySensing.value
|
||||||
if ((Config.variable("proximity_sensing") == "yes") != proximitySensing) {
|
if ((Config.variable("proximity_sensing") == "yes") != proximitySensing) {
|
||||||
Config.replaceVariable("proximity_sensing", if (proximitySensing) "yes" else "no")
|
Config.replaceVariable("proximity_sensing", if (proximitySensing) "yes" else "no")
|
||||||
BaresipService.proximitySensing = proximitySensing
|
BaresipService.proximitySensing = proximitySensing
|
||||||
save = true
|
viewModel.save = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val debug = viewModel.debug.value
|
val debug = viewModel.debug.value
|
||||||
@@ -1421,7 +1423,7 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean {
|
|||||||
Config.replaceVariable("log_level", logLevelString)
|
Config.replaceVariable("log_level", logLevelString)
|
||||||
Api.log_level_set(logLevelString.toInt())
|
Api.log_level_set(logLevelString.toInt())
|
||||||
Log.logLevelSet(logLevelString.toInt())
|
Log.logLevelSet(logLevelString.toInt())
|
||||||
save = true
|
viewModel.save = true
|
||||||
}
|
}
|
||||||
|
|
||||||
val sipTrace = if (debug) viewModel.sipTrace.value else false
|
val sipTrace = if (debug) viewModel.sipTrace.value else false
|
||||||
@@ -1430,7 +1432,7 @@ private fun checkOnClick(ctx: Context, viewModel: SettingsViewModel): Boolean {
|
|||||||
Api.uag_enable_sip_trace(sipTrace)
|
Api.uag_enable_sip_trace(sipTrace)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (save) Config.save()
|
if (viewModel.save) Config.save()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import android.content.Context.ROLE_SERVICE
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -35,6 +37,9 @@ class SettingsViewModel: ViewModel() {
|
|||||||
val debug = MutableStateFlow(false)
|
val debug = MutableStateFlow(false)
|
||||||
val sipTrace = MutableStateFlow(false)
|
val sipTrace = MutableStateFlow(false)
|
||||||
|
|
||||||
|
var restart by mutableStateOf(false)
|
||||||
|
var save by mutableStateOf(false)
|
||||||
|
|
||||||
private var isLoaded = false
|
private var isLoaded = false
|
||||||
|
|
||||||
fun loadSettings(ctx: Context) {
|
fun loadSettings(ctx: Context) {
|
||||||
@@ -45,10 +50,7 @@ class SettingsViewModel: ViewModel() {
|
|||||||
|
|
||||||
listenAddress.value = Config.variable("sip_listen")
|
listenAddress.value = Config.variable("sip_listen")
|
||||||
|
|
||||||
val familyValues = listOf("", "ipv4", "ipv6")
|
addressFamily.value = Config.variable("net_af").lowercase()
|
||||||
val itemPosition =
|
|
||||||
mutableIntStateOf(familyValues.indexOf(Config.variable("net_af").lowercase()))
|
|
||||||
addressFamily.value = familyValues[itemPosition.intValue]
|
|
||||||
|
|
||||||
transportProtocols.value = Config.variable("sip_transports")
|
transportProtocols.value = Config.variable("sip_transports")
|
||||||
|
|
||||||
|
|||||||
@@ -450,6 +450,9 @@
|
|||||||
<string name="audio_delay_help">Audion odotusviive (millisekunneissa) soitetun puhelun alkaessa.
|
<string name="audio_delay_help">Audion odotusviive (millisekunneissa) soitetun puhelun alkaessa.
|
||||||
Aseta korkeampi arvo, jos et kuule vastaajan ääntä heti, kun puhelu alkaa.</string>
|
Aseta korkeampi arvo, jos et kuule vastaajan ääntä heti, kun puhelu alkaa.</string>
|
||||||
<string name="invalid_audio_delay">Virheellinen audioviive \'%1$s\'. Sallittu arvo on välillä 100–3000.</string>
|
<string name="invalid_audio_delay">Virheellinen audioviive \'%1$s\'. Sallittu arvo on välillä 100–3000.</string>
|
||||||
|
<string name="ilbc_mode">iLBC-moodi</string>
|
||||||
|
<string name="ilbc_mode_help">Valitsee iLBC-koodekin kehyksen keston.
|
||||||
|
30ms tarjoaa paremman äänenlaadun alhaisilla bittinopeuksilla, kun taas 20ms:ssä on pienempi viive.</string>
|
||||||
<string name="default_call_volume">Oletus äänen voimakkuus</string>
|
<string name="default_call_volume">Oletus äänen voimakkuus</string>
|
||||||
<string name="default_call_volume_help">Jos valittu, puhelun äänen voimakkuus
|
<string name="default_call_volume_help">Jos valittu, puhelun äänen voimakkuus
|
||||||
asteikolla 1–10.</string>
|
asteikolla 1–10.</string>
|
||||||
|
|||||||
@@ -429,6 +429,9 @@
|
|||||||
<string name="audio_delay_help">Time (in milliseconds) to wait audio from callee when call is established.
|
<string name="audio_delay_help">Time (in milliseconds) to wait audio from callee when call is established.
|
||||||
Set to a higher value if you miss audio from callee at the beginning of the call.</string>
|
Set to a higher value if you miss audio from callee at the beginning of the call.</string>
|
||||||
<string name="invalid_audio_delay">Invalid Audio Delay \'%1$s\'. Valid values are from 100 to 3000.</string>
|
<string name="invalid_audio_delay">Invalid Audio Delay \'%1$s\'. Valid values are from 100 to 3000.</string>
|
||||||
|
<string name="ilbc_mode">iLBC Mode</string>
|
||||||
|
<string name="ilbc_mode_help">Selects the default iLBC frame duration.
|
||||||
|
30ms provides better low-bitrate quality, while 20ms has lower latency.</string>
|
||||||
<string name="default_call_volume">Default Call Volume</string>
|
<string name="default_call_volume">Default Call Volume</string>
|
||||||
<string name="default_call_volume_help">If set, default call audio volume at scale 1–10.</string>
|
<string name="default_call_volume_help">If set, default call audio volume at scale 1–10.</string>
|
||||||
<string name="tone_country">Tone Country</string>
|
<string name="tone_country">Tone Country</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user