438 lines
18 KiB
Kotlin
Raw Normal View History

2025-03-09 23:45:43 +09:00
package com.tutpro.baresip.plus
import android.os.Build
import android.os.Bundle
import android.util.TypedValue
import android.view.Gravity
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.*
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.core.view.updatePadding
import com.tutpro.baresip.plus.databinding.ActivityAudioBinding
class AudioActivity : AppCompatActivity() {
private lateinit var binding: ActivityAudioBinding
private lateinit var micGain: EditText
private lateinit var opusBitRate: EditText
private lateinit var opusPacketLoss: EditText
private lateinit var aec: CheckBox
private lateinit var speakerPhone: CheckBox
private lateinit var audioDelay: EditText
private var save = false
private var restart = false
private var callVolume = BaresipService.callVolume
private var oldMicGain = ""
private var oldAudioModules = mutableMapOf<String, Boolean>()
private var oldOpusBitrate = ""
private var oldOpusPacketLoss = ""
private var oldAec = false
private var toneCountry = BaresipService.toneCountry
private val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
goBack()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAudioBinding.inflate(layoutInflater)
setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { v: View, insets: WindowInsetsCompat ->
val systemBars: Insets = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
if (Build.VERSION.SDK_INT >= 35)
binding.AudioView.updatePadding(top = systemBars.top + 56)
WindowInsetsCompat.CONSUMED
}
if (!Utils.isDarkTheme(this))
WindowInsetsControllerCompat(window, binding.root).isAppearanceLightStatusBars = true
supportActionBar?.setDisplayHomeAsUpEnabled(true)
Utils.addActivity("audio")
val callVolSpinner = binding.VolumeSpinner
val volKeys = arrayListOf("None", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10")
val volVals = arrayListOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val curVal = callVolume
val curKey = volKeys[curVal]
volKeys.removeAt(curVal)
volVals.removeAt(curVal)
volKeys.add(0, curKey)
volVals.add(0, curVal)
val callVolAdapter = ArrayAdapter(
this, android.R.layout.simple_spinner_item,
volKeys
)
callVolAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
callVolSpinner.adapter = callVolAdapter
callVolSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>,
view: View?,
position: Int,
id: Long
) {
callVolume = volVals[volKeys.indexOf(parent.selectedItem.toString())]
}
override fun onNothingSelected(parent: AdapterView<*>) {
}
}
if (!BaresipService.agcAvailable) {
micGain = binding.MicGain
oldMicGain = Config.variable("augain")
micGain.setText(oldMicGain)
} else {
binding.MicGainLayout.visibility = View.GONE
}
speakerPhone = binding.SpeakerPhone
speakerPhone.isChecked = BaresipService.speakerPhone
val modules = Config.variables("module")
val audioModulesList = binding.AudioModulesList
var id = 1000
for (module in audioModules) {
val rl = RelativeLayout(this)
val rlParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT)
rlParams.marginStart = 16
rl.layoutParams = rlParams
val tv = TextView(this)
tv.id = id++
val tvParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT)
tvParams.addRule(RelativeLayout.ALIGN_PARENT_START)
tvParams.addRule(RelativeLayout.CENTER_VERTICAL)
tvParams.addRule(RelativeLayout.START_OF, id)
tv.layoutParams = tvParams
tv.text = String.format(getString(R.string.bullet_item), module)
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18f)
tv.setTextColor(ContextCompat.getColor(this, R.color.colorItemText))
rl.addView(tv)
val cb = CheckBox(this)
cb.id = id++
val cbParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT)
cbParams.addRule(RelativeLayout.ALIGN_PARENT_END)
cbParams.addRule(RelativeLayout.CENTER_VERTICAL)
cb.layoutParams = cbParams
cb.gravity = Gravity.END
cb.isChecked = modules.contains("${module}.so")
oldAudioModules[module] = cb.isChecked
rl.addView(cb)
audioModulesList.addView(rl)
}
opusBitRate = binding.OpusBitRate
oldOpusBitrate = Config.variable("opus_bitrate")
opusBitRate.setText(oldOpusBitrate)
opusPacketLoss = binding.OpusPacketLoss
oldOpusPacketLoss = Config.variable("opus_packet_loss")
opusPacketLoss.setText(oldOpusPacketLoss)
aec = binding.Aec
oldAec = modules.contains("webrtc_aecm.so")
aec.isChecked = oldAec
audioDelay = binding.AudioDelay
audioDelay.setText("${BaresipService.audioDelay}")
val toneCountrySpinner = binding.ToneCountrySpinner
val toneCountryKeys = arrayListOf("bg", "br", "de", "cz", "es", "fi", "fr", "uk", "jp", "no", "nz", "se", "ru", "us")
val toneCountryVals = arrayListOf("BG", "BR", "DE", "CZ", "ES", "FI", "FR", "GB", "JP", "NO", "NZ", "SE", "RU", "US")
val keyIx = toneCountryKeys.indexOf(toneCountry)
val keyVal = toneCountryVals.elementAt(keyIx)
toneCountryKeys.removeAt(keyIx)
toneCountryVals.removeAt(keyIx)
toneCountryKeys.add(0, toneCountry)
toneCountryVals.add(0, keyVal)
val toneCountryAdapter = ArrayAdapter(this,android.R.layout.simple_spinner_item,
toneCountryVals)
toneCountryAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
toneCountrySpinner.adapter = toneCountryAdapter
toneCountrySpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
toneCountry = toneCountryKeys[toneCountryVals.indexOf(parent.selectedItem.toString())]
}
override fun onNothingSelected(parent: AdapterView<*>) {
}
}
bindTitles()
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
val inflater = menuInflater
inflater.inflate(R.menu.check_icon, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (BaresipService.activities.indexOf("audio") == -1)
return true
when (item.itemId) {
R.id.checkIcon -> {
if (BaresipService.callVolume != callVolume) {
BaresipService.callVolume = callVolume
Config.replaceVariable("call_volume", callVolume.toString())
save = true
}
var gain = "1.0"
if (!BaresipService.agcAvailable) {
gain = micGain.text.toString().trim()
if (!gain.contains("."))
gain = "$gain.0"
if (gain != oldMicGain) {
if (!checkMicGain(gain)) {
Utils.alertView(
this, getString(R.string.notice),
"${getString(R.string.invalid_microphone_gain)}: $gain."
)
micGain.setText(oldMicGain)
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) {
Utils.alertView(
this, getString(R.string.error),
getString(R.string.failed_to_load_module)
)
micGain.setText(oldMicGain)
return false
}
Config.addVariable("module", "augain.so")
}
Config.replaceVariable("augain", gain)
Api.cmd_exec("augain $gain")
}
save = true
}
}
if (speakerPhone.isChecked != BaresipService.speakerPhone) {
BaresipService.speakerPhone = speakerPhone.isChecked
Config.replaceVariable("speaker_phone",
if (BaresipService.speakerPhone) "yes" else "no")
save = true
}
var id = 1001
for (module in audioModules) {
val box = findViewById<CheckBox>(id++)
if (box.isChecked && !oldAudioModules[module]!!) {
if (Api.module_load("${module}.so") != 0) {
Utils.alertView(this, getString(R.string.error),
"${getString(R.string.failed_to_load_module)}: ${module}.so")
return false
}
Config.addVariable("module", "${module}.so")
save = true
}
if (!box.isChecked && oldAudioModules[module]!!) {
Api.module_unload("${module}.so")
Config.removeVariableValue("module", "${module}.so")
for (ua in BaresipService.uas)
ua.account.removeAudioCodecs(module)
AccountsActivity.saveAccounts()
save = true
}
id++
}
val opusBitRate = opusBitRate.text.toString().trim()
if (opusBitRate != oldOpusBitrate) {
if (!checkOpusBitRate(opusBitRate)) {
Utils.alertView(this, getString(R.string.notice),
"${getString(R.string.invalid_opus_bitrate)}: $opusBitRate.")
return false
}
Config.replaceVariable("opus_bitrate", opusBitRate)
restart = true
save = true
}
val opusPacketLoss = opusPacketLoss.text.toString().trim()
if (opusPacketLoss != oldOpusPacketLoss) {
if (!checkOpusPacketLoss(opusPacketLoss)) {
Utils.alertView(this, getString(R.string.notice),
"${getString(R.string.invalid_opus_packet_loss)}: $opusPacketLoss")
return false
}
Config.replaceVariable("opus_packet_loss", opusPacketLoss)
restart = true
save = true
}
if (aec.isChecked != oldAec) {
if (aec.isChecked) {
Config.addVariable("module", "webrtc_aecm.so")
if (gain != "1.0") {
restart = true
} else {
if (Api.module_load("webrtc_aecm.so") != 0) {
Utils.alertView(
this, getString(R.string.error),
getString(R.string.failed_to_load_module)
)
aec.isChecked = false
return false
}
}
BaresipService.webrtcAec = true
} else {
Api.module_unload("webrtc_aecm.so")
Config.removeVariableValue("module", "webrtc_aecm.so")
BaresipService.webrtcAec = false
}
save = true
}
val audioDelay = audioDelay.text.toString().trim()
if (audioDelay != BaresipService.audioDelay.toString()) {
if (!checkAudioDelay(audioDelay)) {
Utils.alertView(this, getString(R.string.notice),
String.format(getString(R.string.invalid_audio_delay), audioDelay))
return false
}
Config.replaceVariable("audio_delay", audioDelay)
BaresipService.audioDelay = audioDelay.toLong()
save = true
}
if (BaresipService.toneCountry != toneCountry) {
BaresipService.toneCountry = toneCountry
Config.replaceVariable("tone_country", toneCountry)
save = true
}
if (save)
Config.save()
setResult(if (restart) RESULT_OK else RESULT_CANCELED)
BaresipService.activities.remove("audio")
finish()
return true
}
android.R.id.home -> {
goBack()
return true
}
}
return super.onOptionsItemSelected(item)
}
private fun goBack() {
BaresipService.activities.remove("audio")
finish()
}
private fun bindTitles() {
binding.VolumeTitle.setOnClickListener {
Utils.alertView(this, getString(R.string.default_call_volume),
getString(R.string.default_call_volume_help))
}
binding.MicGainTitle.setOnClickListener {
Utils.alertView(this, getString(R.string.microphone_gain),
getString(R.string.microphone_gain_help)
)
}
binding.SpeakerPhoneTitle.setOnClickListener {
Utils.alertView(this, getString(R.string.speaker_phone),
getString(R.string.speaker_phone_help))
}
binding.AudioModulesTitle.setOnClickListener {
Utils.alertView(this, getString(R.string.audio_modules_title),
getString(R.string.audio_modules_help))
}
binding.OpusBitRateTitle.setOnClickListener {
Utils.alertView(this, getString(R.string.opus_bit_rate),
getString(R.string.opus_bit_rate_help))
}
binding.OpusPacketLossTitle.setOnClickListener {
Utils.alertView(this, getString(R.string.opus_packet_loss),
getString(R.string.opus_packet_loss_help))
}
binding.AecTitle.setOnClickListener {
Utils.alertView(this, getString(R.string.aec),
getString(R.string.aec_help))
}
binding.AudioDelayTitle.setOnClickListener {
Utils.alertView(this, getString(R.string.audio_delay),
getString(R.string.audio_delay_help))
}
binding.ToneCountryTitle.setOnClickListener {
Utils.alertView(this, getString(R.string.tone_country),
getString(R.string.tone_country_help))
}
}
private fun checkMicGain(micGain: String): Boolean {
val number =
try {
micGain.toDouble()
} catch (e: 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)
}
companion object {
val audioModules = listOf("opus", "amr", "g722", "g7221", "g726", "g729", "codec2", "g711")
}
}