Composed chats activity alert dialogs

This commit is contained in:
Juha Heinanen
2025-04-12 17:51:28 +03:00
parent 81eca1fe34
commit 874b7c87e5
2 changed files with 236 additions and 141 deletions

View File

@ -1,7 +1,6 @@
package com.tutpro.baresip
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.graphics.BitmapFactory
import android.os.Bundle
@ -77,12 +76,13 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.tutpro.baresip.BaresipService.Companion.contactNames
import com.tutpro.baresip.CustomElements.AlertDialog
import com.tutpro.baresip.CustomElements.ImageAvatar
import com.tutpro.baresip.CustomElements.Text
import com.tutpro.baresip.CustomElements.TextAvatar
import com.tutpro.baresip.CustomElements.verticalScrollbar
import com.tutpro.baresip.CustomElements.SelectableAlertDialog
import java.text.DateFormat
import java.util.GregorianCalendar
@ -92,7 +92,9 @@ class ChatsActivity: ComponentActivity() {
internal lateinit var account: Account
private lateinit var chatRequest: ActivityResultLauncher<Intent>
private var _uaMessages = mutableStateOf<List<Message>>(emptyList())
private var uaMessages : List<Message> by _uaMessages
private var uaMessages: List<Message> by _uaMessages
private var showNotice by mutableStateOf(false)
private var noticeMessage = mutableStateOf("")
private val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
@ -158,8 +160,21 @@ class ChatsActivity: ComponentActivity() {
var expanded by remember { mutableStateOf(false) }
val delete = String.format(getString(R.string.delete))
val disable = String.format(getString(R.string.disable_history))
val enable = String.format(getString(R.string.enable_history))
val showDialog = remember { mutableStateOf(false) }
val positiveAction = remember { mutableStateOf({}) }
AlertDialog(
showDialog = showDialog,
title = getString(R.string.confirmation),
message = String.format(
getString(R.string.delete_chats_alert),
aor.substringAfter(":")
),
positiveButtonText = getString(R.string.delete),
onPositiveClicked = positiveAction.value,
negativeButtonText = getString(R.string.cancel),
)
TopAppBar(
title = {
@ -192,34 +207,22 @@ class ChatsActivity: ComponentActivity() {
tint = LocalCustomColors.current.light
)
}
CustomElements.DropdownMenu(expanded,
CustomElements.DropdownMenu(
expanded,
{ expanded = false },
listOf(delete, if (account.callHistory) disable else enable),
listOf(delete),
onItemClick = { selectedItem ->
expanded = false
when (selectedItem) {
delete ->
with(
MaterialAlertDialogBuilder(
this@ChatsActivity,
R.style.AlertDialogTheme
)
) {
setTitle(R.string.confirmation)
setMessage(String.format(getString(R.string.delete_chats_alert),
aor.substringAfter(":")))
setPositiveButton(delete) { dialog, _ ->
Message.clearMessagesOfAor(aor)
Message.save()
_uaMessages.value = listOf()
account.unreadMessages = false
dialog.dismiss()
}
setNeutralButton(getText(R.string.cancel)) { dialog, _ ->
dialog.dismiss()
}
show()
delete -> {
positiveAction.value = {
Message.clearMessagesOfAor(aor)
Message.save()
_uaMessages.value = listOf()
account.unreadMessages = false
}
showDialog.value = true
}
}
}
)
@ -236,8 +239,10 @@ class ChatsActivity: ComponentActivity() {
verticalArrangement = Arrangement.Top
) {
Account(account)
// Spacer(modifier = Modifier.weight(1f))
Chats(ctx, account)
if (showNotice)
Notice()
}
}
@ -261,6 +266,25 @@ class ChatsActivity: ComponentActivity() {
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Chats(ctx: Context, account: Account) {
val showDialog = remember { mutableStateOf(false) }
val dialogMessage = remember { mutableStateOf("") }
val positiveButtonText = remember { mutableStateOf("") }
val positiveAction = remember { mutableStateOf({}) }
val neutralButtonText = remember { mutableStateOf("") }
val neutralAction = remember { mutableStateOf({}) }
AlertDialog(
showDialog = showDialog,
title = getString(R.string.confirmation),
message = dialogMessage.value,
positiveButtonText = positiveButtonText.value,
onPositiveClicked = positiveAction.value,
neutralButtonText = neutralButtonText.value,
onNeutralClicked = neutralAction.value,
negativeButtonText = getString(R.string.cancel)
)
val lazyListState = rememberLazyListState()
LazyColumn(
modifier = Modifier
@ -287,6 +311,7 @@ class ChatsActivity: ComponentActivity() {
else
TextAvatar(contact.name, contact.color)
}
is Contact.AndroidContact -> {
val thumbNailUri = contact.thumbnailUri
if (thumbNailUri != null)
@ -301,6 +326,7 @@ class ChatsActivity: ComponentActivity() {
else
TextAvatar(contact.name, contact.color)
}
null -> {
val avatarImage = BitmapFactory
.decodeResource(ctx.resources, R.drawable.person_image)
@ -318,72 +344,40 @@ class ChatsActivity: ComponentActivity() {
chatRequest.launch(i)
},
onLongClick = {
val dialogClickListener =
DialogInterface.OnClickListener { _, which ->
when (which) {
DialogInterface.BUTTON_POSITIVE -> {
val i =
Intent(ctx, BaresipContactActivity::class.java)
val b = Bundle()
b.putBoolean("new", true)
b.putString("uri", message.peerUri)
i.putExtras(b)
startActivity(i)
}
DialogInterface.BUTTON_NEGATIVE -> {
Message.deleteAorPeerMessages(aor, message.peerUri)
}
DialogInterface.BUTTON_NEUTRAL -> {
}
}
}
val builder = MaterialAlertDialogBuilder(
this@ChatsActivity,
R.style.AlertDialogTheme
)
val peer = Utils.friendlyUri(ctx, message.peerUri, account)
if (!Contact.nameExists(peer, BaresipService.contacts, false))
with(builder) {
setTitle(R.string.confirmation)
setMessage(
String.format(
getString(R.string.long_chat_question),
peer
)
)
setNeutralButton(
getText(R.string.cancel),
dialogClickListener
)
setNegativeButton(
getText(R.string.delete),
dialogClickListener
)
setPositiveButton(
getText(R.string.add_contact),
dialogClickListener
)
show()
val contactExists =
Contact.nameExists(peer, BaresipService.contacts, false)
if (contactExists) {
dialogMessage.value = String.format(
getString(R.string.short_chat_question),
peer
)
positiveButtonText.value = getString(R.string.delete)
positiveAction.value = {
Message.deleteAorPeerMessages(aor, message.peerUri)
_uaMessages.value = uaMessages(aor)
}
else
with(builder) {
setTitle(R.string.confirmation)
setMessage(
String.format(
getString(R.string.short_chat_question),
peer
)
)
setNeutralButton(
getText(R.string.cancel),
dialogClickListener
)
setNegativeButton(
getText(R.string.delete),
dialogClickListener
)
show()
neutralButtonText.value = ""
} else {
dialogMessage.value = String.format(
getString(R.string.long_chat_question),
peer
)
positiveButtonText.value = getString(R.string.add_contact)
positiveAction.value = {
val i = Intent(ctx, BaresipContactActivity::class.java)
val b = Bundle()
b.putBoolean("new", true)
b.putString("uri", message.peerUri)
i.putExtras(b)
startActivity(i)
}
neutralButtonText.value = getString(R.string.delete)
neutralAction.value = {
Message.deleteAorPeerMessages(aor, message.peerUri)
}
}
showDialog.value = true
},
modifier = Modifier
.fillMaxWidth()
@ -393,14 +387,13 @@ class ChatsActivity: ComponentActivity() {
RoundedCornerShape(50.dp, 20.dp, 20.dp, 10.dp)
else
RoundedCornerShape(20.dp, 10.dp, 50.dp, 20.dp),
color = //ButtonDefaults.buttonColors(containerColor =
color =
if (message.direction == MESSAGE_DOWN) {
if (BaresipService.darkTheme.value)
LocalCustomColors.current.secondaryDark
else
LocalCustomColors.current.secondaryLight
}
else {
} else {
if (BaresipService.darkTheme.value)
LocalCustomColors.current.primaryDark
else
@ -423,8 +416,7 @@ class ChatsActivity: ComponentActivity() {
LocalCustomColors.current.secondaryLight
else
LocalCustomColors.current.secondaryDark
}
else {
} else {
if (BaresipService.darkTheme.value)
LocalCustomColors.current.primaryLight
else
@ -456,6 +448,20 @@ class ChatsActivity: ComponentActivity() {
@Composable
fun NewChatPeer() {
val showDialog = remember { mutableStateOf(false) }
val items = remember { mutableStateOf(listOf<String>()) }
val itemAction = remember { mutableStateOf<(Int) -> Unit>({ index -> {} }) }
SelectableAlertDialog(
openDialog = showDialog,
title = "Choose Destination",
items = items.value,
onItemClicked = itemAction.value,
neutralButtonText = getString(R.string.cancel),
onNeutralClicked = {}
)
val suggestions by remember { contactNames }
var filteredSuggestions by remember { mutableStateOf(suggestions) }
var showSuggestions by remember { mutableStateOf(false) }
@ -477,8 +483,10 @@ class ChatsActivity: ComponentActivity() {
Column(
modifier = Modifier
.shadow(8.dp, RoundedCornerShape(8.dp))
.background(LocalCustomColors.current.grayLight,
shape = RoundedCornerShape(8.dp))
.background(
LocalCustomColors.current.grayLight,
shape = RoundedCornerShape(8.dp)
)
.animateContentSize()
) {
Box(modifier = Modifier.fillMaxWidth()) {
@ -565,7 +573,10 @@ class ChatsActivity: ComponentActivity() {
fontSize = 18.sp,
color = LocalCustomColors.current.itemText
),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done)
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Done
)
)
}
Spacer(Modifier.width(4.dp))
@ -573,10 +584,21 @@ class ChatsActivity: ComponentActivity() {
modifier = Modifier.padding(end = 4.dp),
onClick = {
showSuggestions = false
if (makeChat(newPeer, true)) {
newPeer = ""
focusManager.clearFocus()
var peerUri = newPeer.trim()
if (peerUri.isNotEmpty()) {
val uris = Contact.contactUris(peerUri)
if (uris.size == 1) {
makeChat(uris[0])
} else if (uris.size > 1) {
items.value = uris
itemAction.value = { index ->
makeChat(uris[index])
}
}
showDialog.value = true
}
newPeer = ""
focusManager.clearFocus()
},
containerColor = LocalCustomColors.current.accent,
contentColor = LocalCustomColors.current.background
@ -590,54 +612,48 @@ class ChatsActivity: ComponentActivity() {
}
}
private fun makeChat(peer: String, lookForContact: Boolean = true): Boolean {
var peerUri = peer.trim()
if (peerUri.isNotEmpty()) {
if (lookForContact) {
val uris = Contact.contactUris(peerUri)
if (uris.size == 1)
peerUri = uris[0]
else if (uris.size > 1) {
val builder = MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)
with(builder) {
setTitle("Choose Destination")
setItems(uris.toTypedArray()) { _, which ->
peerUri = uris[which]
makeChat(peerUri, false)
}
setNeutralButton(getString(R.string.cancel)) { _: DialogInterface, _: Int -> }
show()
}
return true
}
}
if (Utils.isTelNumber(peerUri))
peerUri = "tel:$peerUri"
val uri = if (Utils.isTelUri(peerUri)) {
if (account.telProvider == "") {
Utils.alertView(this, getString(R.string.notice),
String.format(getString(R.string.no_telephony_provider), account.aor))
return false
}
@Composable
fun Notice() {
AlertDialog(
showDialog = remember { mutableStateOf(true) },
title = getString(R.string.notice),
message = noticeMessage.value,
positiveButtonText = getString(R.string.ok),
onPositiveClicked = { showNotice = false },
)
}
private fun makeChat(chatPeer: String) {
val peerUri = if (Utils.isTelNumber(chatPeer))
"tel:$chatPeer"
else
chatPeer
val uri = if (Utils.isTelUri(peerUri)) {
if (account.telProvider == "") {
noticeMessage.value =
String.format(getString(R.string.no_telephony_provider), account.aor)
showNotice = true
""
} else
Utils.telToSip(peerUri, account)
} else {
Utils.uriComplete(peerUri, aor)
}
} else
Utils.uriComplete(peerUri, aor)
if (noticeMessage.value.isEmpty()) {
if (!Utils.checkUri(uri)) {
Utils.alertView(this, getString(R.string.notice),
String.format(getString(R.string.invalid_sip_or_tel_uri), uri))
return false
} else {
noticeMessage.value = String.format(getString(R.string.invalid_sip_or_tel_uri), uri)
showNotice = true
}
else {
val i = Intent(this@ChatsActivity, ChatActivity::class.java)
val b = Bundle()
b.putString("aor", aor)
b.putString("peer", uri)
i.putExtras(b)
chatRequest.launch(i)
return true
}
}
return false
}
override fun onPause() {

View File

@ -24,7 +24,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
@ -330,6 +333,7 @@ object CustomElements {
onNeutralClicked: () -> Unit = {}
) {
if (showDialog.value) {
Log.d(TAG, "*********** message = '$message'")
BasicAlertDialog(
onDismissRequest = {
showDialog.value = false
@ -440,6 +444,81 @@ object CustomElements {
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SelectableAlertDialog(
openDialog: MutableState<Boolean>,
title: String,
items: List<String>,
onItemClicked: (Int) -> Unit,
neutralButtonText: String = "",
onNeutralClicked: () -> Unit = {}
) {
if (openDialog.value) {
BasicAlertDialog(
onDismissRequest = {
openDialog.value = false
},
content = {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(top = 16.dp, start = 16.dp, end = 16.dp, bottom = 0.dp),
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(
containerColor = LocalCustomColors.current.cardBackground
)
) {
Column(modifier = Modifier.padding(16.dp)) {
androidx.compose.material3.Text(
text = title,
fontSize = 20.sp,
color = LocalCustomColors.current.alert,
textAlign = TextAlign.Start,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
val lazyListState = rememberLazyListState()
LazyColumn(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.End,
state = lazyListState,
) {
itemsIndexed(items) { index, item ->
TextButton(
onClick = {
onItemClicked(index)
openDialog.value = false
},
modifier = Modifier.align(Alignment.End)
) {
androidx.compose.material3.Text(
text = item,
color = LocalCustomColors.current.itemText,
)
}
}
}
if (neutralButtonText.isNotEmpty()) {
TextButton(
onClick = {
onNeutralClicked()
openDialog.value = false
},
modifier = Modifier.align(Alignment.End)){
androidx.compose.material3.Text(
text = neutralButtonText.uppercase(),
color = LocalCustomColors.current.gray,
)
}
}
}
}
}
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PasswordDialog(