Completed composting alert of dialogs (testing still needed)

UI improvements
This commit is contained in:
Juha Heinanen
2025-04-15 18:10:58 +03:00
parent c1b7e4b01e
commit fb35f9fbd8
12 changed files with 580 additions and 702 deletions
@@ -63,6 +63,7 @@ import androidx.compose.ui.unit.sp
import com.tutpro.baresip.BaresipService.Companion.uas import com.tutpro.baresip.BaresipService.Companion.uas
import com.tutpro.baresip.CustomElements.AlertDialog import com.tutpro.baresip.CustomElements.AlertDialog
import com.tutpro.baresip.CustomElements.Checkbox import com.tutpro.baresip.CustomElements.Checkbox
import com.tutpro.baresip.CustomElements.LabelText
import com.tutpro.baresip.CustomElements.verticalScrollbar import com.tutpro.baresip.CustomElements.verticalScrollbar
class AccountActivity : ComponentActivity() { class AccountActivity : ComponentActivity() {
@@ -334,7 +335,10 @@ class AccountActivity : ComponentActivity() {
fontSize = 18.sp, fontSize = 18.sp,
color = LocalCustomColors.current.itemText color = LocalCustomColors.current.itemText
), ),
label = { Text(stringResource(R.string.sip_uri)) } label = {
LabelText(text = stringResource(R.string.sip_uri),
fontWeight = FontWeight.Bold)
}
) )
} }
} }
@@ -362,7 +366,7 @@ class AccountActivity : ComponentActivity() {
showAlert.value = true }, showAlert.value = true },
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.nickname)) }, label = { LabelText(stringResource(R.string.nickname)) },
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences, capitalization = KeyboardCapitalization.Sentences,
keyboardType = KeyboardType.Text), keyboardType = KeyboardType.Text),
@@ -393,7 +397,7 @@ class AccountActivity : ComponentActivity() {
showAlert.value = true }, showAlert.value = true },
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.display_name)) }, label = { LabelText(stringResource(R.string.display_name)) },
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences, capitalization = KeyboardCapitalization.Sentences,
keyboardType = KeyboardType.Text), keyboardType = KeyboardType.Text),
@@ -424,7 +428,7 @@ class AccountActivity : ComponentActivity() {
showAlert.value = true }, showAlert.value = true },
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.authentication_username)) }, label = { LabelText(stringResource(R.string.authentication_username)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
) )
} }
@@ -474,7 +478,7 @@ class AccountActivity : ComponentActivity() {
showAlert.value = true }, showAlert.value = true },
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.authentication_password)) }, label = { LabelText(stringResource(R.string.authentication_password)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
) )
} }
@@ -509,7 +513,7 @@ class AccountActivity : ComponentActivity() {
singleLine = true, singleLine = true,
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.sip_uri_of_proxy_server)) }, label = { LabelText(stringResource(R.string.sip_uri_of_proxy_server)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
) )
} }
@@ -531,7 +535,7 @@ class AccountActivity : ComponentActivity() {
singleLine = true, singleLine = true,
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.sip_uri_of_another_proxy_server)) }, label = { LabelText(stringResource(R.string.sip_uri_of_another_proxy_server)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
) )
} }
@@ -587,7 +591,7 @@ class AccountActivity : ComponentActivity() {
showAlert.value = true }, showAlert.value = true },
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.reg_int)) }, label = { LabelText(stringResource(R.string.reg_int)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
) )
} }
@@ -764,7 +768,7 @@ class AccountActivity : ComponentActivity() {
showAlert.value = true }, showAlert.value = true },
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.stun_server)) }, label = { LabelText(stringResource(R.string.stun_server)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
) )
} }
@@ -793,7 +797,7 @@ class AccountActivity : ComponentActivity() {
showAlert.value = true }, showAlert.value = true },
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.stun_username)) }, label = { LabelText(stringResource(R.string.stun_username)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
) )
} }
@@ -847,7 +851,7 @@ class AccountActivity : ComponentActivity() {
showAlert.value = true }, showAlert.value = true },
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.stun_password)) }, label = { LabelText(stringResource(R.string.stun_password)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
) )
} }
@@ -1111,7 +1115,7 @@ class AccountActivity : ComponentActivity() {
showAlert.value = true }, showAlert.value = true },
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.voicemail_uri)) }, label = { LabelText(stringResource(R.string.voicemail_uri)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
) )
} }
@@ -1140,7 +1144,7 @@ class AccountActivity : ComponentActivity() {
showAlert.value = true }, showAlert.value = true },
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.country_code)) }, label = { LabelText(stringResource(R.string.country_code)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
) )
} }
@@ -1169,7 +1173,7 @@ class AccountActivity : ComponentActivity() {
showAlert.value = true }, showAlert.value = true },
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.telephony_provider)) }, label = { LabelText(stringResource(R.string.telephony_provider)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
) )
} }
@@ -56,6 +56,7 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.tutpro.baresip.CustomElements.AlertDialog import com.tutpro.baresip.CustomElements.AlertDialog
import com.tutpro.baresip.CustomElements.LabelText
import com.tutpro.baresip.CustomElements.verticalScrollbar import com.tutpro.baresip.CustomElements.verticalScrollbar
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@@ -283,12 +284,7 @@ class AccountsActivity : ComponentActivity() {
) )
} }
}, },
label = { label = { LabelText(stringResource(R.string.new_account)) },
CustomElements.Text(
text = getString(R.string.new_account),
fontSize = 18.sp
)
},
textStyle = TextStyle(fontSize = 18.sp), textStyle = TextStyle(fontSize = 18.sp),
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text, keyboardType = KeyboardType.Text,
@@ -1,6 +1,5 @@
package com.tutpro.baresip package com.tutpro.baresip
import android.content.Context
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
@@ -103,7 +102,7 @@ class AudioActivity : ComponentActivity() {
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
color = LocalCustomColors.current.background color = LocalCustomColors.current.background
) { ) {
AudioScreen(this, title) { goBack() } AudioScreen(title) { goBack() }
} }
} }
} }
@@ -112,7 +111,7 @@ class AudioActivity : ComponentActivity() {
} }
@Composable @Composable
fun AudioScreen(ctx: Context, title: String, navigateBack: () -> Unit) { fun AudioScreen(title: String, navigateBack: () -> Unit) {
Scaffold( Scaffold(
modifier = Modifier modifier = Modifier
.fillMaxHeight() .fillMaxHeight()
@@ -448,7 +447,7 @@ class AudioActivity : ComponentActivity() {
}, },
textStyle = androidx.compose.ui.text.TextStyle( textStyle = androidx.compose.ui.text.TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { LabelText(getString(R.string.audio_delay)) }, label = { LabelText(stringResource(R.string.audio_delay)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
) )
} }
@@ -75,6 +75,7 @@ import androidx.compose.ui.unit.sp
import androidx.core.graphics.scale import androidx.core.graphics.scale
import androidx.exifinterface.media.ExifInterface import androidx.exifinterface.media.ExifInterface
import coil.compose.rememberAsyncImagePainter import coil.compose.rememberAsyncImagePainter
import com.tutpro.baresip.CustomElements.AlertDialog
import com.tutpro.baresip.CustomElements.Checkbox import com.tutpro.baresip.CustomElements.Checkbox
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
@@ -86,7 +87,6 @@ class BaresipContactActivity : ComponentActivity() {
private var new = false private var new = false
private var favorite = false private var favorite = false
private var android = false private var android = false
private var avatarImage: Bitmap? = null
private var newName = "" private var newName = ""
private var newUri = "" private var newUri = ""
private var newFavorite = false private var newFavorite = false
@@ -94,9 +94,17 @@ class BaresipContactActivity : ComponentActivity() {
private var uriOrName = "" private var uriOrName = ""
private var color = 0 private var color = 0
private var id: Long = 0 private var id: Long = 0
private var newAvatar = "" private var newId: Long = 0
private var tmpFile: File? = null private var tmpFile: File? = null
private val alertTitle = mutableStateOf("")
private val alertMessage = mutableStateOf("")
private val showAlert = mutableStateOf(false)
private val textAvatarText = mutableStateOf("")
private val textAvatarColor = mutableIntStateOf(0)
private val imageAvatarUri = mutableStateOf("")
private val onBackPressedCallback = object : OnBackPressedCallback(true) { private val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() { override fun handleOnBackPressed() {
goBack() goBack()
@@ -117,23 +125,29 @@ class BaresipContactActivity : ComponentActivity() {
name = "" name = ""
uri = intent.getStringExtra("uri")!! uri = intent.getStringExtra("uri")!!
favorite = false favorite = false
avatarImage = null
android = BaresipService.contactsMode == "android" android = BaresipService.contactsMode == "android"
title = getString(R.string.new_contact) title = getString(R.string.new_contact)
uriOrName = uri uriOrName = uri
color = Utils.randomColor() color = Utils.randomColor()
id = System.currentTimeMillis() id = System.currentTimeMillis()
} else { newId = id
}
else {
name = intent.getStringExtra("name")!! name = intent.getStringExtra("name")!!
val contact = Contact.baresipContact(name)!! val contact = Contact.baresipContact(name)!!
uri = contact.uri uri = contact.uri
favorite = contact.favorite favorite = contact.favorite
avatarImage = contact.avatarImage
android = false android = false
title = name title = name
uriOrName = name uriOrName = name
color = contact.color color = contact.color
id = contact.id id = contact.id
newId = id
if (contact.avatarImage != null) {
val avatarFile = File(BaresipService.filesPath, "${newId}.png")
if (avatarFile.exists())
imageAvatarUri.value = Uri.fromFile(avatarFile).toString()
}
} }
setContent { setContent {
@@ -159,7 +173,7 @@ class BaresipContactActivity : ComponentActivity() {
containerColor = LocalCustomColors.current.background, containerColor = LocalCustomColors.current.background,
topBar = { TopAppBar(ctx, title, navigateBack) }, topBar = { TopAppBar(ctx, title, navigateBack) },
content = { contentPadding -> content = { contentPadding ->
ContactContent(ctx, contentPadding) ContactContent(contentPadding)
} }
) )
} }
@@ -203,13 +217,60 @@ class BaresipContactActivity : ComponentActivity() {
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun ContactContent(ctx: Context, contentPadding: PaddingValues) { fun ContactContent(contentPadding: PaddingValues) {
var avatarType by remember { mutableStateOf(if (avatarImage != null) "image" else "text") } if (showAlert.value) {
var textAvatarText by remember { mutableStateOf("") } AlertDialog(
var textAvatarColor by remember { mutableIntStateOf(0) } showDialog = showAlert,
var imageAvatarUri by remember { mutableStateOf("") } title = alertTitle.value,
message = alertMessage.value,
positiveButtonText = stringResource(R.string.ok),
)
}
Column(
modifier = Modifier
.fillMaxWidth()
.background(LocalCustomColors.current.background)
.padding(contentPadding)
.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 52.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Avatar()
ContactName()
ContactUri()
Favorite()
Android()
}
}
@Composable
fun TextAvatar(size: Int) {
Box(
modifier = Modifier.size(size.dp),
contentAlignment = Alignment.Center
) {
Canvas(modifier = Modifier.fillMaxSize()) {
drawCircle(SolidColor(Color(textAvatarColor.intValue)))
}
Text(textAvatarText.value, fontSize = 72.sp, color = Color.White)
}
}
@Composable
fun ImageAvatar(size: Int) {
Image(
painter = rememberAsyncImagePainter(model = imageAvatarUri.value),
contentDescription = stringResource(R.string.avatar_image),
contentScale = ContentScale.Crop,
modifier = Modifier.size(size.dp).clip(CircleShape)
)
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun Avatar() {
val avatarRequest = val avatarRequest =
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
if (it != null) if (it != null)
@@ -224,67 +285,22 @@ class BaresipContactActivity : ComponentActivity() {
ExifInterface.ORIENTATION_NORMAL ExifInterface.ORIENTATION_NORMAL
) )
val rotatedBitmap = rotateBitmap(scaledBitmap, orientation) val rotatedBitmap = rotateBitmap(scaledBitmap, orientation)
tmpFile = File( if (tmpFile != null && tmpFile!!.exists())
BaresipService.filesPath + "/tmp", Utils.deleteFile(tmpFile!!)
"${System.currentTimeMillis()}.png" newId = System.currentTimeMillis()
) tmpFile = File(BaresipService.filesPath, "${newId}.png")
if (Utils.saveBitmap(rotatedBitmap, tmpFile!!)) { if (Utils.saveBitmap(rotatedBitmap, tmpFile!!)) {
newAvatar = "image" imageAvatarUri.value = Uri.fromFile(tmpFile).toString()
avatarType = newAvatar
imageAvatarUri = Uri.fromFile(tmpFile).toString()
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Could not read avatar image: ${e.message}") Log.e(TAG, "Could not read avatar image: ${e.message}")
} }
} }
@Composable
fun TextAvatar(size: Int) {
Box(
modifier = Modifier.size(size.dp),
contentAlignment = Alignment.Center
) {
Canvas(modifier = Modifier.fillMaxSize()) {
drawCircle(SolidColor(Color(textAvatarColor)))
}
textAvatarText = if (name == "") "?" else name[0].toString()
Text(textAvatarText, fontSize = 72.sp, color = Color.White)
}
}
@Composable
fun ImageAvatar(size: Int) {
Image(
painter = rememberAsyncImagePainter(model = imageAvatarUri),
contentDescription = stringResource(R.string.avatar_image),
contentScale = ContentScale.Crop,
modifier = Modifier.size(size.dp).clip(CircleShape)
)
}
@Composable
fun avatar() {
Row( Row(
Modifier.fillMaxWidth(), Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center horizontalArrangement = Arrangement.Center
) { ) {
if (new)
textAvatarColor = color
else {
val avatarImage = avatarImage
val avatarFile = File(BaresipService.filesPath, "${id}.png")
if (avatarImage != null && (newAvatar == "" || newAvatar == "image")) {
imageAvatarUri = if (tmpFile != null && tmpFile!!.exists())
Uri.fromFile(tmpFile).toString()
else
Uri.fromFile(avatarFile).toString()
}
else {
textAvatarText = "${name[0]}"
textAvatarColor = color
}
}
Box( Box(
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
modifier = Modifier.size(96.dp) modifier = Modifier.size(96.dp)
@@ -293,23 +309,27 @@ class BaresipContactActivity : ComponentActivity() {
avatarRequest.launch("image/*") avatarRequest.launch("image/*")
}, },
onLongClick = { onLongClick = {
textAvatarColor = Utils.randomColor() textAvatarColor.intValue = Utils.randomColor()
color = textAvatarColor color = textAvatarColor.intValue
newAvatar = "text" imageAvatarUri.value = ""
avatarType = newAvatar
} }
), ),
) { ) {
if (avatarType == "text") if (imageAvatarUri.value == "") {
TextAvatar(96) textAvatarText.value = if (new)
"?"
else else
"${name[0]}"
textAvatarColor.intValue = color
TextAvatar(96)
} else
ImageAvatar(96) ImageAvatar(96)
} }
} }
} }
@Composable @Composable
fun contactName() { fun ContactName() {
val focusRequester = FocusRequester() val focusRequester = FocusRequester()
var contactName by remember { mutableStateOf(name) } var contactName by remember { mutableStateOf(name) }
newName = contactName newName = contactName
@@ -337,7 +357,7 @@ class BaresipContactActivity : ComponentActivity() {
} }
@Composable @Composable
fun contactUri() { fun ContactUri() {
var contactUri by remember { mutableStateOf(uri) } var contactUri by remember { mutableStateOf(uri) }
newUri = contactUri newUri = contactUri
OutlinedTextField( OutlinedTextField(
@@ -357,7 +377,7 @@ class BaresipContactActivity : ComponentActivity() {
} }
@Composable @Composable
fun favorite(ctx: Context) { fun Favorite() {
Row( Row(
Modifier.fillMaxWidth(), Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@@ -367,9 +387,9 @@ class BaresipContactActivity : ComponentActivity() {
text = stringResource(R.string.favorite), text = stringResource(R.string.favorite),
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
.clickable { .clickable {
Utils.alertView( alertTitle.value = getString(R.string.favorite)
ctx, getString(R.string.favorite), getString(R.string.favorite_help) alertMessage.value = getString(R.string.favorite_help)
) showAlert.value = true
}, },
color = LocalCustomColors.current.itemText, color = LocalCustomColors.current.itemText,
) )
@@ -386,7 +406,7 @@ class BaresipContactActivity : ComponentActivity() {
} }
@Composable @Composable
fun android() { fun Android() {
if (new && BaresipService.contactsMode != "baresip") if (new && BaresipService.contactsMode != "baresip")
Row( Row(
Modifier.fillMaxWidth(), Modifier.fillMaxWidth(),
@@ -412,22 +432,6 @@ class BaresipContactActivity : ComponentActivity() {
} }
} }
Column(
modifier = Modifier
.fillMaxWidth()
.background(LocalCustomColors.current.background)
.padding(contentPadding)
.padding(start = 16.dp, end = 16.dp, top = 16.dp, bottom = 52.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
avatar()
contactName()
contactUri()
favorite(ctx)
android()
}
}
private fun checkOnClick(ctx: Context) { private fun checkOnClick(ctx: Context) {
if (BaresipService.activities.indexOf("baresip contact,$new,$uriOrName") == -1) if (BaresipService.activities.indexOf("baresip contact,$new,$uriOrName") == -1)
return return
@@ -439,20 +443,18 @@ class BaresipContactActivity : ComponentActivity() {
else else
"sip:$newUri" "sip:$newUri"
if (!Utils.checkUri(newUri)) { if (!Utils.checkUri(newUri)) {
Utils.alertView( alertTitle.value = getString(R.string.notice)
ctx, getString(R.string.notice), alertMessage.value = String.format(getString(R.string.invalid_sip_or_tel_uri), newUri)
String.format(getString(R.string.invalid_sip_or_tel_uri), newUri) showAlert.value = true
)
return return
} }
newName = newName.trim() newName = newName.trim()
if (newName == "") newName = newUri.substringAfter(":") if (newName == "") newName = newUri.substringAfter(":")
if (!Utils.checkName(newName)) { if (!Utils.checkName(newName)) {
Utils.alertView( alertTitle.value = getString(R.string.notice)
ctx, getString(R.string.notice), alertMessage.value = String.format(getString(R.string.invalid_contact), newName)
String.format(getString(R.string.invalid_contact), newName) showAlert.value = true
)
return return
} }
@@ -462,39 +464,25 @@ class BaresipContactActivity : ComponentActivity() {
(uriOrName != newName) && Contact.nameExists(newName, BaresipService.contacts, false) (uriOrName != newName) && Contact.nameExists(newName, BaresipService.contacts, false)
} }
if (alert) { if (alert) {
Utils.alertView( alertTitle.value = getString(R.string.notice)
ctx, getString(R.string.notice), alertMessage.value = String.format(getString(R.string.contact_already_exists), newName)
String.format(getString(R.string.contact_already_exists), newName)) showAlert.value = true
return return
} }
val contact: Contact.BaresipContact val contact: Contact.BaresipContact =
Contact.BaresipContact(newName, newUri, color, newId, newFavorite)
if (new) { if (imageAvatarUri.value == "") {
contact = Contact.BaresipContact(newName, newUri, color, id, newFavorite)
} else {
contact = Contact.baresipContact(name)!!
contact.uri = newUri
contact.name = newName
contact.color = color
contact.favorite = newFavorite
}
when (newAvatar) {
"text" -> {
if (contact.avatarImage != null) { if (contact.avatarImage != null) {
contact.avatarImage = null contact.avatarImage = null
Utils.deleteFile(File(BaresipService.filesPath, "${contact.id}.png")) Utils.deleteFile(File(BaresipService.filesPath, "${newId}.png"))
} }
} }
"image" -> { else {
val imageFilePath = BaresipService.filesPath + "/${contact.id}.png" val imageFilePath = BaresipService.filesPath + "/${newId}.png"
val imageFile = File(imageFilePath)
tmpFile!!.copyTo(target = imageFile, overwrite = true)
contact.avatarImage = BitmapFactory.decodeFile(imageFilePath) contact.avatarImage = BitmapFactory.decodeFile(imageFilePath)
} }
}
if (newAndroid) if (newAndroid)
addOrUpdateAndroidContact(ctx, contact) addOrUpdateAndroidContact(ctx, contact)
@@ -502,7 +490,7 @@ class BaresipContactActivity : ComponentActivity() {
if (new) if (new)
Contact.addBaresipContact(contact) Contact.addBaresipContact(contact)
else else
Contact.updateBaresipContact(contact) Contact.updateBaresipContact(id, contact)
} }
BaresipService.activities.remove("baresip contact,$new,$uriOrName") BaresipService.activities.remove("baresip contact,$new,$uriOrName")
@@ -64,6 +64,7 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.SoftwareKeyboardController import androidx.compose.ui.platform.SoftwareKeyboardController
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
@@ -74,6 +75,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.tutpro.baresip.CustomElements.AlertDialog import com.tutpro.baresip.CustomElements.AlertDialog
import com.tutpro.baresip.CustomElements.LabelText
import com.tutpro.baresip.CustomElements.Text import com.tutpro.baresip.CustomElements.Text
import com.tutpro.baresip.CustomElements.verticalScrollbar import com.tutpro.baresip.CustomElements.verticalScrollbar
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -530,7 +532,7 @@ class ChatActivity : ComponentActivity() {
modifier = Modifier.clickable { newMessage = "" } modifier = Modifier.clickable { newMessage = "" }
) )
} }, } },
label = { Text(text = getString(R.string.new_message), fontSize = 18.sp) }, label = { LabelText(text = stringResource(R.string.new_message), fontSize = 16.sp) },
textStyle = TextStyle(fontSize = 18.sp), textStyle = TextStyle(fontSize = 18.sp),
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.Sentences, capitalization = KeyboardCapitalization.Sentences,
@@ -78,6 +78,7 @@ import coil.compose.AsyncImage
import com.tutpro.baresip.BaresipService.Companion.contactNames import com.tutpro.baresip.BaresipService.Companion.contactNames
import com.tutpro.baresip.CustomElements.AlertDialog import com.tutpro.baresip.CustomElements.AlertDialog
import com.tutpro.baresip.CustomElements.ImageAvatar import com.tutpro.baresip.CustomElements.ImageAvatar
import com.tutpro.baresip.CustomElements.LabelText
import com.tutpro.baresip.CustomElements.Text import com.tutpro.baresip.CustomElements.Text
import com.tutpro.baresip.CustomElements.TextAvatar import com.tutpro.baresip.CustomElements.TextAvatar
import com.tutpro.baresip.CustomElements.verticalScrollbar import com.tutpro.baresip.CustomElements.verticalScrollbar
@@ -574,9 +575,9 @@ class ChatsActivity: ComponentActivity() {
) )
}, },
label = { label = {
Text( LabelText(
text = getString(R.string.new_chat_peer), text = stringResource(R.string.new_chat_peer),
fontSize = 18.sp fontSize = 16.sp
) )
}, },
textStyle = TextStyle( textStyle = TextStyle(
@@ -65,6 +65,7 @@ import androidx.compose.ui.unit.sp
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.tutpro.baresip.CustomElements.AlertDialog import com.tutpro.baresip.CustomElements.AlertDialog
import com.tutpro.baresip.CustomElements.Checkbox import com.tutpro.baresip.CustomElements.Checkbox
import com.tutpro.baresip.CustomElements.LabelText
import com.tutpro.baresip.CustomElements.verticalScrollbar import com.tutpro.baresip.CustomElements.verticalScrollbar
import com.tutpro.baresip.Utils.copyInputStreamToFile import com.tutpro.baresip.Utils.copyInputStreamToFile
import java.io.File import java.io.File
@@ -476,7 +477,7 @@ class ConfigActivity : ComponentActivity() {
}, },
textStyle = androidx.compose.ui.text.TextStyle( textStyle = androidx.compose.ui.text.TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.listen_address)) }, label = { LabelText(stringResource(R.string.listen_address)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
) )
} }
@@ -573,7 +574,7 @@ class ConfigActivity : ComponentActivity() {
}, },
textStyle = androidx.compose.ui.text.TextStyle( textStyle = androidx.compose.ui.text.TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.dns_servers)) }, label = { LabelText(stringResource(R.string.dns_servers)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
) )
} }
@@ -781,7 +782,7 @@ class ConfigActivity : ComponentActivity() {
}, },
textStyle = androidx.compose.ui.text.TextStyle( textStyle = androidx.compose.ui.text.TextStyle(
fontSize = 18.sp, color = LocalCustomColors.current.itemText), fontSize = 18.sp, color = LocalCustomColors.current.itemText),
label = { Text(stringResource(R.string.user_agent)) }, label = { LabelText(stringResource(R.string.user_agent)) },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
) )
} }
@@ -272,13 +272,11 @@ sealed class Contact {
contactsUpdate() contactsUpdate()
} }
fun updateBaresipContact(contact: BaresipContact) { fun updateBaresipContact(id: Long, contact: BaresipContact) {
val updatedContacts = BaresipService.baresipContacts.value.toMutableList() val updatedContacts = BaresipService.baresipContacts.value.toMutableList()
val updatedContact = contact.copy() as BaresipContact updatedContacts.removeIf { it.id == id }
updatedContact.id = System.currentTimeMillis() updatedContacts.add(contact)
updatedContacts.removeIf { it.id == contact.id } BaresipService.baresipContacts.value = updatedContacts.toList()
updatedContacts.add(updatedContact)
BaresipService.baresipContacts.value = updatedContacts
saveBaresipContacts() saveBaresipContacts()
contactsUpdate() contactsUpdate()
} }
@@ -369,7 +369,7 @@ class ContactsActivity : ComponentActivity() {
Icon( Icon(
imageVector = Icons.Filled.Edit, imageVector = Icons.Filled.Edit,
modifier = Modifier.size(28.dp), modifier = Modifier.size(28.dp),
contentDescription = stringResource(R.string.add) contentDescription = stringResource(R.string.edit)
) )
} }
} }
@@ -12,7 +12,6 @@ import android.app.KeyguardManager
import android.app.NotificationManager import android.app.NotificationManager
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.content.Intent.ACTION_CALL import android.content.Intent.ACTION_CALL
import android.content.Intent.ACTION_DIAL import android.content.Intent.ACTION_DIAL
@@ -58,11 +57,11 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
@@ -130,13 +129,15 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.tutpro.baresip.BaresipService.Companion.contactNames import com.tutpro.baresip.BaresipService.Companion.contactNames
import com.tutpro.baresip.BaresipService.Companion.uas import com.tutpro.baresip.BaresipService.Companion.uas
import com.tutpro.baresip.BaresipService.Companion.uasStatus import com.tutpro.baresip.BaresipService.Companion.uasStatus
import com.tutpro.baresip.CustomElements.AlertDialog
import com.tutpro.baresip.CustomElements.Checkbox import com.tutpro.baresip.CustomElements.Checkbox
import com.tutpro.baresip.CustomElements.DropdownMenu import com.tutpro.baresip.CustomElements.DropdownMenu
import com.tutpro.baresip.CustomElements.LabelText
import com.tutpro.baresip.CustomElements.PullToRefreshBox import com.tutpro.baresip.CustomElements.PullToRefreshBox
import com.tutpro.baresip.CustomElements.SelectableAlertDialog
import com.tutpro.baresip.CustomElements.Text import com.tutpro.baresip.CustomElements.Text
import com.tutpro.baresip.CustomElements.verticalScrollbar import com.tutpro.baresip.CustomElements.verticalScrollbar
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -220,6 +221,22 @@ class MainActivity : ComponentActivity() {
private var passwordAccounts = mutableListOf<String>() private var passwordAccounts = mutableListOf<String>()
private var passwordTitle by mutableStateOf("") private var passwordTitle by mutableStateOf("")
private val alertTitle = mutableStateOf("")
private val alertMessage = mutableStateOf("")
private val showAlert = mutableStateOf(false)
private val dialogTitle = mutableStateOf("")
private val dialogMessage = mutableStateOf("")
private val positiveText = mutableStateOf("")
private val onPositiveClicked = mutableStateOf({})
private val negativeText = mutableStateOf("")
private val onNegativeClicked = mutableStateOf({})
private val showDialog = mutableStateOf(false)
private val showSelectItemDialog = mutableStateOf(false)
val items = mutableStateOf(listOf<String>())
val itemAction = mutableStateOf<(Int) -> Unit>({ index -> {} })
private val onBackPressedCallback = object : OnBackPressedCallback(true) { private val onBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() { override fun handleOnBackPressed() {
moveTaskToBack(true) moveTaskToBack(true)
@@ -330,18 +347,14 @@ class MainActivity : ComponentActivity() {
configRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { configRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if ((it.data != null) && it.data!!.hasExtra("restart")) { if ((it.data != null) && it.data!!.hasExtra("restart")) {
with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) { dialogTitle.value = getString(R.string.restart_request)
setTitle(R.string.restart_request) dialogMessage.value = getString(R.string.config_restart)
setMessage(getString(R.string.config_restart)) positiveText.value = getString(R.string.restart)
setPositiveButton(getText(R.string.restart)) { dialog, _ -> onPositiveClicked.value = {
dialog.dismiss()
quitRestart(true) quitRestart(true)
} }
setNeutralButton(getText(R.string.cancel)) { dialog, _ -> negativeText.value = getString(R.string.cancel)
dialog.dismiss() showDialog.value = true
}
show()
}
} }
val displayTheme = Preferences(applicationContext).displayTheme val displayTheme = Preferences(applicationContext).displayTheme
if (displayTheme != AppCompatDelegate.getDefaultNightMode()) { if (displayTheme != AppCompatDelegate.getDefaultNightMode()) {
@@ -436,21 +449,26 @@ class MainActivity : ComponentActivity() {
shouldShow.add(permission.key) shouldShow.add(permission.key)
} }
} }
if (denied.contains(POST_NOTIFICATIONS) && if (denied.contains(POST_NOTIFICATIONS) && !shouldShow.contains(POST_NOTIFICATIONS)) {
!shouldShow.contains(POST_NOTIFICATIONS)) { dialogTitle.value = getString(R.string.notice)
with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) { dialogMessage.value = getString(R.string.no_notifications)
setTitle(getString(R.string.notice)) positiveText.value = getString(R.string.ok)
setMessage(getString(R.string.no_notifications)) onPositiveClicked.value = {
setPositiveButton(getString(R.string.ok)) { _, _ ->
quitRestart(false) quitRestart(false)
} }
show() negativeText.value = ""
} showDialog.value = true
} else { } else {
if (shouldShow.isNotEmpty()) if (shouldShow.isNotEmpty()) {
Utils.alertView(this, getString(R.string.permissions_rationale), dialogTitle.value = getString(R.string.permissions_rationale)
getString(R.string.audio_permissions) dialogMessage.value = getString(R.string.audio_permissions)
) { requestPermissionsLauncher.launch(permissions) } positiveText.value = getString(R.string.ok)
onPositiveClicked.value = {
requestPermissionsLauncher.launch(permissions)
}
negativeText.value = ""
showDialog.value = true
}
else else
startBaresip() startBaresip()
} }
@@ -486,6 +504,7 @@ class MainActivity : ComponentActivity() {
@Composable @Composable
fun MainContent(ctx: Context, contentPadding: PaddingValues) { fun MainContent(ctx: Context, contentPadding: PaddingValues) {
var isRefreshing by remember { mutableStateOf(false) } var isRefreshing by remember { mutableStateOf(false) }
LaunchedEffect(isRefreshing) { LaunchedEffect(isRefreshing) {
if (isRefreshing) { if (isRefreshing) {
@@ -493,6 +512,18 @@ class MainActivity : ComponentActivity() {
isRefreshing = false isRefreshing = false
} }
} }
if (showDialog.value)
AlertDialog(
showDialog = showDialog,
title = getString(R.string.confirmation),
message = dialogMessage.value,
positiveButtonText = positiveText.value,
onPositiveClicked = onPositiveClicked.value,
negativeButtonText = getString(R.string.cancel),
onNegativeClicked = onNegativeClicked.value
)
PullToRefreshBox( PullToRefreshBox(
isRefreshing = isRefreshing, isRefreshing = isRefreshing,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
@@ -508,21 +539,27 @@ class MainActivity : ComponentActivity() {
} }
} }
) { ) {
SelectableAlertDialog(
openDialog = showSelectItemDialog,
title = getString(R.string.choose_destination_uri),
items = items.value,
onItemClicked = itemAction.value,
neutralButtonText = getString(R.string.cancel),
onNeutralClicked = {}
)
Column( Column(
modifier = Modifier modifier = Modifier
.consumeWindowInsets(contentPadding) .imePadding()
.padding( .fillMaxWidth()
PaddingValues( .padding(contentPadding)
top = 76.dp, bottom = 6.dp, .padding(top = 18.dp, bottom = 6.dp, start = 16.dp, end = 16.dp)
start = 16.dp, end = 16.dp
)
)
.fillMaxSize() .fillMaxSize()
.verticalScroll(rememberScrollState()), .verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
AccountSpinner(ctx) AccountSpinner(ctx)
CallUriRow(ctx) CallUriRow()
CallRow(ctx) CallRow(ctx)
OnHoldNotice() OnHoldNotice()
AskPasswords(ctx) AskPasswords(ctx)
@@ -668,10 +705,16 @@ class MainActivity : ComponentActivity() {
passwordTitle = getString(R.string.encrypt_password) passwordTitle = getString(R.string.encrypt_password)
showPasswordDialog.value = true showPasswordDialog.value = true
} }
shouldShowRequestPermissionRationale(WRITE_EXTERNAL_STORAGE) -> shouldShowRequestPermissionRationale(WRITE_EXTERNAL_STORAGE) -> {
Utils.alertView(ctx, getString(R.string.notice), getString(R.string.no_backup)) { dialogTitle.value = getString(R.string.notice)
dialogMessage.value = getString(R.string.no_backup)
positiveText.value = getString(R.string.ok)
onPositiveClicked.value = {
requestPermissionLauncher.launch(WRITE_EXTERNAL_STORAGE) requestPermissionLauncher.launch(WRITE_EXTERNAL_STORAGE)
} }
negativeText.value = ""
showDialog.value = true
}
else -> else ->
requestPermissionLauncher.launch(WRITE_EXTERNAL_STORAGE) requestPermissionLauncher.launch(WRITE_EXTERNAL_STORAGE)
} }
@@ -688,10 +731,16 @@ class MainActivity : ComponentActivity() {
passwordTitle = getString(R.string.decrypt_password) passwordTitle = getString(R.string.decrypt_password)
showPasswordDialog.value = true showPasswordDialog.value = true
} }
shouldShowRequestPermissionRationale(READ_EXTERNAL_STORAGE) -> shouldShowRequestPermissionRationale(READ_EXTERNAL_STORAGE) -> {
Utils.alertView(ctx, getString(R.string.notice), getString(R.string.no_restore)) { dialogTitle.value = getString(R.string.notice)
dialogMessage.value = getString(R.string.no_restore)
positiveText.value = getString(R.string.ok)
onPositiveClicked.value = {
requestPermissionLauncher.launch(READ_EXTERNAL_STORAGE) requestPermissionLauncher.launch(READ_EXTERNAL_STORAGE)
} }
negativeText.value = ""
showDialog.value = true
}
else -> else ->
requestPermissionLauncher.launch(READ_EXTERNAL_STORAGE) requestPermissionLauncher.launch(READ_EXTERNAL_STORAGE)
} }
@@ -885,7 +934,7 @@ class MainActivity : ComponentActivity() {
} }
@Composable @Composable
fun CallUriRow(ctx: Context) { fun CallUriRow() {
val suggestions by remember { contactNames } val suggestions by remember { contactNames }
var filteredSuggestions by remember { mutableStateOf(suggestions) } var filteredSuggestions by remember { mutableStateOf(suggestions) }
val focusRequester = remember { FocusRequester() } val focusRequester = remember { FocusRequester() }
@@ -929,7 +978,7 @@ class MainActivity : ComponentActivity() {
.padding(start = 4.dp, end = 4.dp, top = 12.dp, bottom = 2.dp) .padding(start = 4.dp, end = 4.dp, top = 12.dp, bottom = 2.dp)
.focusRequester(focusRequester), .focusRequester(focusRequester),
label = { label = {
Text( LabelText(
text = callUriLabel.value, text = callUriLabel.value,
fontSize = 18.sp, fontSize = 18.sp,
) )
@@ -1018,29 +1067,20 @@ class MainActivity : ComponentActivity() {
.clickable { .clickable {
when (securityIcon.intValue) { when (securityIcon.intValue) {
R.drawable.unlocked -> { R.drawable.unlocked -> {
Utils.alertView( alertTitle.value = getString(R.string.alert)
ctx, getString(R.string.alert), alertMessage.value = getString(R.string.call_not_secure)
getString(R.string.call_not_secure) showAlert.value = true
)
} }
R.drawable.locked_yellow -> { R.drawable.locked_yellow -> {
Utils.alertView( alertTitle.value = getString(R.string.alert)
ctx, getString(R.string.alert), alertMessage.value = getString(R.string.peer_not_verified)
getString(R.string.peer_not_verified) showAlert.value = true
)
} }
R.drawable.locked_green -> { R.drawable.locked_green -> {
with( dialogTitle.value = getString(R.string.info)
MaterialAlertDialogBuilder( dialogMessage.value = getString(R.string.call_is_secure)
ctx, positiveText.value = getString(R.string.unverify)
R.style.AlertDialogTheme onPositiveClicked.value = {
)
) {
setTitle(R.string.info)
setMessage(getString(R.string.call_is_secure))
setPositiveButton(getString(R.string.unverify)) { dialog, _ ->
val ua = UserAgent.ofAor(viewModel.selectedAor.value)!! val ua = UserAgent.ofAor(viewModel.selectedAor.value)!!
val call = ua.currentCall() val call = ua.currentCall()
if (call != null) { if (call != null) {
@@ -1052,13 +1092,9 @@ class MainActivity : ComponentActivity() {
else else
securityIcon.intValue = R.drawable.locked_yellow securityIcon.intValue = R.drawable.locked_yellow
} }
dialog.dismiss()
}
setNeutralButton(getString(R.string.cancel)) { dialog, _ ->
dialog.dismiss()
}
show()
} }
negativeText.value = getString(R.string.cancel)
showDialog.value = true
} }
} }
}, },
@@ -1168,11 +1204,11 @@ class MainActivity : ComponentActivity() {
val call = ua.currentCall() val call = ua.currentCall()
if (call != null) { if (call != null) {
if (call.onHoldCall != null) { if (call.onHoldCall != null) {
if (!call.executeTransfer()) if (!call.executeTransfer()) {
Utils.alertView( alertTitle.value = getString(R.string.notice)
ctx, getString(R.string.notice), alertMessage.value = getString(R.string.transfer_failed)
String.format(getString(R.string.transfer_failed)) showAlert.value = true
) }
} }
else else
showTransferDialog = true showTransferDialog = true
@@ -1254,13 +1290,7 @@ class MainActivity : ComponentActivity() {
bottom = 2.dp bottom = 2.dp
) )
.focusRequester(focusRequester), .focusRequester(focusRequester),
label = { label = { LabelText(stringResource(R.string.transfer_destination)) },
Text(
text = stringResource(R.string.transfer_destination),
fontSize = 18.sp,
color = LocalCustomColors.current.dark
)
},
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 18.sp, fontSize = 18.sp,
color = LocalCustomColors.current.dark color = LocalCustomColors.current.dark
@@ -1365,30 +1395,24 @@ class MainActivity : ComponentActivity() {
} }
TextButton( TextButton(
onClick = { onClick = {
showSuggestions = false
var uriText = transferUri.trim() var uriText = transferUri.trim()
if (uriText.isNotEmpty()) { if (uriText.isNotEmpty()) {
val uris = Contact.contactUris(uriText) val uris = Contact.contactUris(uriText)
if (uris.size > 1) { if (uris.size > 1) {
val destinationBuilder = items.value = uris
MaterialAlertDialogBuilder( itemAction.value = { index ->
ctx, val uriText = uris[index]
R.style.AlertDialogTheme
)
with(destinationBuilder) {
setTitle(R.string.choose_destination_uri)
setItems(uris.toTypedArray()) { _, which ->
uriText = uris[which]
transfer( transfer(
ua, ua,
if (Utils.isTelNumber(uriText)) if (Utils.isTelNumber(uriText)) "tel:$uriText" else uriText,
"tel:$uriText"
else
uriText,
!blindChecked.value !blindChecked.value
) )
showSelectItemDialog.value = false
} }
showSelectItemDialog.value = true
} }
} else { else {
if (uris.size == 1) if (uris.size == 1)
uriText = uris[0] uriText = uris[0]
} }
@@ -1441,7 +1465,7 @@ class MainActivity : ComponentActivity() {
.focusRequester(focusRequester), .focusRequester(focusRequester),
enabled = dtmfEnabled.value, enabled = dtmfEnabled.value,
textStyle = TextStyle(fontSize = 16.sp), textStyle = TextStyle(fontSize = 16.sp),
label = { Text(getString(R.string.dtmf), fontSize = 16.sp) }, label = { LabelText(stringResource(R.string.dtmf)) },
singleLine = true singleLine = true
) )
LaunchedEffect(shouldRequestFocus) { LaunchedEffect(shouldRequestFocus) {
@@ -1468,8 +1492,8 @@ class MainActivity : ComponentActivity() {
val duration = call.duration() val duration = call.duration()
val txCodec = codecs.split(',')[0].split("/") val txCodec = codecs.split(',')[0].split("/")
val rxCodec = codecs.split(',')[1].split("/") val rxCodec = codecs.split(',')[1].split("/")
Utils.alertView( alertTitle.value = getString(R.string.call_info)
ctx, getString(R.string.call_info), alertMessage.value =
"${String.format(getString(R.string.duration), duration)}\n" + "${String.format(getString(R.string.duration), duration)}\n" +
"${getString(R.string.codecs)}: ${txCodec[0]} ch ${txCodec[2]}/" + "${getString(R.string.codecs)}: ${txCodec[0]} ch ${txCodec[2]}/" +
"${rxCodec[0]} ch ${rxCodec[2]}\n" + "${rxCodec[0]} ch ${rxCodec[2]}\n" +
@@ -1483,12 +1507,11 @@ class MainActivity : ComponentActivity() {
"${getString(R.string.packets)}: ${parts[2]}\n" + "${getString(R.string.packets)}: ${parts[2]}\n" +
"${getString(R.string.lost)}: ${parts[3]}\n" + "${getString(R.string.lost)}: ${parts[3]}\n" +
String.format(getString(R.string.jitter), parts[4]) String.format(getString(R.string.jitter), parts[4])
) showAlert.value = true
} else { } else {
Utils.alertView( alertTitle.value = getString(R.string.call_info)
ctx, getString(R.string.call_info), alertMessage.value = getString(R.string.call_info_not_available)
getString(R.string.call_info_not_available) showAlert.value = true
)
} }
}, },
) { ) {
@@ -1539,43 +1562,48 @@ class MainActivity : ComponentActivity() {
private fun callClick(ctx: Context) { private fun callClick(ctx: Context) {
if (viewModel.selectedAor.value != "") { if (viewModel.selectedAor.value != "") {
if (Utils.checkPermissions(ctx, arrayOf(RECORD_AUDIO))) if (Utils.checkPermissions(ctx, arrayOf(RECORD_AUDIO))) {
makeCall() if (Call.inCall())
return
var uriText = callUri.value.trim()
if (uriText.isNotEmpty()) {
val uris = Contact.contactUris(uriText)
if (uris.isEmpty())
makeCall(uriText)
else if (uris.size == 1)
makeCall(uris[0])
else {
items.value = uris
itemAction.value = { index ->
makeCall(uris[index])
}
showSelectItemDialog.value = true
}
}
else {
val ua = UserAgent.ofAor(viewModel.selectedAor.value)!!
val latestPeerUri = CallHistoryNew.aorLatestPeerUri(ua.account.aor)
if (latestPeerUri != null)
callUri.value = Utils.friendlyUri(this, latestPeerUri, ua.account)
}
}
else else
Toast.makeText(applicationContext, R.string.no_calls, Toast.LENGTH_SHORT).show() Toast.makeText(applicationContext, R.string.no_calls, Toast.LENGTH_SHORT).show()
} }
} }
private fun makeCall(lookForContact: Boolean = true) { private fun makeCall(uriText: String) {
val ua = UserAgent.ofAor(viewModel.selectedAor.value) val ua = UserAgent.ofAor(viewModel.selectedAor.value)!!
val aor = ua!!.account.aor val aor = ua.account.aor
if (!Call.inCall()) { val peerUri = if (Utils.isTelNumber(uriText))
var uriText = callUri.value.trim() "tel:$uriText"
if (uriText.isNotEmpty()) { else
if (lookForContact) { uriText
val uris = Contact.contactUris(uriText) val uri = if (Utils.isTelUri(peerUri)) {
if (uris.size == 1)
uriText = uris[0]
else if (uris.size > 1) {
val builder = MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)
with(builder) {
setTitle(R.string.choose_destination_uri)
setItems(uris.toTypedArray()) { _, which ->
callUri.value = uris[which]
makeCall(false)
}
setNeutralButton(getString(R.string.cancel)) { _: DialogInterface, _: Int -> }
show()
}
return
}
}
if (Utils.isTelNumber(uriText))
uriText = "tel:$uriText"
val uri = if (Utils.isTelUri(uriText)) {
if (ua.account.telProvider == "") { if (ua.account.telProvider == "") {
Utils.alertView(this, getString(R.string.notice), alertTitle.value = getString(R.string.notice)
String.format(getString(R.string.no_telephony_provider), aor)) alertMessage.value = String.format(getString(R.string.no_telephony_provider), aor)
showAlert.value = true
return return
} }
Utils.telToSip(uriText, ua.account) Utils.telToSip(uriText, ua.account)
@@ -1583,8 +1611,9 @@ class MainActivity : ComponentActivity() {
Utils.uriComplete(uriText, aor) Utils.uriComplete(uriText, aor)
} }
if (!Utils.checkUri(uri)) { if (!Utils.checkUri(uri)) {
Utils.alertView(this, getString(R.string.notice), alertTitle.value = getString(R.string.notice)
String.format(getString(R.string.invalid_sip_or_tel_uri), uri)) alertMessage.value = String.format(getString(R.string.invalid_sip_or_tel_uri), uri)
showAlert.value = true
} else if (!BaresipService.requestAudioFocus(applicationContext)) { } else if (!BaresipService.requestAudioFocus(applicationContext)) {
Toast.makeText(applicationContext, R.string.audio_focus_denied, Toast.makeText(applicationContext, R.string.audio_focus_denied,
Toast.LENGTH_SHORT).show() Toast.LENGTH_SHORT).show()
@@ -1620,12 +1649,6 @@ class MainActivity : ComponentActivity() {
} }
} }
} }
} else {
val latestPeerUri = CallHistoryNew.aorLatestPeerUri(aor)
if (latestPeerUri != null)
callUri.value = Utils.friendlyUri(this, latestPeerUri, ua.account)
}
}
} }
private fun answer(ctx: Context) { private fun answer(ctx: Context) {
@@ -1668,9 +1691,10 @@ class MainActivity : ComponentActivity() {
val ua = UserAgent.ofAor(viewModel.selectedAor.value)!! val ua = UserAgent.ofAor(viewModel.selectedAor.value)!!
val acc = ua.account val acc = ua.account
if (acc.vmUri != "") { if (acc.vmUri != "") {
val dialogClickListener = DialogInterface.OnClickListener { _, which -> dialogTitle.value = getString(R.string.voicemail_messages)
when (which) { dialogMessage.value = acc.vmMessages(ctx)
DialogInterface.BUTTON_POSITIVE -> { positiveText.value = getString(R.string.listen)
onPositiveClicked.value = {
val i = Intent(ctx, MainActivity::class.java) val i = Intent(ctx, MainActivity::class.java)
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK or i.flags = Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
@@ -1679,17 +1703,9 @@ class MainActivity : ComponentActivity() {
i.putExtra("peer", acc.vmUri) i.putExtra("peer", acc.vmUri)
startActivity(i) startActivity(i)
} }
DialogInterface.BUTTON_NEGATIVE -> { negativeText.value = getString(R.string.cancel)
} onNegativeClicked.value = {}
} showDialog.value = true
}
with(MaterialAlertDialogBuilder(ctx, R.style.AlertDialogTheme)) {
setTitle(R.string.voicemail_messages)
setMessage(acc.vmMessages(ctx))
setPositiveButton(getString(R.string.listen), dialogClickListener)
setNeutralButton(getString(R.string.cancel), dialogClickListener)
show()
}
} }
} }
}, },
@@ -2074,8 +2090,9 @@ class MainActivity : ComponentActivity() {
val ev = action.split(",") val ev = action.split(",")
when (ev[0]) { when (ev[0]) {
"no network" -> { "no network" -> {
Utils.alertView(this, getString(R.string.notice), alertTitle.value = getString(R.string.notice)
getString(R.string.no_network)) alertMessage.value = getString(R.string.no_network)
showAlert.value = true
return return
} }
"call", "dial" -> { "call", "dial" -> {
@@ -2205,8 +2222,11 @@ class MainActivity : ComponentActivity() {
if (event == "stopped") { if (event == "stopped") {
Log.d(TAG, "Handling service event 'stopped' with start error '${params[0]}'") Log.d(TAG, "Handling service event 'stopped' with start error '${params[0]}'")
if (params[0] != "") { if (params[0] != "") {
Utils.alertView(this, getString(R.string.notice), getString(R.string.start_failed)) alertTitle.value = getString(R.string.notice)
} else { alertMessage.value = getString(R.string.start_failed)
showAlert.value = true
}
else {
finishAndRemoveTask() finishAndRemoveTask()
if (restart) if (restart)
reStart() reStart()
@@ -2262,19 +2282,17 @@ class MainActivity : ComponentActivity() {
String.format(getString(R.string.redirect_notice), target), String.format(getString(R.string.redirect_notice), target),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} else { }
with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) { else {
setTitle(R.string.redirect_request) dialogTitle.value = getString(R.string.redirect_request)
setMessage(String.format(getString(R.string.redirect_request_query), target)) dialogMessage.value = String.format(getString(R.string.redirect_request_query), target)
setPositiveButton(getString(R.string.yes)) { dialog, _ -> positiveText.value = getString(R.string.yes)
onPositiveClicked.value = {
redirect(ua, target) redirect(ua, target)
dialog.dismiss()
}
setNeutralButton(getString(R.string.no)) { dialog, _ ->
dialog.dismiss()
}
show()
} }
negativeText.value = getString(R.string.no)
onNegativeClicked.value = {}
showDialog.value = true
} }
showCall(ua) showCall(ua)
} }
@@ -2294,10 +2312,10 @@ class MainActivity : ComponentActivity() {
handleNextEvent("Call $callp to be verified is not found") handleNextEvent("Call $callp to be verified is not found")
return return
} }
with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) { dialogTitle.value = getString(R.string.verify)
setTitle(R.string.verify) dialogMessage.value = String.format(getString(R.string.verify_sas), call.zid)
setMessage(String.format(getString(R.string.verify_sas), ev[1])) positiveText.value = getString(R.string.yes)
setPositiveButton(getString(R.string.yes)) { dialog, _ -> onPositiveClicked.value = {
call.security = if (Api.cmd_exec("zrtp_verify ${ev[2]}") != 0) { call.security = if (Api.cmd_exec("zrtp_verify ${ev[2]}") != 0) {
Log.e(TAG, "Command 'zrtp_verify ${ev[2]}' failed") Log.e(TAG, "Command 'zrtp_verify ${ev[2]}' failed")
R.drawable.locked_yellow R.drawable.locked_yellow
@@ -2307,16 +2325,14 @@ class MainActivity : ComponentActivity() {
call.zid = ev[2] call.zid = ev[2]
if (aor == viewModel.selectedAor.value) if (aor == viewModel.selectedAor.value)
securityIcon.intValue = call.security securityIcon.intValue = call.security
dialog.dismiss()
} }
setNeutralButton(getString(R.string.no)) { dialog, _ -> negativeText.value = getString(R.string.no)
onNegativeClicked.value = {
call.security = R.drawable.locked_yellow call.security = R.drawable.locked_yellow
call.zid = ev[2] call.zid = ev[2]
if (aor == viewModel.selectedAor.value) if (aor == viewModel.selectedAor.value)
securityIcon.intValue = R.drawable.locked_yellow securityIcon.intValue = R.drawable.locked_yellow
dialog.dismiss() onNegativeClicked.value = {}
}
show()
} }
} }
"call verified", "call secure" -> { "call verified", "call secure" -> {
@@ -2344,40 +2360,28 @@ class MainActivity : ComponentActivity() {
} }
val call = Call.ofCallp(callp) val call = Call.ofCallp(callp)
val target = Utils.friendlyUri(this, ev[1], acc) val target = Utils.friendlyUri(this, ev[1], acc)
with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) { dialogTitle.value = if (call != null)
if (call != null) { getString(R.string.transfer_request)
setTitle(R.string.transfer_request) else
setMessage( getString(R.string.call_request)
String.format( dialogMessage.value = if (call != null)
getString(R.string.transfer_request_query), String.format(getString(R.string.transfer_request_query), target)
target else
) String.format(getString(R.string.call_request_query), target)
) positiveText.value = getString(R.string.yes)
} else { onPositiveClicked.value = {
setTitle(R.string.call_request)
setMessage(
String.format(
getString(R.string.call_request_query),
target
)
)
}
setPositiveButton(getString(R.string.yes)) { dialog, _ ->
if (call in Call.calls()) if (call in Call.calls())
acceptTransfer(ua, call!!, ev[1]) acceptTransfer(ua, call!!, ev[1])
else { else
callUri.value = ev[1] makeCall(ev[1])
makeCall()
} }
dialog.dismiss() negativeText.value = getString(R.string.no)
} onNegativeClicked.value = {
setNeutralButton(getString(R.string.no)) { dialog, _ ->
if (call in Call.calls()) if (call in Call.calls())
call!!.notifySipfrag(603, "Decline") call!!.notifySipfrag(603, "Decline")
dialog.dismiss() onNegativeClicked.value = {}
}
show()
} }
showDialog.value = true
} }
"transfer accept" -> { "transfer accept" -> {
val callp = params[1] as Long val callp = params[1] as Long
@@ -2509,9 +2513,11 @@ class MainActivity : ComponentActivity() {
else else
Utils.uriComplete(uriText, ua.account.aor) Utils.uriComplete(uriText, ua.account.aor)
if (!Utils.checkUri(uri)) { if (!Utils.checkUri(uri)) {
Utils.alertView(this@MainActivity, getString(R.string.notice), alertTitle.value = getString(R.string.notice)
String.format(getString(R.string.invalid_sip_or_tel_uri), uri)) alertMessage.value = String.format(getString(R.string.invalid_sip_or_tel_uri), uri)
} else { showAlert.value = true
}
else {
val call = ua.currentCall() val call = ua.currentCall()
if (call != null) { if (call != null) {
if (attended) { if (attended) {
@@ -2519,10 +2525,12 @@ class MainActivity : ComponentActivity() {
call.referTo = uri call.referTo = uri
call(ua, uri, call) call(ua, uri, call)
} }
} else { }
else {
if (!call.transfer(uri)) { if (!call.transfer(uri)) {
Utils.alertView(this@MainActivity, getString(R.string.notice), alertTitle.value = getString(R.string.notice)
String.format(getString(R.string.transfer_failed))) alertMessage.value = getString(R.string.transfer_failed)
showAlert.value = true
} }
} }
showCall(ua) showCall(ua)
@@ -2550,31 +2558,35 @@ class MainActivity : ComponentActivity() {
val zipFilePath = BaresipService.filesPath + "/$zipFile" val zipFilePath = BaresipService.filesPath + "/$zipFile"
if (!Utils.zip(files, zipFile)) { if (!Utils.zip(files, zipFile)) {
Log.w(TAG, "Failed to write zip file '$zipFile'") Log.w(TAG, "Failed to write zip file '$zipFile'")
Utils.alertView(this, getString(R.string.error), alertTitle.value = getString(R.string.error)
String.format(getString(R.string.backup_failed), alertMessage.value = String.format(getString(R.string.backup_failed),
Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))) Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))
showAlert.value = true
downloadsOutputUri = null downloadsOutputUri = null
return return
} }
val content = Utils.getFileContents(zipFilePath) val content = Utils.getFileContents(zipFilePath)
if (content == null) { if (content == null) {
Log.w(TAG, "Failed to read zip file '$zipFile'") Log.w(TAG, "Failed to read zip file '$zipFile'")
Utils.alertView(this, getString(R.string.error), alertTitle.value = getString(R.string.error)
String.format(getString(R.string.backup_failed), alertMessage.value = String.format(getString(R.string.backup_failed),
Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))) Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))
showAlert.value = true
downloadsOutputUri = null downloadsOutputUri = null
return return
} }
if (!Utils.encryptToUri(applicationContext, downloadsOutputUri!!, content, password)) { if (!Utils.encryptToUri(applicationContext, downloadsOutputUri!!, content, password)) {
Utils.alertView(this, getString(R.string.error), alertTitle.value = getString(R.string.error)
String.format(getString(R.string.backup_failed), alertMessage.value = String.format(getString(R.string.backup_failed),
Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))) Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))
showAlert.value = true
downloadsOutputUri = null downloadsOutputUri = null
return return
} }
Utils.alertView(this, getString(R.string.info), alertTitle.value = getString(R.string.info)
String.format(getString(R.string.backed_up), alertMessage.value = String.format(getString(R.string.backed_up),
Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))) Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))
showAlert.value = true
Utils.deleteFile(File(zipFilePath)) Utils.deleteFile(File(zipFilePath))
downloadsOutputUri = null downloadsOutputUri = null
} }
@@ -2584,34 +2596,27 @@ class MainActivity : ComponentActivity() {
val zipFilePath = BaresipService.filesPath + "/$zipFile" val zipFilePath = BaresipService.filesPath + "/$zipFile"
val zipData = Utils.decryptFromUri(applicationContext, downloadsInputUri!!, password) val zipData = Utils.decryptFromUri(applicationContext, downloadsInputUri!!, password)
if (zipData == null) { if (zipData == null) {
Utils.alertView( alertTitle.value = getString(R.string.error)
this, getString(R.string.error), alertMessage.value = String.format(getString(R.string.restore_failed),
String.format( Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))
getString(R.string.restore_failed), showAlert.value = true
Utils.fileNameOfUri(applicationContext, downloadsInputUri!!)
)
)
downloadsOutputUri = null downloadsOutputUri = null
return return
} }
if (!Utils.putFileContents(zipFilePath, zipData)) { if (!Utils.putFileContents(zipFilePath, zipData)) {
Log.w(TAG, "Failed to write zip file '$zipFile'") Log.w(TAG, "Failed to write zip file '$zipFile'")
Utils.alertView( alertTitle.value = getString(R.string.error)
this, getString(R.string.error), alertMessage.value = String.format(getString(R.string.restore_failed),
String.format( Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))
getString(R.string.restore_failed), showAlert.value = true
Utils.fileNameOfUri(applicationContext, downloadsInputUri!!)
)
)
downloadsOutputUri = null downloadsOutputUri = null
return return
} }
if (!Utils.unZip(zipFilePath)) { if (!Utils.unZip(zipFilePath)) {
Log.w(TAG, "Failed to unzip file '$zipFile'") Log.w(TAG, "Failed to unzip file '$zipFile'")
Utils.alertView( alertTitle.value = getString(R.string.error)
this, getString(R.string.error), alertMessage.value = String.format(getString(R.string.restore_unzip_failed), "baresip", BuildConfig.VERSION_NAME)
String.format(getString(R.string.restore_unzip_failed), "baresip", "60.0.0") showAlert.value = true
)
downloadsOutputUri = null downloadsOutputUri = null
return return
} }
@@ -2627,18 +2632,16 @@ class MainActivity : ComponentActivity() {
h.recording = arrayOf("", "") h.recording = arrayOf("", "")
CallHistoryNew.save() CallHistoryNew.save()
with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) { dialogTitle.value = getString(R.string.info)
setTitle(getString(R.string.info)) dialogMessage.value = getString(R.string.restored)
setMessage(getString(R.string.restored)) positiveText.value = getString(R.string.restart)
setPositiveButton(getText(R.string.restart)) { dialog, _ -> onPositiveClicked.value = {
quitRestart(true) quitRestart(true)
dialog.dismiss()
}
setNeutralButton(getText(R.string.cancel)) { dialog, _ ->
dialog.dismiss()
}
show()
} }
negativeText.value = getString(R.string.cancel)
onNegativeClicked.value = {}
showDialog.value = true
downloadsOutputUri = null downloadsOutputUri = null
} }
@@ -8,8 +8,6 @@ import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.media.AudioAttributes import android.media.AudioAttributes
import android.media.AudioDeviceInfo import android.media.AudioDeviceInfo
import android.media.AudioManager import android.media.AudioManager
@@ -25,21 +23,14 @@ import android.provider.DocumentsContract
import android.provider.MediaStore import android.provider.MediaStore
import android.provider.OpenableColumns import android.provider.OpenableColumns
import android.text.format.DateUtils import android.text.format.DateUtils
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.createBitmap
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.text.isDigitsOnly import androidx.core.text.isDigitsOnly
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
import java.io.File import java.io.File
@@ -92,18 +83,6 @@ object Utils {
return result return result
} }
fun alertView(context: Context, title: String, message: String, action: () -> (Unit) = {}) {
with(MaterialAlertDialogBuilder(context, R.style.AlertDialogTheme)) {
setTitle(title)
setMessage(message)
setPositiveButton(R.string.ok) { dialog, _ ->
dialog.dismiss()
action()
}
show()
}
}
fun uriHostPart(uri: String): String { fun uriHostPart(uri: String): String {
return if (uri.contains("@")) { return if (uri.contains("@")) {
uri.substringAfter("@") uri.substringAfter("@")
@@ -407,46 +386,6 @@ object Utils {
return Regex("^[-a-zA-Z0-9.!%*_+`'~]+\$").matches(token) return Regex("^[-a-zA-Z0-9.!%*_+`'~]+\$").matches(token)
} }
fun setAvatar(ctx: Context, imageView: ImageView, textView: TextView, uri: String) {
when (val contact = Contact.findContact(uri)) {
is Contact.BaresipContact -> {
val avatarImage = contact.avatarImage
if (avatarImage != null) {
imageView.setImageBitmap(avatarImage)
} else {
textView.background.setTint(contact.color)
if (contact.name.isNotEmpty())
textView.text = "${contact.name[0]}"
else
textView.text = ""
imageView.setImageBitmap(bitmapFromView(textView))
}
}
is Contact.AndroidContact -> {
val thumbnailUri = contact.thumbnailUri
if (thumbnailUri != null) {
imageView.setImageURI(thumbnailUri)
} else {
textView.background.setTint(contact.color)
if (contact.name.isNotEmpty())
textView.text = "${contact.name[0]}"
else
textView.text = ""
imageView.setImageBitmap(bitmapFromView(textView))
}
}
null -> {
val bitmap = BitmapFactory.decodeResource(ctx.resources, R.drawable.person_image)
imageView.setImageBitmap(bitmap)
}
}
}
@Suppress("unused") @Suppress("unused")
fun checkIfName(name: String): Boolean { fun checkIfName(name: String): Boolean {
if ((name.length < 2) || !name.first().isLetter()) return false if ((name.length < 2) || !name.first().isLetter()) return false
@@ -883,13 +822,6 @@ object Utils {
Configuration.UI_MODE_NIGHT_YES Configuration.UI_MODE_NIGHT_YES
} }
@Composable
fun isNightMode() = when (AppCompatDelegate.getDefaultNightMode()) {
AppCompatDelegate.MODE_NIGHT_NO -> false
AppCompatDelegate.MODE_NIGHT_YES -> true
else -> isSystemInDarkTheme()
}
fun relativeTime(ctx: Context, time: GregorianCalendar): String { fun relativeTime(ctx: Context, time: GregorianCalendar): String {
return if (DateUtils.isToday(time.timeInMillis)) { return if (DateUtils.isToday(time.timeInMillis)) {
val fmt = DateFormat.getTimeInstance(DateFormat.SHORT) val fmt = DateFormat.getTimeInstance(DateFormat.SHORT)
@@ -907,14 +839,6 @@ object Utils {
} }
} }
private fun bitmapFromView(view: View): Bitmap {
val bitmap = createBitmap(view.layoutParams.width, view.layoutParams.height)
val canvas = Canvas(bitmap)
view.layout(0, 0, view.layoutParams.width, view.layoutParams.height)
view.draw(canvas)
return bitmap
}
fun isSpeakerPhoneOn(am: AudioManager): Boolean { fun isSpeakerPhoneOn(am: AudioManager): Boolean {
return if (Build.VERSION.SDK_INT >= 31) return if (Build.VERSION.SDK_INT >= 31)
am.communicationDevice!!.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER am.communicationDevice!!.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
-38
View File
@@ -1,38 +0,0 @@
<resources>
<style name="AlertDialogTheme" parent="@style/ThemeOverlay.MaterialComponents.MaterialAlertDialog">
<item name="materialAlertDialogTitleTextStyle">@style/Alert.Title</item>
<item name="materialAlertDialogBodyTextStyle">@style/Alert.Message</item>
<item name="buttonBarPositiveButtonStyle">@style/Alert.Button.Positive</item>
<item name="buttonBarNegativeButtonStyle">@style/Alert.Button.Positive</item>
<item name="buttonBarNeutralButtonStyle">@style/Alert.Button.Neutral</item>
<item name="android:windowBackground">@color/colorCardBackground</item>
<item name="android:background">@color/colorCardBackground</item>
<item name="android:textSize">14sp</item>
</style>
<style name="Alert.Title" parent="@style/MaterialAlertDialog.Material3.Title.Text">
<item name="android:textColor">@color/colorAlert</item>
<item name="android:textSize">20sp</item>
</style>
<style name="Alert.Message" parent="@style/MaterialAlertDialog.Material3.Body.Text">
<item name="android:textColor">@color/colorItemText</item>
<item name="android:textSize">16sp</item>
</style>
<style name="Alert.Button.Positive" parent="Widget.Material3.Button.TextButton">
<item name="android:textColor">@color/colorAlert</item>
<item name="rippleColor">@color/colorAccent</item>
<item name="android:textSize">14sp</item>
<item name="android:textAllCaps">true</item>
</style>
<style name="Alert.Button.Neutral" parent="Widget.Material3.Button.TextButton">
<item name="rippleColor">@color/colorAccent</item>
<item name="android:textColor">@color/colorGray</item>
<item name="android:textSize">14sp</item>
<item name="android:textAllCaps">true</item>
</style>
</resources>