Renamed AndroidContactScreen to ContactScreen and use it for both contact types

This commit is contained in:
Juha Heinanen
2026-05-19 06:46:52 +03:00
parent b12d657ee1
commit 5ebfccfe8c
6 changed files with 230 additions and 257 deletions

View File

@ -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<String>, onUrisChange: (List<String>) -> 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)
)
}
}
}
}

View File

@ -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)

View File

@ -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<String>
abstract fun email(): String
class BaresipContact(var name: String, val uris: ArrayList<String>, 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<String>()
}
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<String>, 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<String>(), "", 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
}

View File

@ -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(
}
}
}

View File

@ -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
}
)
)

View File

@ -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)