diff --git a/app/src/main/kotlin/com/tutpro/baresip/Account.kt b/app/src/main/kotlin/com/tutpro/baresip/Account.kt index c8ab2b34..6e7fc6c9 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Account.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Account.kt @@ -90,7 +90,7 @@ class Account(val accp: Long, virtualAor: String? = null) { fun print() : String { var res = if (isMobile) { - "<${aor};transport=udp>" + "" } else { if (displayName != "") "\"${displayName}\" " diff --git a/app/src/main/kotlin/com/tutpro/baresip/AccountScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/AccountScreen.kt index 1868b091..56fb7d97 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/AccountScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/AccountScreen.kt @@ -45,6 +45,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -62,6 +63,10 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.compose.runtime.DisposableEffect +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder @@ -81,6 +86,7 @@ import java.io.StringReader import java.net.URL import java.util.Locale import javax.net.ssl.HttpsURLConnection +import android.telephony.SubscriptionManager fun NavGraphBuilder.accountScreenRoute(navController: NavController) { composable( @@ -126,6 +132,20 @@ private fun AccountScreen( ) { val ua = UserAgent.ofAor(aor)!! val acc = ua.account + val lifecycleOwner = LocalLifecycleOwner.current + var resumeToggle by remember { mutableLongStateOf(0L) } + + DisposableEffect(lifecycleOwner) { + val observer = LifecycleEventObserver { _, event -> + if (event == Lifecycle.Event.ON_RESUME) { + resumeToggle = System.currentTimeMillis() + } + } + lifecycleOwner.lifecycle.addObserver(observer) + onDispose { + lifecycleOwner.lifecycle.removeObserver(observer) + } + } var isAccountAvailable by remember { mutableStateOf(false) } var isAccountLoaded by remember { mutableStateOf(false) } @@ -190,7 +210,7 @@ private fun AccountScreen( } ) { contentPadding -> if (isAccountLoaded) - AccountContent(viewModel, navController, contentPadding, ua) + AccountContent(viewModel, navController, contentPadding, ua, resumeToggle) else Box( modifier = Modifier.fillMaxSize(), @@ -215,7 +235,8 @@ private fun AccountContent( viewModel: AccountViewModel, navController: NavController, contentPadding: PaddingValues, - ua: UserAgent + ua: UserAgent, + resumeToggle: Long ) { val ctx = LocalContext.current val aor = ua.account.aor @@ -224,42 +245,79 @@ private fun AccountContent( val showStun by remember { derivedStateOf { mediaNat.isNotEmpty() } } @Composable - fun AoR() { - Row( + fun AoRField(value: String, label: String) { + OutlinedTextField( + value = value, + enabled = false, + onValueChange = {}, + modifier = Modifier.fillMaxWidth(), + textStyle = TextStyle(fontSize = 18.sp), + label = { + Text( + text = label, + fontWeight = FontWeight.Bold + ) + }, + colors = OutlinedTextFieldDefaults.colors( + disabledTextColor = MaterialTheme.colorScheme.onSurface, + disabledBorderColor = MaterialTheme.colorScheme.outline, + disabledLeadingIconColor = MaterialTheme.colorScheme.onSurfaceVariant, + disabledTrailingIconColor = MaterialTheme.colorScheme.onSurfaceVariant, + disabledLabelColor = MaterialTheme.colorScheme.onSurfaceVariant, + ) + ) + } + + @Composable + fun AoR(toggle: Long) { + Column( Modifier .fillMaxWidth() .padding(top = 8.dp, end = 10.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Start + verticalArrangement = Arrangement.spacedBy(8.dp) ) { - val aorText = if (ua.account.isMobile) { - if (ua.account.aor == "sip:mobile@pstn") - stringResource(R.string.not_available) - else - "tel:${Utils.uriUserPart(ua.account.aor)}" - } else - ua.account.luri + if (ua.account.isMobile && android.os.Build.VERSION.SDK_INT >= 29) { + val voiceSubId = remember(toggle) { SubscriptionManager.getDefaultVoiceSubscriptionId() } + val smsSubId = remember(toggle) { SubscriptionManager.getDefaultSmsSubscriptionId() } - OutlinedTextField( - value = aorText, - enabled = false, - onValueChange = {}, - modifier = Modifier.fillMaxWidth(), - textStyle = TextStyle(fontSize = 18.sp), - label = { - Text( - text = stringResource(if (ua.account.isMobile) R.string.tel_uri else R.string.sip_uri), - fontWeight = FontWeight.Bold + if (voiceSubId != smsSubId && + voiceSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID && + smsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID + ) { + val voiceNumber = remember(voiceSubId, toggle) { Utils.getLine1Number(ctx, voiceSubId) } + AoRField( + value = if (voiceNumber != null) "tel:$voiceNumber" else stringResource(R.string.not_available), + label = stringResource(R.string.tel_uri_calls) ) - }, - colors = OutlinedTextFieldDefaults.colors( - disabledTextColor = MaterialTheme.colorScheme.onSurface, - disabledBorderColor = MaterialTheme.colorScheme.outline, - disabledLeadingIconColor = MaterialTheme.colorScheme.onSurfaceVariant, - disabledTrailingIconColor = MaterialTheme.colorScheme.onSurfaceVariant, - disabledLabelColor = MaterialTheme.colorScheme.onSurfaceVariant, + val smsNumber = remember(smsSubId, toggle) { Utils.getLine1Number(ctx, smsSubId) } + AoRField( + value = if (smsNumber != null) "tel:$smsNumber" else stringResource(R.string.not_available), + label = stringResource(R.string.tel_uri_messages) + ) + } else { + val currentSubId = remember(toggle) { + if (voiceSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) + voiceSubId + else + smsSubId + } + val currentNumber = remember(currentSubId, toggle) { + if (currentSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) + Utils.getLine1Number(ctx, currentSubId) + else + Utils.getLine1Number(ctx) + } + AoRField( + value = if (currentNumber != null) "tel:$currentNumber" else stringResource(R.string.not_available), + label = stringResource(R.string.tel_uri) + ) + } + } else { + AoRField( + value = if (ua.account.isMobile) stringResource(R.string.not_available) else ua.account.luri, + label = stringResource(if (ua.account.isMobile) R.string.tel_uri else R.string.sip_uri) ) - ) + } } } @@ -1259,7 +1317,7 @@ private fun AccountContent( .verticalScroll(state = scrollState), verticalArrangement = Arrangement.spacedBy(8.dp), ) { - AoR() + AoR(resumeToggle) Nickname() if (!ua.account.isMobile) { DisplayName() diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt index 8a83e388..d8c0e62a 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt @@ -2088,21 +2088,9 @@ class BaresipService: Service() { return } - val userPart = Utils.getLine1Number(this) ?: "mobile" - val mobileAor = "sip:$userPart@pstn" + val mobileAor = "sip:mobile@pstn" if (existingMobileUa != null) { - // Update AOR if it previously was sip:mobile@pstn but now a real number - if (existingMobileUa.account.aor == "sip:mobile@pstn" && mobileAor != "sip:mobile@pstn") { - Log.d(TAG, "Updating existing Mobile account AOR to $mobileAor") - val aorField = Account::class.java.getDeclaredField("aor") - aorField.isAccessible = true - aorField.set(existingMobileUa.account, mobileAor) - val luriField = Account::class.java.getDeclaredField("luri") - luriField.isAccessible = true - luriField.set(existingMobileUa.account, mobileAor) - Account.saveAccounts() - } return } diff --git a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt index ef322315..e4c61bdf 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Utils.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Utils.kt @@ -1374,35 +1374,45 @@ object Utils { } } + @RequiresApi(29) @SuppressLint("HardwareIds") - fun getLine1Number(ctx: Context): String? { + fun getLine1Number(ctx: Context, subscriptionId: Int = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID): String? { try { if (Build.VERSION.SDK_INT >= 33) { if (ctx.checkSelfPermission(Manifest.permission.READ_PHONE_NUMBERS) == - PackageManager.PERMISSION_GRANTED) { - val sm = ctx.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager - val number = sm.getPhoneNumber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) + PackageManager.PERMISSION_GRANTED + ) { + val sm = + ctx.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager + val number = sm.getPhoneNumber(subscriptionId) if (number != "") { - Log.d(TAG, "Retrieved SIM number $number via SubscriptionManager") + Log.d(TAG, "Retrieved SIM number $number via SubscriptionManager for $subscriptionId") return number - } - else - Log.d(TAG, "Did not get SIM number via SubscriptionManager") - } - else + } else + Log.d(TAG, "Did not get SIM number via SubscriptionManager for $subscriptionId") + } else Log.d(TAG, "No READ_PHONE_NUMBERS permission") } else { - if (checkPermissions(ctx, arrayOf(Manifest.permission.READ_PHONE_NUMBERS, - Manifest.permission.READ_PHONE_STATE))) { + if (checkPermissions( + ctx, arrayOf( + Manifest.permission.READ_PHONE_NUMBERS, + Manifest.permission.READ_PHONE_STATE + ) + ) + ) { val tm = ctx.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager + val targetTm = if (subscriptionId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) + tm + else + tm.createForSubscriptionId(subscriptionId) + @Suppress("DEPRECATION") - val number = tm.line1Number - if (number != null) { - Log.d(TAG, "Retrieved SIM number $number via TelephonyManager") + val number = targetTm.line1Number + if (number != null && number != "") { + Log.d(TAG, "Retrieved SIM number $number via TelephonyManager for $subscriptionId") return number } - } - else + } else Log.d(TAG, "No READ_PHONE_NUMBERS and/or READ_PHONE_STATE permissions") } } catch (e: Exception) { diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml index 618bc5db..536e2cea 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -113,6 +113,8 @@ Tili + TEL URI Puhelut + TEL URI Viestit Ei saatavana Lempinimi (jos annettu) millä tämä tili identifioidaan baresip sovelluksessa. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8b996136..32385bd9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -116,6 +116,10 @@ Account + SIP URI + TEL URI + TEL URI for Calls + TEL URI for Messages Not available Nickname (if any) used to identify this account within baresip app. @@ -496,8 +500,6 @@ No Accept Deny - SIP URI - TEL URI Add Delete Edit