Fixed initialization of account from network
This commit is contained in:
@@ -29,6 +29,7 @@ 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.Check
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -43,6 +44,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@@ -68,6 +70,15 @@ import com.tutpro.baresip.BaresipService.Companion.uas
|
||||
import com.tutpro.baresip.CustomElements.AlertDialog
|
||||
import com.tutpro.baresip.CustomElements.LabelText
|
||||
import com.tutpro.baresip.CustomElements.verticalScrollbar
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import org.xmlpull.v1.XmlPullParserFactory
|
||||
import java.io.StringReader
|
||||
import java.net.URL
|
||||
import java.util.Locale
|
||||
|
||||
class AccountActivity : ComponentActivity() {
|
||||
|
||||
@@ -80,6 +91,7 @@ class AccountActivity : ComponentActivity() {
|
||||
private lateinit var ua: UserAgent
|
||||
private lateinit var aor: String
|
||||
|
||||
private var kind: String? = null
|
||||
private var reRegister = false
|
||||
private var oldNickname = ""
|
||||
private var newNickname = ""
|
||||
@@ -165,6 +177,7 @@ class AccountActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
aor = intent.getStringExtra("aor")!!
|
||||
kind = intent.getStringExtra("kind")
|
||||
|
||||
Utils.addActivity("account,$aor")
|
||||
|
||||
@@ -183,8 +196,94 @@ class AccountActivity : ComponentActivity() {
|
||||
answerModeMap = mapOf(Api.ANSWERMODE_MANUAL to getString(R.string.manual),
|
||||
Api.ANSWERMODE_AUTO to getString(R.string.auto))
|
||||
|
||||
redirectModeMap = mapOf(false to getString(R.string.manual),
|
||||
true to getString(R.string.auto))
|
||||
redirectModeMap = mapOf(false to getString(R.string.manual), true to getString(R.string.auto))
|
||||
|
||||
setContent {
|
||||
AppTheme {
|
||||
keyboardController = LocalSoftwareKeyboardController.current
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = LocalCustomColors.current.background
|
||||
) {
|
||||
AccountScreen(kind) { goBack() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AccountScreen(kind: String?, navigateBack: () -> Unit) {
|
||||
|
||||
var isConfigLoaded by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(kind, acc) {
|
||||
if (kind == "new")
|
||||
initAccountFromConfig(acc) { isConfigLoaded = true }
|
||||
else
|
||||
isConfigLoaded = true
|
||||
}
|
||||
|
||||
val title = if (acc.nickName.value != "")
|
||||
acc.nickName.value
|
||||
else
|
||||
acc.aor.substringAfter(":")
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.imePadding()
|
||||
.safeDrawingPadding(),
|
||||
containerColor = LocalCustomColors.current.background,
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = title,
|
||||
color = LocalCustomColors.current.light,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
},
|
||||
colors = TopAppBarDefaults.mediumTopAppBarColors(
|
||||
containerColor = LocalCustomColors.current.primary
|
||||
),
|
||||
navigationIcon = {
|
||||
IconButton(onClick = navigateBack) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "Back",
|
||||
tint = LocalCustomColors.current.light
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = {
|
||||
updateAccount()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Check,
|
||||
tint = LocalCustomColors.current.light,
|
||||
contentDescription = "Check"
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
content = { contentPadding ->
|
||||
if (isConfigLoaded)
|
||||
AccountContent(this, contentPadding)
|
||||
else
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AccountContent(ctx: Context, contentPadding: PaddingValues) {
|
||||
|
||||
oldNickname = acc.nickName.value
|
||||
newNickname = oldNickname
|
||||
@@ -240,75 +339,6 @@ class AccountActivity : ComponentActivity() {
|
||||
oldDefaultAccount = UserAgent.findAorIndex(aor)!! == 0
|
||||
newDefaultAccount = oldDefaultAccount
|
||||
|
||||
setContent {
|
||||
AppTheme {
|
||||
keyboardController = LocalSoftwareKeyboardController.current
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = LocalCustomColors.current.background
|
||||
) {
|
||||
AccountScreen { goBack() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AccountScreen(navigateBack: () -> Unit) {
|
||||
val title = if (acc.nickName.value != "")
|
||||
acc.nickName.value
|
||||
else
|
||||
acc.aor.substringAfter(":")
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.imePadding()
|
||||
.safeDrawingPadding(),
|
||||
containerColor = LocalCustomColors.current.background,
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
text = title,
|
||||
color = LocalCustomColors.current.light,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
},
|
||||
colors = TopAppBarDefaults.mediumTopAppBarColors(
|
||||
containerColor = LocalCustomColors.current.primary
|
||||
),
|
||||
navigationIcon = {
|
||||
IconButton(onClick = navigateBack) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "Back",
|
||||
tint = LocalCustomColors.current.light
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = {
|
||||
updateAccount()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Check,
|
||||
tint = LocalCustomColors.current.light,
|
||||
contentDescription = "Check"
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
content = { contentPadding ->
|
||||
AccountContent(this, contentPadding)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AccountContent(ctx: Context, contentPadding: PaddingValues) {
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
if (showAlert.value) {
|
||||
@@ -1680,4 +1710,124 @@ class AccountActivity : ComponentActivity() {
|
||||
return Utils.checkHostPortParams(uri.substring(4))
|
||||
}
|
||||
|
||||
|
||||
private fun initAccountFromConfig(acc: Account, onConfigLoaded: () -> Unit) {
|
||||
val scope = CoroutineScope(Job() + Dispatchers.Main)
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val url = "https://${Utils.uriHostPart(acc.aor)}/baresip/account_config.xml"
|
||||
val config = try {
|
||||
URL(url).readText()
|
||||
} catch (e: java.lang.Exception) {
|
||||
Log.d(TAG, "Failed to get account configuration from network: ${e.message}")
|
||||
null
|
||||
}
|
||||
if (config != null) {
|
||||
Log.d(TAG, "Got account config '$config'")
|
||||
val parserFactory: XmlPullParserFactory = XmlPullParserFactory.newInstance()
|
||||
val parser: XmlPullParser = parserFactory.newPullParser()
|
||||
parser.setInput(StringReader(config))
|
||||
var tag: String?
|
||||
var text = ""
|
||||
var event = parser.eventType
|
||||
val audioCodecs = ArrayList(Api.audio_codecs().split(","))
|
||||
val videoCodecs = ArrayList(Api.video_codecs().split(","))
|
||||
|
||||
while (event != XmlPullParser.END_DOCUMENT) {
|
||||
tag = parser.name
|
||||
when (event) {
|
||||
XmlPullParser.TEXT ->
|
||||
text = parser.text
|
||||
|
||||
XmlPullParser.START_TAG -> {
|
||||
if (tag == "audio-codecs")
|
||||
acc.audioCodec.clear()
|
||||
if (tag == "video-codecs")
|
||||
acc.videoCodec.clear()
|
||||
}
|
||||
|
||||
XmlPullParser.END_TAG ->
|
||||
when (tag) {
|
||||
"outbound-proxy-1" ->
|
||||
if (text.isNotEmpty())
|
||||
acc.outbound.add(text)
|
||||
|
||||
"outbound-proxy-2" ->
|
||||
if (text.isNotEmpty())
|
||||
acc.outbound.add(text)
|
||||
|
||||
"registration-interval" ->
|
||||
acc.configuredRegInt = text.toInt()
|
||||
|
||||
"register" ->
|
||||
acc.regint = if (text == "yes") acc.configuredRegInt else 0
|
||||
|
||||
"audio-codec" ->
|
||||
if (text in audioCodecs)
|
||||
acc.audioCodec.add(text)
|
||||
|
||||
"video-codec" ->
|
||||
if (text in videoCodecs)
|
||||
acc.videoCodec.add(text)
|
||||
|
||||
"media-encoding" -> {
|
||||
val enc = text.lowercase(Locale.ROOT)
|
||||
if (enc in mediaEncMap.keys && enc.isNotEmpty())
|
||||
acc.mediaEnc = enc
|
||||
}
|
||||
|
||||
"media-nat" -> {
|
||||
val nat = text.lowercase(Locale.ROOT)
|
||||
if (nat in mediaNatMap.keys && nat.isNotEmpty())
|
||||
acc.mediaNat = nat
|
||||
}
|
||||
|
||||
"stun-turn-server" ->
|
||||
if (text.isNotEmpty())
|
||||
acc.stunServer = text
|
||||
|
||||
"rtcp-mux" ->
|
||||
acc.rtcpMux = text == "yes"
|
||||
|
||||
"100rel-mode" ->
|
||||
acc.rel100Mode = if (text == "yes")
|
||||
Api.REL100_ENABLED
|
||||
else
|
||||
Api.REL100_DISABLED
|
||||
|
||||
"dtmf-mode" ->
|
||||
if (text in arrayOf("rtp-event", "sip-info", "auto"))
|
||||
acc.dtmfMode = when (text) {
|
||||
"rtp-event" -> Api.DTMFMODE_RTP_EVENT
|
||||
"sip-info" -> Api.DTMFMODE_SIP_INFO
|
||||
else -> Api.DTMFMODE_AUTO
|
||||
}
|
||||
|
||||
"answer-mode" ->
|
||||
if (text in arrayOf("manual", "auto"))
|
||||
acc.answerMode = if (text == "manual")
|
||||
Api.ANSWERMODE_MANUAL
|
||||
else
|
||||
Api.ANSWERMODE_AUTO
|
||||
|
||||
"redirect-mode" ->
|
||||
acc.autoRedirect = text == "yes"
|
||||
|
||||
"voicemail-uri" ->
|
||||
if (text.isNotEmpty())
|
||||
acc.vmUri = text
|
||||
|
||||
"country-code" ->
|
||||
acc.countryCode = text
|
||||
|
||||
"tel-provider" ->
|
||||
acc.telProvider = text
|
||||
}
|
||||
}
|
||||
event = parser.next()
|
||||
}
|
||||
}
|
||||
onConfigLoaded()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -62,15 +62,6 @@ import androidx.compose.ui.unit.sp
|
||||
import com.tutpro.baresip.CustomElements.AlertDialog
|
||||
import com.tutpro.baresip.CustomElements.LabelText
|
||||
import com.tutpro.baresip.CustomElements.verticalScrollbar
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import org.xmlpull.v1.XmlPullParserFactory
|
||||
import java.io.StringReader
|
||||
import java.net.URL
|
||||
import java.util.Locale
|
||||
|
||||
class AccountsActivity : ComponentActivity() {
|
||||
|
||||
@@ -80,7 +71,6 @@ class AccountsActivity : ComponentActivity() {
|
||||
|
||||
private var showAccounts = mutableStateOf(true)
|
||||
private var lastClick: Long = 0
|
||||
private val scope = CoroutineScope(Job() + Dispatchers.Main)
|
||||
|
||||
private val alertTitle = mutableStateOf("")
|
||||
private val alertMessage = mutableStateOf("")
|
||||
@@ -320,6 +310,7 @@ class AccountsActivity : ComponentActivity() {
|
||||
val i = Intent(ctx, AccountActivity::class.java)
|
||||
val b = Bundle()
|
||||
b.putString("aor", account.aor)
|
||||
b.putString("kind", "new")
|
||||
i.putExtras(b)
|
||||
startActivity(i)
|
||||
newAor = ""
|
||||
@@ -366,110 +357,11 @@ class AccountsActivity : ComponentActivity() {
|
||||
// Api.account_debug(ua.account.accp)
|
||||
val acc = ua.account
|
||||
Log.d(TAG, "Allocated UA ${ua.uap} with SIP URI ${acc.luri}")
|
||||
initAccountFromConfig(this@AccountsActivity, acc)
|
||||
saveAccounts()
|
||||
|
||||
return acc
|
||||
}
|
||||
|
||||
private fun initAccountFromConfig(ctx: AccountsActivity, acc: Account) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val url = "https://${Utils.uriHostPart(acc.aor)}/baresip/account_config.xml"
|
||||
val config = try {
|
||||
URL(url).readText()
|
||||
} catch (e: java.lang.Exception) {
|
||||
Log.d(TAG, "Failed to get account configuration from network: ${e.message}")
|
||||
null
|
||||
}
|
||||
if (config != null && !ctx.isFinishing) {
|
||||
Log.d(TAG, "Got account config '$config'")
|
||||
val parserFactory: XmlPullParserFactory = XmlPullParserFactory.newInstance()
|
||||
val parser: XmlPullParser = parserFactory.newPullParser()
|
||||
parser.setInput(StringReader(config))
|
||||
var tag: String?
|
||||
var text = ""
|
||||
var event = parser.eventType
|
||||
val audioCodecs = ArrayList(Api.audio_codecs().split(","))
|
||||
val videoCodecs = ArrayList(Api.video_codecs().split(","))
|
||||
while (event != XmlPullParser.END_DOCUMENT) {
|
||||
tag = parser.name
|
||||
when (event) {
|
||||
XmlPullParser.TEXT ->
|
||||
text = parser.text
|
||||
XmlPullParser.START_TAG -> {
|
||||
if (tag == "audio-codecs")
|
||||
acc.audioCodec.clear()
|
||||
if (tag == "video-codecs")
|
||||
acc.videoCodec.clear()
|
||||
}
|
||||
XmlPullParser.END_TAG ->
|
||||
when (tag) {
|
||||
"outbound-proxy-1" ->
|
||||
if (text.isNotEmpty())
|
||||
acc.outbound.add(text)
|
||||
"outbound-proxy-2" ->
|
||||
if (text.isNotEmpty())
|
||||
acc.outbound.add(text)
|
||||
"registration-interval" ->
|
||||
acc.configuredRegInt = text.toInt()
|
||||
"register" ->
|
||||
acc.regint = if (text == "yes") acc.configuredRegInt else 0
|
||||
"audio-codec" ->
|
||||
if (text in audioCodecs)
|
||||
acc.audioCodec.add(text)
|
||||
"video-codec" ->
|
||||
if (text in videoCodecs)
|
||||
acc.videoCodec.add(text)
|
||||
"media-encoding" -> {
|
||||
val enc = text.lowercase(Locale.ROOT)
|
||||
if (enc in mediaEncMap.keys && enc.isNotEmpty())
|
||||
acc.mediaEnc = enc
|
||||
}
|
||||
"media-nat" -> {
|
||||
val nat = text.lowercase(Locale.ROOT)
|
||||
if (nat in mediaNatMap.keys && nat.isNotEmpty())
|
||||
acc.mediaNat = nat
|
||||
}
|
||||
"stun-turn-server" ->
|
||||
if (text.isNotEmpty())
|
||||
acc.stunServer = text
|
||||
"rtcp-mux" ->
|
||||
acc.rtcpMux = text == "yes"
|
||||
"100rel-mode" ->
|
||||
acc.rel100Mode = if (text == "yes")
|
||||
Api.REL100_ENABLED
|
||||
else
|
||||
Api.REL100_DISABLED
|
||||
"dtmf-mode" ->
|
||||
if (text in arrayOf("rtp-event", "sip-info", "auto"))
|
||||
acc.dtmfMode = when (text) {
|
||||
"rtp-event" -> Api.DTMFMODE_RTP_EVENT
|
||||
"sip-info" -> Api.DTMFMODE_SIP_INFO
|
||||
else -> Api.DTMFMODE_AUTO
|
||||
}
|
||||
"answer-mode" ->
|
||||
if (text in arrayOf("manual", "auto"))
|
||||
acc.answerMode = if (text == "manual")
|
||||
Api.ANSWERMODE_MANUAL
|
||||
else
|
||||
Api.ANSWERMODE_AUTO
|
||||
"redirect-mode" ->
|
||||
acc.autoRedirect = text == "yes"
|
||||
"voicemail-uri" ->
|
||||
if (text.isNotEmpty())
|
||||
acc.vmUri = text
|
||||
"country-code" ->
|
||||
acc.countryCode = text
|
||||
"tel-provider" ->
|
||||
acc.telProvider = text
|
||||
}
|
||||
}
|
||||
event = parser.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun goBack() {
|
||||
BaresipService.activities.remove("accounts,$aor")
|
||||
setResult(RESULT_CANCELED, Intent())
|
||||
|
||||
Reference in New Issue
Block a user