From 5ebfccfe8c1134a75f3f307987b3834cc82bcd38 Mon Sep 17 00:00:00 2001 From: Juha Heinanen Date: Tue, 19 May 2026 06:46:52 +0300 Subject: [PATCH] Renamed AndroidContactScreen to ContactScreen and use it for both contact types --- .../tutpro/baresip/BaresipContactScreen.kt | 31 ++-- .../com/tutpro/baresip/BaresipService.kt | 40 ++++ .../main/kotlin/com/tutpro/baresip/Contact.kt | 92 +++++----- ...droidContactScreen.kt => ContactScreen.kt} | 148 +++++++++++---- .../com/tutpro/baresip/ContactsScreen.kt | 172 ++---------------- .../kotlin/com/tutpro/baresip/MainActivity.kt | 4 +- 6 files changed, 230 insertions(+), 257 deletions(-) rename app/src/main/kotlin/com/tutpro/baresip/{AndroidContactScreen.kt => ContactScreen.kt} (66%) diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipContactScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipContactScreen.kt index 3360d1ae..359c2900 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BaresipContactScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipContactScreen.kt @@ -46,6 +46,7 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalMinimumInteractiveComponentSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold @@ -54,6 +55,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.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -495,19 +497,22 @@ private fun ContactUris(uris: List, onUrisChange: (List) -> Unit keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) ) if (uris.size > 1) { - IconButton( - onClick = { - val newList = uris.toMutableList() - newList.removeAt(index) - onUrisChange(newList.toList()) - }, - modifier = Modifier.padding(start = 4.dp) - ) { - Icon( - imageVector = Icons.Filled.Remove, - contentDescription = "Remove", - tint = MaterialTheme.colorScheme.error - ) + CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) { + IconButton( + onClick = { + val newList = uris.toMutableList() + newList.removeAt(index) + onUrisChange(newList.toList()) + }, + modifier = Modifier.padding(start = 2.dp) + ) { + Icon( + imageVector = Icons.Filled.Remove, + contentDescription = "Remove", + tint = MaterialTheme.colorScheme.error, + modifier = Modifier.size(32.dp) + ) + } } } } diff --git a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt index 0d5e4633..90b5c5a4 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/BaresipService.kt @@ -104,6 +104,7 @@ class BaresipService: Service() { private lateinit var wifiLock: WifiManager.WifiLock private lateinit var bluetoothReceiver: BroadcastReceiver private lateinit var hotSpotReceiver: BroadcastReceiver + private lateinit var airplaneModeReceiver: BroadcastReceiver private lateinit var androidContactsObserver: ContentObserver private lateinit var networkCallback: ConnectivityManager.NetworkCallback private lateinit var stopState: String @@ -120,6 +121,7 @@ class BaresipService: Service() { private var androidContactsObserverRegistered = false private var hotSpotReceiverRegistered = false private var bluetoothReceiverRegistered = false + private var airplaneModeReceiverRegistered = false private var isNotificationInCall = false private var isServiceClean = false private var cleanupRunnable: Runnable? = null @@ -288,6 +290,23 @@ class BaresipService: Service() { ) hotSpotReceiverRegistered = true + airplaneModeReceiver = object : BroadcastReceiver() { + override fun onReceive(ctx: Context, intent: Intent) { + if (intent.action == Intent.ACTION_AIRPLANE_MODE_CHANGED) { + val isAirplaneModeOn = intent.getBooleanExtra("state", false) + Log.d(TAG, "Airplane mode changed: $isAirplaneModeOn") + updateMobileStatus(isAirplaneModeOn) + } + } + } + ContextCompat.registerReceiver( + this, + airplaneModeReceiver, + IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED), + ContextCompat.RECEIVER_EXPORTED + ) + airplaneModeReceiverRegistered = true + tm = getSystemService(TELECOM_SERVICE) as TelecomManager btm = getSystemService(BLUETOOTH_SERVICE) as BluetoothManager @@ -2027,6 +2046,8 @@ class BaresipService: Service() { account.regint = 0 account.telProvider = "" val mobileUa = UserAgent(0L, account) + val isAirplaneModeOn = Settings.Global.getInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0) != 0 + mobileUa.status = if (isAirplaneModeOn) R.drawable.circle_white else circleGreen.getValue(colorblind) val updatedUas = uas.value.toMutableList() updatedUas.add(mobileUa) @@ -2042,6 +2063,19 @@ class BaresipService: Service() { } } + private fun updateMobileStatus(isAirplaneModeOn: Boolean) { + uas.value.find { it.account.isMobile }?.let { ua -> + val status = if (isAirplaneModeOn) + R.drawable.circle_white + else + circleGreen.getValue(colorblind) + ua.updateStatus(status) + updateStatusNotification() + if (isMainVisible) + registrationUpdate.postValue(System.currentTimeMillis()) + } + } + @SuppressLint("FullScreenIntentPolicy") fun handleIncomingCall(call: Call) { val ua = call.ua @@ -2635,6 +2669,12 @@ class BaresipService: Service() { bluetoothReceiverRegistered = false } catch (_: IllegalArgumentException) {} } + if (airplaneModeReceiverRegistered) { + try { + unregisterReceiver(airplaneModeReceiver) + airplaneModeReceiverRegistered = false + } catch (_: IllegalArgumentException) {} + } val callps = ConnectionService.connections.keys.toList() for (callp in callps) ConnectionService.onCallClosed(callp) diff --git a/app/src/main/kotlin/com/tutpro/baresip/Contact.kt b/app/src/main/kotlin/com/tutpro/baresip/Contact.kt index 91169fd2..d5276445 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/Contact.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/Contact.kt @@ -13,45 +13,38 @@ import java.util.ArrayList sealed class Contact { + abstract fun name(): String + abstract fun id(): Long + abstract fun favorite(): Boolean + abstract fun colorInt(): Int + abstract fun uris(): List + abstract fun email(): String + class BaresipContact(var name: String, val uris: ArrayList, var email: String, var color: Int, var id: Long, var favorite: Boolean): Contact() { var avatarImage: Bitmap? = null + override fun name() = name + override fun id() = id + override fun favorite() = favorite + override fun colorInt() = color + override fun uris() = uris + override fun email() = email } - class AndroidContact(var name: String, var color: Int, var thumbnailUri: Uri?, - var id: Long, var favorite: Boolean): Contact() { - val uris = ArrayList() - } - - fun name(): String { - return when (this) { - is AndroidContact -> name - is BaresipContact -> name - } - } - - fun id(): Long { - return when (this) { - is AndroidContact -> id - is BaresipContact -> id - } - } - - fun favorite(): Boolean { - return when (this) { - is AndroidContact -> favorite - is BaresipContact -> favorite - } + class AndroidContact(var name: String, val uris: ArrayList, var email: String, var color: Int, + var thumbnailUri: Uri?, var id: Long, var favorite: Boolean): Contact() { + override fun name() = name + override fun id() = id + override fun favorite() = favorite + override fun colorInt() = color + override fun uris() = uris + override fun email() = email } fun color(): String { - val intColor = when (this) { - is AndroidContact -> color - is BaresipContact -> color - } // Mask with 0xFFFFFF to ensure we get the standard 6-character hex code (RRGGBB) // ignoring the alpha channel for compatibility with simple string parsers. - return String.format("#%06X", 0xFFFFFF and intColor) + return String.format("#%06X", 0xFFFFFF and colorInt()) } fun copy(): Contact { @@ -59,14 +52,10 @@ sealed class Contact { is BaresipContact -> BaresipContact(name, ArrayList(uris), email, color, id, favorite) is AndroidContact -> - AndroidContact(name, color, thumbnailUri, id, favorite) - } - when (this) { - is BaresipContact -> - (copy as BaresipContact).avatarImage = this.avatarImage - is AndroidContact -> - (copy as AndroidContact).uris.addAll(this.uris) + AndroidContact(name, ArrayList(uris), email, color, thumbnailUri, id, favorite) } + if (this is BaresipContact) + (copy as BaresipContact).avatarImage = this.avatarImage return copy } @@ -175,7 +164,8 @@ sealed class Contact { ContactsContract.Contacts.STARRED) val selection = ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE + "' OR " + - ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "'" + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "' OR " + + ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE + "'" val cur: Cursor? = ctx.contentResolver.query(ContactsContract.Data.CONTENT_URI, projection, selection, null, null) BaresipService.androidContacts.value = listOf() @@ -184,27 +174,33 @@ sealed class Contact { val id = cur.getLong(0) val name = cur.getString(1) ?: "" val mime = cur.getString(2) - val data = cur.getString(3) + val data = cur.getString(3) ?: continue val thumb = cur.getString(4)?.toUri() val starred = cur.getInt(5) val contact = if (contacts.containsKey(id)) contacts[id]!! else - AndroidContact(name, Utils.randomColor(), thumb, id, starred == 1) + AndroidContact(name, ArrayList(), "", Utils.randomColor(), thumb, id, starred == 1) if (contact.name == "" && name != "") contact.name = name if (contact.thumbnailUri == null && thumb != null) contact.thumbnailUri = thumb - if (mime == ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) { - val uri = "tel:${data.filterNot { setOf('-', ' ', '(', ')').contains(it) }}" - if (uri !in contact.uris) - contact.uris.add(uri) - // contact.types.add(typeToString(cur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE))) + when (mime) { + ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE -> { + val uri = "tel:${data.filterNot { setOf('-', ' ', '(', ')').contains(it) }}" + if (uri !in contact.uris) + contact.uris.add(uri) + } + ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE -> { + val uri = "sip:$data" + if (uri !in contact.uris) + contact.uris.add(uri) + } + ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE -> { + if (contact.email == "") + contact.email = data + } } - else if (mime == ContactsContract.CommonDataKinds.SipAddress.CONTENT_ITEM_TYPE) - contact.uris.add("sip:$data") - else - continue if (!contacts.containsKey(id)) contacts[id] = contact } diff --git a/app/src/main/kotlin/com/tutpro/baresip/AndroidContactScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/ContactScreen.kt similarity index 66% rename from app/src/main/kotlin/com/tutpro/baresip/AndroidContactScreen.kt rename to app/src/main/kotlin/com/tutpro/baresip/ContactScreen.kt index 7a91ffd5..e5438deb 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/AndroidContactScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ContactScreen.kt @@ -2,8 +2,10 @@ package com.tutpro.baresip import android.content.Context import android.content.Intent +import android.graphics.Bitmap import android.net.Uri import androidx.compose.foundation.Canvas +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -18,14 +20,14 @@ import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.statusBars -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.Chat import androidx.compose.material.icons.outlined.Call +import androidx.compose.material.icons.outlined.Email import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -40,10 +42,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController @@ -52,10 +56,11 @@ import androidx.navigation.NavType import androidx.navigation.compose.composable import androidx.navigation.navArgument import coil.compose.AsyncImage +import androidx.core.net.toUri -fun NavGraphBuilder.androidContactScreenRoute(navController: NavController, viewModel: ViewModel) { +fun NavGraphBuilder.contactScreenRoute(navController: NavController, viewModel: ViewModel) { composable( - route = "android_contact/{name}", + route = "contact/{name}", arguments = listOf(navArgument("name") { type = NavType.StringType }) ) { backStackEntry -> val ctx = LocalContext.current @@ -71,10 +76,11 @@ fun NavGraphBuilder.androidContactScreenRoute(navController: NavController, view @Composable private fun ContactScreen(ctx: Context, viewModel: ViewModel, navController: NavController, name: String) { - val contact = Contact.androidContact(name) + val contact = Contact.baresipContact(name) ?: Contact.androidContact(name) if (contact == null) { - Log.e(TAG, "No Android contact found with name $name") + Log.e(TAG, "No contact found with name $name") navController.navigateUp() + return } Scaffold( modifier = Modifier.fillMaxSize().imePadding(), @@ -90,7 +96,7 @@ private fun ContactScreen(ctx: Context, viewModel: ViewModel, navController: Nav } }, content = { contentPadding -> - ContactContent(ctx, viewModel, navController, contentPadding, contact!!) + ContactContent(ctx, viewModel, navController, contentPadding, contact) } ) } @@ -123,19 +129,23 @@ private fun ContactContent( viewModel: ViewModel, navController: NavController, contentPadding: PaddingValues, - contact: Contact.AndroidContact + contact: Contact ) { + val scrollState = rememberScrollState() Column( modifier = Modifier .fillMaxWidth() .background(MaterialTheme.colorScheme.background) .padding(contentPadding) - .padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 52.dp), + .padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 52.dp) + .verticalScroll(scrollState), verticalArrangement = Arrangement.spacedBy(12.dp), ) { Avatar(contact) - ContactName(contact.name) + ContactName(contact.name()) Uris(ctx, viewModel, navController, contact) + if (contact.email().isNotEmpty()) + Email(ctx, contact.email()) } } @@ -153,7 +163,19 @@ private fun TextAvatar(text: String, color: Int) { } @Composable -private fun ImageAvatar(uri: Uri) { +private fun ImageAvatar(bitmap: Bitmap) { + Image( + bitmap = bitmap.asImageBitmap(), + contentDescription = "Avatar", + contentScale = ContentScale.Crop, + modifier = Modifier + .size(avatarSize.dp) + .clip(CircleShape) + ) +} + +@Composable +private fun AsyncImageAvatar(uri: Uri) { AsyncImage( model = uri, contentDescription = stringResource(R.string.avatar_image), @@ -163,19 +185,28 @@ private fun ImageAvatar(uri: Uri) { } @Composable -private fun Avatar(contact: Contact.AndroidContact) { +private fun Avatar(contact: Contact) { Row( Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center ) { - val color = contact.color - val name = contact.name - val thumbnailUri = contact.thumbnailUri - if (thumbnailUri != null) - ImageAvatar(thumbnailUri) - else - TextAvatar(if (name == "") "" else name[0].toString(), color) + val color = contact.colorInt() + val name = contact.name() + when (contact) { + is Contact.BaresipContact -> { + if (contact.avatarImage != null) + ImageAvatar(contact.avatarImage!!) + else + TextAvatar(if (name == "") "" else name[0].toString(), color) + } + is Contact.AndroidContact -> { + if (contact.thumbnailUri != null) + AsyncImageAvatar(contact.thumbnailUri!!) + else + TextAvatar(if (name == "") "" else name[0].toString(), color) + } + } } } @@ -184,9 +215,48 @@ private fun ContactName(name: String) { Row( Modifier.fillMaxWidth().padding(top = 16.dp, bottom = 8.dp), verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Text( + text = name, + fontSize = 24.sp, + color = MaterialTheme.colorScheme.onBackground, + textAlign = TextAlign.Center + ) + } +} + +@Composable +private fun Email(ctx: Context, email: String) { + Row( + Modifier.fillMaxWidth().padding(bottom = 12.dp), + verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { - Text(name, fontSize = 24.sp, color = MaterialTheme.colorScheme.onBackground) + Text( + text = stringResource(R.string.bullet_item, email), + modifier = Modifier.weight(1f), + fontSize = 18.sp, + color = MaterialTheme.colorScheme.onBackground, + ) + IconButton( + onClick = { + val emailIntent = Intent(Intent.ACTION_SENDTO).apply { + data = "mailto:$email".toUri() + } + try { + ctx.startActivity(emailIntent) + } catch (e: Exception) { + Log.e(TAG, "Failed to start email activity: ${e.message}") + } + } + ) { + Icon( + imageVector = Icons.Outlined.Email, + contentDescription = "Send Email", + tint = MaterialTheme.colorScheme.onBackground + ) + } } } @@ -195,23 +265,18 @@ private fun Uris( ctx: Context, viewModel: ViewModel, navController: NavController, - contact: Contact.AndroidContact + contact: Contact ) { - val lazyListState = rememberLazyListState() - LazyColumn( - modifier = Modifier - .fillMaxWidth() - .padding(start = 16.dp, end = 4.dp) - .background(MaterialTheme.colorScheme.background), - state = lazyListState, + Column( + modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(12.dp), ) { - items(contact.uris) { uri -> + for (uri in contact.uris()) { Row( verticalAlignment = Alignment.CenterVertically, ) { Text( - text = uri.substringAfter(":"), + text = stringResource(R.string.bullet_item, uri.substringAfter(":")), modifier = Modifier.weight(1f), fontSize = 18.sp, color = MaterialTheme.colorScheme.onBackground, @@ -225,13 +290,18 @@ private fun Uris( if (ua == null) Log.w(TAG, "Message clickable did not find AoR $aor") else { - val intent = Intent(ctx, MainActivity::class.java) - intent.putExtra("uap", ua.uap) - intent.putExtra("peer", uri) - handleIntent(ctx, viewModel, intent, "message") - navController.navigate("main") { - popUpTo("main") { inclusive = false } - launchSingleTop = true + if (ua.account.isMobile && !Utils.isDefaultSmsApp(ctx)) { + handleDialog(ctx, ctx.getString(R.string.notice), + ctx.getString(R.string.enable_default_messaging)) + } else { + val intent = Intent(ctx, MainActivity::class.java) + intent.putExtra("uap", ua.uap) + intent.putExtra("peer", uri) + handleIntent(ctx, viewModel, intent, "message") + navController.navigate("main") { + popUpTo("main") { inclusive = false } + launchSingleTop = true + } } } } @@ -272,5 +342,3 @@ private fun Uris( } } } - - diff --git a/app/src/main/kotlin/com/tutpro/baresip/ContactsScreen.kt b/app/src/main/kotlin/com/tutpro/baresip/ContactsScreen.kt index c1be18ee..9c02dd62 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/ContactsScreen.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/ContactsScreen.kt @@ -84,25 +84,18 @@ import com.tutpro.baresip.CustomElements.TextAvatar import com.tutpro.baresip.CustomElements.verticalScrollbar import java.io.File import java.io.IOException -import androidx.core.net.toUri const val avatarSize: Int = 96 -fun NavGraphBuilder.contactsScreenRoute( - navController: NavController, - viewModel: ViewModel -) { +fun NavGraphBuilder.contactsScreenRoute(navController: NavController) { composable("contacts") { _ -> - ContactsScreen(navController = navController, viewModel = viewModel) + ContactsScreen(navController = navController) } } @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun ContactsScreen( - navController: NavController, - viewModel: ViewModel -) { +private fun ContactsScreen(navController: NavController) { val ctx = LocalContext.current val activity = LocalActivity.current!! @@ -429,7 +422,7 @@ private fun ContactsScreen( ) }, content = { contentPadding -> - ContactsContent(ctx, viewModel, navController, contentPadding, searchContactName) + ContactsContent(ctx, navController, contentPadding, searchContactName) } ) } @@ -497,31 +490,21 @@ private fun BottomBar( @Composable private fun ContactsContent( ctx: Context, - viewModel: ViewModel, navController: NavController, contentPadding: PaddingValues, searchQuery: String ) { - val showDialog = remember { mutableStateOf(false) } - val dialogMessage = remember { mutableStateOf("") } - val secondText = remember { mutableStateOf("") } - val secondAction = remember { mutableStateOf({}) } - val lastText = remember { mutableStateOf("") } + val showConfirmationDialog = remember { mutableStateOf(false) } + val confirmationDialogMessage = remember { mutableStateOf("") } val lastAction = remember { mutableStateOf({}) } - val thirdText = remember { mutableStateOf("") } - val thirdAction = remember { mutableStateOf({}) } - if (showDialog.value) + if (showConfirmationDialog.value) AlertDialog( - showDialog = showDialog, + showDialog = showConfirmationDialog, title = stringResource(R.string.confirmation), - message = dialogMessage.value, + message = confirmationDialogMessage.value, firstButtonText = stringResource(R.string.cancel), - secondButtonText = secondText.value, - onSecondClicked = secondAction.value, - thirdButtonText = thirdText.value, - onThirdClicked = thirdAction.value, - lastButtonText = lastText.value, + lastButtonText = stringResource(R.string.delete), onLastClicked = lastAction.value, ) @@ -638,132 +621,13 @@ private fun ContactsContent( .padding(start = 10.dp) .combinedClickable( onClick = { - val aor = viewModel.selectedAor.value - val ua = UserAgent.ofAor(aor) - val intent = Intent(ctx, MainActivity::class.java) - if (ua != null) { - intent.putExtra("uap", ua.uap) - } - else - Log.w(TAG, "onClickListener did not find UA for $aor") - val peerName = contact.name() - if (contact.email.isNotEmpty()) - dialogMessage.value = String.format( - ctx.getString(R.string.contact_email_action_question), - peerName - ) - else - dialogMessage.value = String.format( - ctx.getString(R.string.contact_action_question), - peerName - ) - secondText.value = ctx.getString(R.string.call) - secondAction.value = { - if (ua != null) { - val uris = if (ua.account.isMobile) - contact.uris.filter { it.startsWith("tel:") } - else - contact.uris - if (uris.size > 1) { - CustomElements.selectItems.value = uris - CustomElements.selectItemAction.value = { index: Int -> - intent.putExtra("peer", uris[index]) - handleIntent(ctx, viewModel, intent, "call") - navController.navigate("main") { - popUpTo("main") - launchSingleTop = true - } - CustomElements.showSelectItemDialog.value = false - } - CustomElements.showSelectItemDialog.value = true - } - else if (uris.size == 1) { - intent.putExtra("peer", uris[0]) - handleIntent(ctx, viewModel, intent, "call") - navController.navigate("main") { - popUpTo("main") - launchSingleTop = true - } - } - else { - alertTitle.value = ctx.getString(R.string.notice) - alertMessage.value = ctx.getString(R.string.no_telephone_number) - showAlert.value = true - } - } - } - thirdText.value = ctx.getString(R.string.send_message) - thirdAction.value = { - if (ua != null) { - val uris = if (ua.account.isMobile) - contact.uris.filter { it.startsWith("tel:") } - else - contact.uris - - if (uris.size > 1) { - CustomElements.selectItems.value = uris - CustomElements.selectItemAction.value = { index: Int -> - intent.putExtra("peer", uris[index]) - if (ua.account.isMobile) { - if (!Utils.isDefaultSmsApp(ctx)) { - alertTitle.value = ctx.getString(R.string.notice) - alertMessage.value = ctx.getString(R.string.enable_default_messaging) - showAlert.value = true - } else { - handleIntent(ctx, viewModel, intent, "message") - navController.navigateUp() - } - } - else { - handleIntent(ctx, viewModel, intent, "message") - navController.navigateUp() - } - CustomElements.showSelectItemDialog.value = false - } - CustomElements.showSelectItemDialog.value = true - } else if (uris.size == 1) { - intent.putExtra("peer", uris[0]) - if (ua.account.isMobile) { - if (!Utils.isDefaultSmsApp(ctx)) { - alertTitle.value = ctx.getString(R.string.notice) - alertMessage.value = ctx.getString(R.string.enable_default_messaging) - showAlert.value = true - } else { - handleIntent(ctx, viewModel, intent, "message") - navController.navigateUp() - } - } - else { - handleIntent(ctx, viewModel, intent, "message") - navController.navigateUp() - } - } - } - } - if (contact.email.isNotEmpty()) { - lastText.value = ctx.getString(R.string.send_email) - lastAction.value = { - val emailIntent = Intent(Intent.ACTION_SENDTO).apply { - data = "mailto:${contact.email}".toUri() - } - try { - ctx.startActivity(emailIntent) - } catch (e: Exception) { - Log.e(TAG, "Failed to start email activity: ${e.message}") - } - } - } else { - lastText.value = "" - } - showDialog.value = true + navController.navigate("contact/${contact.name()}") }, onLongClick = { - dialogMessage.value = String.format( + confirmationDialogMessage.value = String.format( ctx.getString(R.string.contact_delete_question), contact.name() ) - secondText.value = "" - lastText.value = ctx.getString(R.string.delete) lastAction.value = { val id = contact.id val avatarFile = File( @@ -782,7 +646,7 @@ private fun ContactsContent( } Contact.removeBaresipContact(contact) } - showDialog.value = true + showConfirmationDialog.value = true } ) ) @@ -808,14 +672,14 @@ private fun ContactsContent( .weight(1f) .padding(start = 10.dp, top = 4.dp, bottom = 4.dp) .combinedClickable( - onClick = { navController.navigate("android_contact/${contact.name()}") }, + onClick = { + navController.navigate("contact/${contact.name()}") + }, onLongClick = { - dialogMessage.value = String.format( + confirmationDialogMessage.value = String.format( ctx.getString(R.string.contact_delete_question), contact.name() ) - secondText.value = "" - lastText.value = ctx.getString(R.string.delete) lastAction.value = { ctx.contentResolver.delete( ContactsContract.RawContacts.CONTENT_URI, @@ -823,7 +687,7 @@ private fun ContactsContent( null ) } - showDialog.value = true + showConfirmationDialog.value = true } ) ) diff --git a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt index 3d7f0ef5..542a7e1d 100644 --- a/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt +++ b/app/src/main/kotlin/com/tutpro/baresip/MainActivity.kt @@ -285,9 +285,9 @@ class MainActivity : ComponentActivity() { audioScreenRoute(navController) accountScreenRoute(navController) codecsScreenRoute(navController) - contactsScreenRoute(navController, viewModel) + contactsScreenRoute(navController) baresipContactScreenRoute(navController) - androidContactScreenRoute(navController, viewModel) + contactScreenRoute(navController, viewModel) callsScreenRoute(navController, viewModel) callDetailsScreenRoute(navController, viewModel) blockedScreenRoute(navController)