Completed composting alert of dialogs (testing still needed)
UI improvements
This commit is contained in:
@ -63,6 +63,7 @@ import androidx.compose.ui.unit.sp
|
||||
import com.tutpro.baresip.BaresipService.Companion.uas
|
||||
import com.tutpro.baresip.CustomElements.AlertDialog
|
||||
import com.tutpro.baresip.CustomElements.Checkbox
|
||||
import com.tutpro.baresip.CustomElements.LabelText
|
||||
import com.tutpro.baresip.CustomElements.verticalScrollbar
|
||||
|
||||
class AccountActivity : ComponentActivity() {
|
||||
@ -334,7 +335,10 @@ class AccountActivity : ComponentActivity() {
|
||||
fontSize = 18.sp,
|
||||
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 },
|
||||
textStyle = TextStyle(
|
||||
fontSize = 18.sp, color = LocalCustomColors.current.itemText),
|
||||
label = { Text(stringResource(R.string.nickname)) },
|
||||
label = { LabelText(stringResource(R.string.nickname)) },
|
||||
keyboardOptions = KeyboardOptions(
|
||||
capitalization = KeyboardCapitalization.Sentences,
|
||||
keyboardType = KeyboardType.Text),
|
||||
@ -393,7 +397,7 @@ class AccountActivity : ComponentActivity() {
|
||||
showAlert.value = true },
|
||||
textStyle = TextStyle(
|
||||
fontSize = 18.sp, color = LocalCustomColors.current.itemText),
|
||||
label = { Text(stringResource(R.string.display_name)) },
|
||||
label = { LabelText(stringResource(R.string.display_name)) },
|
||||
keyboardOptions = KeyboardOptions(
|
||||
capitalization = KeyboardCapitalization.Sentences,
|
||||
keyboardType = KeyboardType.Text),
|
||||
@ -424,7 +428,7 @@ class AccountActivity : ComponentActivity() {
|
||||
showAlert.value = true },
|
||||
textStyle = TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
@ -474,7 +478,7 @@ class AccountActivity : ComponentActivity() {
|
||||
showAlert.value = true },
|
||||
textStyle = TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
@ -509,7 +513,7 @@ class AccountActivity : ComponentActivity() {
|
||||
singleLine = true,
|
||||
textStyle = TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
@ -531,7 +535,7 @@ class AccountActivity : ComponentActivity() {
|
||||
singleLine = true,
|
||||
textStyle = TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
@ -587,7 +591,7 @@ class AccountActivity : ComponentActivity() {
|
||||
showAlert.value = true },
|
||||
textStyle = TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
@ -764,7 +768,7 @@ class AccountActivity : ComponentActivity() {
|
||||
showAlert.value = true },
|
||||
textStyle = TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
@ -793,7 +797,7 @@ class AccountActivity : ComponentActivity() {
|
||||
showAlert.value = true },
|
||||
textStyle = TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
@ -847,7 +851,7 @@ class AccountActivity : ComponentActivity() {
|
||||
showAlert.value = true },
|
||||
textStyle = TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
@ -1111,7 +1115,7 @@ class AccountActivity : ComponentActivity() {
|
||||
showAlert.value = true },
|
||||
textStyle = TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
@ -1140,7 +1144,7 @@ class AccountActivity : ComponentActivity() {
|
||||
showAlert.value = true },
|
||||
textStyle = TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
@ -1169,7 +1173,7 @@ class AccountActivity : ComponentActivity() {
|
||||
showAlert.value = true },
|
||||
textStyle = TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
@ -56,6 +56,7 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.tutpro.baresip.CustomElements.AlertDialog
|
||||
import com.tutpro.baresip.CustomElements.LabelText
|
||||
import com.tutpro.baresip.CustomElements.verticalScrollbar
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -283,12 +284,7 @@ class AccountsActivity : ComponentActivity() {
|
||||
)
|
||||
}
|
||||
},
|
||||
label = {
|
||||
CustomElements.Text(
|
||||
text = getString(R.string.new_account),
|
||||
fontSize = 18.sp
|
||||
)
|
||||
},
|
||||
label = { LabelText(stringResource(R.string.new_account)) },
|
||||
textStyle = TextStyle(fontSize = 18.sp),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Text,
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
package com.tutpro.baresip
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
@ -103,7 +102,7 @@ class AudioActivity : ComponentActivity() {
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = LocalCustomColors.current.background
|
||||
) {
|
||||
AudioScreen(this, title) { goBack() }
|
||||
AudioScreen(title) { goBack() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -112,7 +111,7 @@ class AudioActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AudioScreen(ctx: Context, title: String, navigateBack: () -> Unit) {
|
||||
fun AudioScreen(title: String, navigateBack: () -> Unit) {
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
@ -448,7 +447,7 @@ class AudioActivity : ComponentActivity() {
|
||||
},
|
||||
textStyle = androidx.compose.ui.text.TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
@ -75,6 +75,7 @@ import androidx.compose.ui.unit.sp
|
||||
import androidx.core.graphics.scale
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
import com.tutpro.baresip.CustomElements.AlertDialog
|
||||
import com.tutpro.baresip.CustomElements.Checkbox
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
@ -86,7 +87,6 @@ class BaresipContactActivity : ComponentActivity() {
|
||||
private var new = false
|
||||
private var favorite = false
|
||||
private var android = false
|
||||
private var avatarImage: Bitmap? = null
|
||||
private var newName = ""
|
||||
private var newUri = ""
|
||||
private var newFavorite = false
|
||||
@ -94,9 +94,17 @@ class BaresipContactActivity : ComponentActivity() {
|
||||
private var uriOrName = ""
|
||||
private var color = 0
|
||||
private var id: Long = 0
|
||||
private var newAvatar = ""
|
||||
private var newId: Long = 0
|
||||
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) {
|
||||
override fun handleOnBackPressed() {
|
||||
goBack()
|
||||
@ -117,23 +125,29 @@ class BaresipContactActivity : ComponentActivity() {
|
||||
name = ""
|
||||
uri = intent.getStringExtra("uri")!!
|
||||
favorite = false
|
||||
avatarImage = null
|
||||
android = BaresipService.contactsMode == "android"
|
||||
title = getString(R.string.new_contact)
|
||||
uriOrName = uri
|
||||
color = Utils.randomColor()
|
||||
id = System.currentTimeMillis()
|
||||
} else {
|
||||
newId = id
|
||||
}
|
||||
else {
|
||||
name = intent.getStringExtra("name")!!
|
||||
val contact = Contact.baresipContact(name)!!
|
||||
uri = contact.uri
|
||||
favorite = contact.favorite
|
||||
avatarImage = contact.avatarImage
|
||||
android = false
|
||||
title = name
|
||||
uriOrName = name
|
||||
color = contact.color
|
||||
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 {
|
||||
@ -159,7 +173,7 @@ class BaresipContactActivity : ComponentActivity() {
|
||||
containerColor = LocalCustomColors.current.background,
|
||||
topBar = { TopAppBar(ctx, title, navigateBack) },
|
||||
content = { contentPadding ->
|
||||
ContactContent(ctx, contentPadding)
|
||||
ContactContent(contentPadding)
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -203,13 +217,60 @@ class BaresipContactActivity : ComponentActivity() {
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun ContactContent(ctx: Context, contentPadding: PaddingValues) {
|
||||
fun ContactContent(contentPadding: PaddingValues) {
|
||||
|
||||
var avatarType by remember { mutableStateOf(if (avatarImage != null) "image" else "text") }
|
||||
var textAvatarText by remember { mutableStateOf("") }
|
||||
var textAvatarColor by remember { mutableIntStateOf(0) }
|
||||
var imageAvatarUri by remember { mutableStateOf("") }
|
||||
if (showAlert.value) {
|
||||
AlertDialog(
|
||||
showDialog = showAlert,
|
||||
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 =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) {
|
||||
if (it != null)
|
||||
@ -224,208 +285,151 @@ class BaresipContactActivity : ComponentActivity() {
|
||||
ExifInterface.ORIENTATION_NORMAL
|
||||
)
|
||||
val rotatedBitmap = rotateBitmap(scaledBitmap, orientation)
|
||||
tmpFile = File(
|
||||
BaresipService.filesPath + "/tmp",
|
||||
"${System.currentTimeMillis()}.png"
|
||||
)
|
||||
if (tmpFile != null && tmpFile!!.exists())
|
||||
Utils.deleteFile(tmpFile!!)
|
||||
newId = System.currentTimeMillis()
|
||||
tmpFile = File(BaresipService.filesPath, "${newId}.png")
|
||||
if (Utils.saveBitmap(rotatedBitmap, tmpFile!!)) {
|
||||
newAvatar = "image"
|
||||
avatarType = newAvatar
|
||||
imageAvatarUri = Uri.fromFile(tmpFile).toString()
|
||||
imageAvatarUri.value = Uri.fromFile(tmpFile).toString()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Could not read avatar image: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TextAvatar(size: Int) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.size(size.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.size(96.dp)
|
||||
.combinedClickable(
|
||||
onClick = {
|
||||
avatarRequest.launch("image/*")
|
||||
},
|
||||
onLongClick = {
|
||||
textAvatarColor.intValue = Utils.randomColor()
|
||||
color = textAvatarColor.intValue
|
||||
imageAvatarUri.value = ""
|
||||
}
|
||||
),
|
||||
) {
|
||||
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(
|
||||
Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
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(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.size(96.dp)
|
||||
.combinedClickable(
|
||||
onClick = {
|
||||
avatarRequest.launch("image/*")
|
||||
},
|
||||
onLongClick = {
|
||||
textAvatarColor = Utils.randomColor()
|
||||
color = textAvatarColor
|
||||
newAvatar = "text"
|
||||
avatarType = newAvatar
|
||||
}
|
||||
),
|
||||
) {
|
||||
if (avatarType == "text")
|
||||
TextAvatar(96)
|
||||
if (imageAvatarUri.value == "") {
|
||||
textAvatarText.value = if (new)
|
||||
"?"
|
||||
else
|
||||
ImageAvatar(96)
|
||||
"${name[0]}"
|
||||
textAvatarColor.intValue = color
|
||||
TextAvatar(96)
|
||||
} else
|
||||
ImageAvatar(96)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactName() {
|
||||
val focusRequester = FocusRequester()
|
||||
var contactName by remember { mutableStateOf(name) }
|
||||
newName = contactName
|
||||
OutlinedTextField(
|
||||
value = contactName,
|
||||
placeholder = { Text(stringResource(R.string.contact_name)) },
|
||||
onValueChange = {
|
||||
contactName = it
|
||||
newName = contactName
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().focusRequester(focusRequester),
|
||||
textStyle = androidx.compose.ui.text.TextStyle(
|
||||
fontSize = 18.sp, color = LocalCustomColors.current.itemText
|
||||
),
|
||||
label = { Text(stringResource(R.string.contact_name)) },
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Text,
|
||||
capitalization = KeyboardCapitalization.Sentences
|
||||
)
|
||||
)
|
||||
LaunchedEffect(new) {
|
||||
if (new)
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactUri() {
|
||||
var contactUri by remember { mutableStateOf(uri) }
|
||||
newUri = contactUri
|
||||
OutlinedTextField(
|
||||
value = contactUri,
|
||||
placeholder = { Text(stringResource(R.string.user_domain_or_number)) },
|
||||
onValueChange = {
|
||||
contactUri = it
|
||||
newUri = contactUri
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textStyle = androidx.compose.ui.text.TextStyle(
|
||||
fontSize = 18.sp, color = LocalCustomColors.current.itemText
|
||||
),
|
||||
label = { Text(stringResource(R.string.sip_or_tel_uri)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Favorite() {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Start
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.favorite),
|
||||
modifier = Modifier.weight(1f)
|
||||
.clickable {
|
||||
alertTitle.value = getString(R.string.favorite)
|
||||
alertMessage.value = getString(R.string.favorite_help)
|
||||
showAlert.value = true
|
||||
},
|
||||
color = LocalCustomColors.current.itemText,
|
||||
)
|
||||
var favoriteContact by remember { mutableStateOf(favorite) }
|
||||
newFavorite = favoriteContact
|
||||
Checkbox(
|
||||
checked = favoriteContact,
|
||||
onCheckedChange = {
|
||||
favoriteContact = it
|
||||
newFavorite = favoriteContact
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun contactName() {
|
||||
val focusRequester = FocusRequester()
|
||||
var contactName by remember { mutableStateOf(name) }
|
||||
newName = contactName
|
||||
OutlinedTextField(
|
||||
value = contactName,
|
||||
placeholder = { Text(stringResource(R.string.contact_name)) },
|
||||
onValueChange = {
|
||||
contactName = it
|
||||
newName = contactName
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth().focusRequester(focusRequester),
|
||||
textStyle = androidx.compose.ui.text.TextStyle(
|
||||
fontSize = 18.sp, color = LocalCustomColors.current.itemText
|
||||
),
|
||||
label = { Text(stringResource(R.string.contact_name)) },
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Text,
|
||||
capitalization = KeyboardCapitalization.Sentences
|
||||
)
|
||||
)
|
||||
LaunchedEffect(new) {
|
||||
if (new)
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun contactUri() {
|
||||
var contactUri by remember { mutableStateOf(uri) }
|
||||
newUri = contactUri
|
||||
OutlinedTextField(
|
||||
value = contactUri,
|
||||
placeholder = { Text(stringResource(R.string.user_domain_or_number)) },
|
||||
onValueChange = {
|
||||
contactUri = it
|
||||
newUri = contactUri
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textStyle = androidx.compose.ui.text.TextStyle(
|
||||
fontSize = 18.sp, color = LocalCustomColors.current.itemText
|
||||
),
|
||||
label = { Text(stringResource(R.string.sip_or_tel_uri)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun favorite(ctx: Context) {
|
||||
@Composable
|
||||
fun Android() {
|
||||
if (new && BaresipService.contactsMode != "baresip")
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Start
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.favorite),
|
||||
modifier = Modifier.weight(1f)
|
||||
.clickable {
|
||||
Utils.alertView(
|
||||
ctx, getString(R.string.favorite), getString(R.string.favorite_help)
|
||||
)
|
||||
},
|
||||
color = LocalCustomColors.current.itemText,
|
||||
text = stringResource(R.string.android),
|
||||
modifier = Modifier.weight(1f),
|
||||
color = LocalCustomColors.current.itemText
|
||||
)
|
||||
var favoriteContact by remember { mutableStateOf(favorite) }
|
||||
newFavorite = favoriteContact
|
||||
var androidContact by remember { mutableStateOf(android) }
|
||||
newAndroid = androidContact
|
||||
Checkbox(
|
||||
checked = favoriteContact,
|
||||
checked = androidContact,
|
||||
onCheckedChange = {
|
||||
favoriteContact = it
|
||||
newFavorite = favoriteContact
|
||||
if (BaresipService.contactsMode != "android") {
|
||||
androidContact = it
|
||||
newAndroid = androidContact
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun android() {
|
||||
if (new && BaresipService.contactsMode != "baresip")
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Start
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.android),
|
||||
modifier = Modifier.weight(1f),
|
||||
color = LocalCustomColors.current.itemText
|
||||
)
|
||||
var androidContact by remember { mutableStateOf(android) }
|
||||
newAndroid = androidContact
|
||||
Checkbox(
|
||||
checked = androidContact,
|
||||
onCheckedChange = {
|
||||
if (BaresipService.contactsMode != "android") {
|
||||
androidContact = it
|
||||
newAndroid = androidContact
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -439,20 +443,18 @@ class BaresipContactActivity : ComponentActivity() {
|
||||
else
|
||||
"sip:$newUri"
|
||||
if (!Utils.checkUri(newUri)) {
|
||||
Utils.alertView(
|
||||
ctx, getString(R.string.notice),
|
||||
String.format(getString(R.string.invalid_sip_or_tel_uri), newUri)
|
||||
)
|
||||
alertTitle.value = getString(R.string.notice)
|
||||
alertMessage.value = String.format(getString(R.string.invalid_sip_or_tel_uri), newUri)
|
||||
showAlert.value = true
|
||||
return
|
||||
}
|
||||
|
||||
newName = newName.trim()
|
||||
if (newName == "") newName = newUri.substringAfter(":")
|
||||
if (!Utils.checkName(newName)) {
|
||||
Utils.alertView(
|
||||
ctx, getString(R.string.notice),
|
||||
String.format(getString(R.string.invalid_contact), newName)
|
||||
)
|
||||
alertTitle.value = getString(R.string.notice)
|
||||
alertMessage.value = String.format(getString(R.string.invalid_contact), newName)
|
||||
showAlert.value = true
|
||||
return
|
||||
}
|
||||
|
||||
@ -462,39 +464,25 @@ class BaresipContactActivity : ComponentActivity() {
|
||||
(uriOrName != newName) && Contact.nameExists(newName, BaresipService.contacts, false)
|
||||
}
|
||||
if (alert) {
|
||||
Utils.alertView(
|
||||
ctx, getString(R.string.notice),
|
||||
String.format(getString(R.string.contact_already_exists), newName))
|
||||
alertTitle.value = getString(R.string.notice)
|
||||
alertMessage.value = String.format(getString(R.string.contact_already_exists), newName)
|
||||
showAlert.value = true
|
||||
return
|
||||
}
|
||||
|
||||
val contact: Contact.BaresipContact
|
||||
val contact: Contact.BaresipContact =
|
||||
Contact.BaresipContact(newName, newUri, color, newId, newFavorite)
|
||||
|
||||
if (new) {
|
||||
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) {
|
||||
contact.avatarImage = null
|
||||
Utils.deleteFile(File(BaresipService.filesPath, "${contact.id}.png"))
|
||||
}
|
||||
}
|
||||
"image" -> {
|
||||
val imageFilePath = BaresipService.filesPath + "/${contact.id}.png"
|
||||
val imageFile = File(imageFilePath)
|
||||
tmpFile!!.copyTo(target = imageFile, overwrite = true)
|
||||
contact.avatarImage = BitmapFactory.decodeFile(imageFilePath)
|
||||
if (imageAvatarUri.value == "") {
|
||||
if (contact.avatarImage != null) {
|
||||
contact.avatarImage = null
|
||||
Utils.deleteFile(File(BaresipService.filesPath, "${newId}.png"))
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
val imageFilePath = BaresipService.filesPath + "/${newId}.png"
|
||||
contact.avatarImage = BitmapFactory.decodeFile(imageFilePath)
|
||||
}
|
||||
|
||||
if (newAndroid)
|
||||
addOrUpdateAndroidContact(ctx, contact)
|
||||
@ -502,7 +490,7 @@ class BaresipContactActivity : ComponentActivity() {
|
||||
if (new)
|
||||
Contact.addBaresipContact(contact)
|
||||
else
|
||||
Contact.updateBaresipContact(contact)
|
||||
Contact.updateBaresipContact(id, contact)
|
||||
}
|
||||
|
||||
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.SoftwareKeyboardController
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
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.lifecycle.Observer
|
||||
import com.tutpro.baresip.CustomElements.AlertDialog
|
||||
import com.tutpro.baresip.CustomElements.LabelText
|
||||
import com.tutpro.baresip.CustomElements.Text
|
||||
import com.tutpro.baresip.CustomElements.verticalScrollbar
|
||||
import kotlinx.coroutines.launch
|
||||
@ -530,7 +532,7 @@ class ChatActivity : ComponentActivity() {
|
||||
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),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
capitalization = KeyboardCapitalization.Sentences,
|
||||
|
||||
@ -78,6 +78,7 @@ import coil.compose.AsyncImage
|
||||
import com.tutpro.baresip.BaresipService.Companion.contactNames
|
||||
import com.tutpro.baresip.CustomElements.AlertDialog
|
||||
import com.tutpro.baresip.CustomElements.ImageAvatar
|
||||
import com.tutpro.baresip.CustomElements.LabelText
|
||||
import com.tutpro.baresip.CustomElements.Text
|
||||
import com.tutpro.baresip.CustomElements.TextAvatar
|
||||
import com.tutpro.baresip.CustomElements.verticalScrollbar
|
||||
@ -574,9 +575,9 @@ class ChatsActivity: ComponentActivity() {
|
||||
)
|
||||
},
|
||||
label = {
|
||||
Text(
|
||||
text = getString(R.string.new_chat_peer),
|
||||
fontSize = 18.sp
|
||||
LabelText(
|
||||
text = stringResource(R.string.new_chat_peer),
|
||||
fontSize = 16.sp
|
||||
)
|
||||
},
|
||||
textStyle = TextStyle(
|
||||
|
||||
@ -65,6 +65,7 @@ import androidx.compose.ui.unit.sp
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.tutpro.baresip.CustomElements.AlertDialog
|
||||
import com.tutpro.baresip.CustomElements.Checkbox
|
||||
import com.tutpro.baresip.CustomElements.LabelText
|
||||
import com.tutpro.baresip.CustomElements.verticalScrollbar
|
||||
import com.tutpro.baresip.Utils.copyInputStreamToFile
|
||||
import java.io.File
|
||||
@ -476,7 +477,7 @@ class ConfigActivity : ComponentActivity() {
|
||||
},
|
||||
textStyle = androidx.compose.ui.text.TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
@ -573,7 +574,7 @@ class ConfigActivity : ComponentActivity() {
|
||||
},
|
||||
textStyle = androidx.compose.ui.text.TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
@ -781,7 +782,7 @@ class ConfigActivity : ComponentActivity() {
|
||||
},
|
||||
textStyle = androidx.compose.ui.text.TextStyle(
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
@ -272,13 +272,11 @@ sealed class Contact {
|
||||
contactsUpdate()
|
||||
}
|
||||
|
||||
fun updateBaresipContact(contact: BaresipContact) {
|
||||
fun updateBaresipContact(id: Long, contact: BaresipContact) {
|
||||
val updatedContacts = BaresipService.baresipContacts.value.toMutableList()
|
||||
val updatedContact = contact.copy() as BaresipContact
|
||||
updatedContact.id = System.currentTimeMillis()
|
||||
updatedContacts.removeIf { it.id == contact.id }
|
||||
updatedContacts.add(updatedContact)
|
||||
BaresipService.baresipContacts.value = updatedContacts
|
||||
updatedContacts.removeIf { it.id == id }
|
||||
updatedContacts.add(contact)
|
||||
BaresipService.baresipContacts.value = updatedContacts.toList()
|
||||
saveBaresipContacts()
|
||||
contactsUpdate()
|
||||
}
|
||||
|
||||
@ -369,7 +369,7 @@ class ContactsActivity : ComponentActivity() {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Edit,
|
||||
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.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.Intent.ACTION_CALL
|
||||
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.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.layout.size
|
||||
@ -130,13 +129,15 @@ import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.tutpro.baresip.BaresipService.Companion.contactNames
|
||||
import com.tutpro.baresip.BaresipService.Companion.uas
|
||||
import com.tutpro.baresip.BaresipService.Companion.uasStatus
|
||||
import com.tutpro.baresip.CustomElements.AlertDialog
|
||||
import com.tutpro.baresip.CustomElements.Checkbox
|
||||
import com.tutpro.baresip.CustomElements.DropdownMenu
|
||||
import com.tutpro.baresip.CustomElements.LabelText
|
||||
import com.tutpro.baresip.CustomElements.PullToRefreshBox
|
||||
import com.tutpro.baresip.CustomElements.SelectableAlertDialog
|
||||
import com.tutpro.baresip.CustomElements.Text
|
||||
import com.tutpro.baresip.CustomElements.verticalScrollbar
|
||||
import kotlinx.coroutines.delay
|
||||
@ -220,6 +221,22 @@ class MainActivity : ComponentActivity() {
|
||||
private var passwordAccounts = mutableListOf<String>()
|
||||
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) {
|
||||
override fun handleOnBackPressed() {
|
||||
moveTaskToBack(true)
|
||||
@ -330,18 +347,14 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
configRequest = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if ((it.data != null) && it.data!!.hasExtra("restart")) {
|
||||
with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
|
||||
setTitle(R.string.restart_request)
|
||||
setMessage(getString(R.string.config_restart))
|
||||
setPositiveButton(getText(R.string.restart)) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
quitRestart(true)
|
||||
}
|
||||
setNeutralButton(getText(R.string.cancel)) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
show()
|
||||
dialogTitle.value = getString(R.string.restart_request)
|
||||
dialogMessage.value = getString(R.string.config_restart)
|
||||
positiveText.value = getString(R.string.restart)
|
||||
onPositiveClicked.value = {
|
||||
quitRestart(true)
|
||||
}
|
||||
negativeText.value = getString(R.string.cancel)
|
||||
showDialog.value = true
|
||||
}
|
||||
val displayTheme = Preferences(applicationContext).displayTheme
|
||||
if (displayTheme != AppCompatDelegate.getDefaultNightMode()) {
|
||||
@ -436,21 +449,26 @@ class MainActivity : ComponentActivity() {
|
||||
shouldShow.add(permission.key)
|
||||
}
|
||||
}
|
||||
if (denied.contains(POST_NOTIFICATIONS) &&
|
||||
!shouldShow.contains(POST_NOTIFICATIONS)) {
|
||||
with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
|
||||
setTitle(getString(R.string.notice))
|
||||
setMessage(getString(R.string.no_notifications))
|
||||
setPositiveButton(getString(R.string.ok)) { _, _ ->
|
||||
quitRestart(false)
|
||||
}
|
||||
show()
|
||||
if (denied.contains(POST_NOTIFICATIONS) && !shouldShow.contains(POST_NOTIFICATIONS)) {
|
||||
dialogTitle.value = getString(R.string.notice)
|
||||
dialogMessage.value = getString(R.string.no_notifications)
|
||||
positiveText.value = getString(R.string.ok)
|
||||
onPositiveClicked.value = {
|
||||
quitRestart(false)
|
||||
}
|
||||
negativeText.value = ""
|
||||
showDialog.value = true
|
||||
} else {
|
||||
if (shouldShow.isNotEmpty())
|
||||
Utils.alertView(this, getString(R.string.permissions_rationale),
|
||||
getString(R.string.audio_permissions)
|
||||
) { requestPermissionsLauncher.launch(permissions) }
|
||||
if (shouldShow.isNotEmpty()) {
|
||||
dialogTitle.value = getString(R.string.permissions_rationale)
|
||||
dialogMessage.value = getString(R.string.audio_permissions)
|
||||
positiveText.value = getString(R.string.ok)
|
||||
onPositiveClicked.value = {
|
||||
requestPermissionsLauncher.launch(permissions)
|
||||
}
|
||||
negativeText.value = ""
|
||||
showDialog.value = true
|
||||
}
|
||||
else
|
||||
startBaresip()
|
||||
}
|
||||
@ -486,6 +504,7 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
@Composable
|
||||
fun MainContent(ctx: Context, contentPadding: PaddingValues) {
|
||||
|
||||
var isRefreshing by remember { mutableStateOf(false) }
|
||||
LaunchedEffect(isRefreshing) {
|
||||
if (isRefreshing) {
|
||||
@ -493,6 +512,18 @@ class MainActivity : ComponentActivity() {
|
||||
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(
|
||||
isRefreshing = isRefreshing,
|
||||
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(
|
||||
modifier = Modifier
|
||||
.consumeWindowInsets(contentPadding)
|
||||
.padding(
|
||||
PaddingValues(
|
||||
top = 76.dp, bottom = 6.dp,
|
||||
start = 16.dp, end = 16.dp
|
||||
)
|
||||
)
|
||||
.imePadding()
|
||||
.fillMaxWidth()
|
||||
.padding(contentPadding)
|
||||
.padding(top = 18.dp, bottom = 6.dp, start = 16.dp, end = 16.dp)
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
AccountSpinner(ctx)
|
||||
CallUriRow(ctx)
|
||||
CallUriRow()
|
||||
CallRow(ctx)
|
||||
OnHoldNotice()
|
||||
AskPasswords(ctx)
|
||||
@ -668,10 +705,16 @@ class MainActivity : ComponentActivity() {
|
||||
passwordTitle = getString(R.string.encrypt_password)
|
||||
showPasswordDialog.value = true
|
||||
}
|
||||
shouldShowRequestPermissionRationale(WRITE_EXTERNAL_STORAGE) ->
|
||||
Utils.alertView(ctx, getString(R.string.notice), getString(R.string.no_backup)) {
|
||||
shouldShowRequestPermissionRationale(WRITE_EXTERNAL_STORAGE) -> {
|
||||
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)
|
||||
}
|
||||
negativeText.value = ""
|
||||
showDialog.value = true
|
||||
}
|
||||
else ->
|
||||
requestPermissionLauncher.launch(WRITE_EXTERNAL_STORAGE)
|
||||
}
|
||||
@ -688,10 +731,16 @@ class MainActivity : ComponentActivity() {
|
||||
passwordTitle = getString(R.string.decrypt_password)
|
||||
showPasswordDialog.value = true
|
||||
}
|
||||
shouldShowRequestPermissionRationale(READ_EXTERNAL_STORAGE) ->
|
||||
Utils.alertView(ctx, getString(R.string.notice), getString(R.string.no_restore)) {
|
||||
shouldShowRequestPermissionRationale(READ_EXTERNAL_STORAGE) -> {
|
||||
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)
|
||||
}
|
||||
negativeText.value = ""
|
||||
showDialog.value = true
|
||||
}
|
||||
else ->
|
||||
requestPermissionLauncher.launch(READ_EXTERNAL_STORAGE)
|
||||
}
|
||||
@ -885,7 +934,7 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CallUriRow(ctx: Context) {
|
||||
fun CallUriRow() {
|
||||
val suggestions by remember { contactNames }
|
||||
var filteredSuggestions by remember { mutableStateOf(suggestions) }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
@ -929,7 +978,7 @@ class MainActivity : ComponentActivity() {
|
||||
.padding(start = 4.dp, end = 4.dp, top = 12.dp, bottom = 2.dp)
|
||||
.focusRequester(focusRequester),
|
||||
label = {
|
||||
Text(
|
||||
LabelText(
|
||||
text = callUriLabel.value,
|
||||
fontSize = 18.sp,
|
||||
)
|
||||
@ -1018,47 +1067,34 @@ class MainActivity : ComponentActivity() {
|
||||
.clickable {
|
||||
when (securityIcon.intValue) {
|
||||
R.drawable.unlocked -> {
|
||||
Utils.alertView(
|
||||
ctx, getString(R.string.alert),
|
||||
getString(R.string.call_not_secure)
|
||||
)
|
||||
alertTitle.value = getString(R.string.alert)
|
||||
alertMessage.value = getString(R.string.call_not_secure)
|
||||
showAlert.value = true
|
||||
}
|
||||
|
||||
R.drawable.locked_yellow -> {
|
||||
Utils.alertView(
|
||||
ctx, getString(R.string.alert),
|
||||
getString(R.string.peer_not_verified)
|
||||
)
|
||||
alertTitle.value = getString(R.string.alert)
|
||||
alertMessage.value = getString(R.string.peer_not_verified)
|
||||
showAlert.value = true
|
||||
}
|
||||
|
||||
R.drawable.locked_green -> {
|
||||
with(
|
||||
MaterialAlertDialogBuilder(
|
||||
ctx,
|
||||
R.style.AlertDialogTheme
|
||||
)
|
||||
) {
|
||||
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 call = ua.currentCall()
|
||||
if (call != null) {
|
||||
if (Api.cmd_exec("zrtp_unverify " + call.zid) != 0)
|
||||
Log.e(
|
||||
TAG,
|
||||
"Command 'zrtp_unverify ${call.zid}' failed"
|
||||
)
|
||||
else
|
||||
securityIcon.intValue = R.drawable.locked_yellow
|
||||
}
|
||||
dialog.dismiss()
|
||||
dialogTitle.value = getString(R.string.info)
|
||||
dialogMessage.value = getString(R.string.call_is_secure)
|
||||
positiveText.value = getString(R.string.unverify)
|
||||
onPositiveClicked.value = {
|
||||
val ua = UserAgent.ofAor(viewModel.selectedAor.value)!!
|
||||
val call = ua.currentCall()
|
||||
if (call != null) {
|
||||
if (Api.cmd_exec("zrtp_unverify " + call.zid) != 0)
|
||||
Log.e(
|
||||
TAG,
|
||||
"Command 'zrtp_unverify ${call.zid}' failed"
|
||||
)
|
||||
else
|
||||
securityIcon.intValue = R.drawable.locked_yellow
|
||||
}
|
||||
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()
|
||||
if (call != null) {
|
||||
if (call.onHoldCall != null) {
|
||||
if (!call.executeTransfer())
|
||||
Utils.alertView(
|
||||
ctx, getString(R.string.notice),
|
||||
String.format(getString(R.string.transfer_failed))
|
||||
)
|
||||
if (!call.executeTransfer()) {
|
||||
alertTitle.value = getString(R.string.notice)
|
||||
alertMessage.value = getString(R.string.transfer_failed)
|
||||
showAlert.value = true
|
||||
}
|
||||
}
|
||||
else
|
||||
showTransferDialog = true
|
||||
@ -1254,13 +1290,7 @@ class MainActivity : ComponentActivity() {
|
||||
bottom = 2.dp
|
||||
)
|
||||
.focusRequester(focusRequester),
|
||||
label = {
|
||||
Text(
|
||||
text = stringResource(R.string.transfer_destination),
|
||||
fontSize = 18.sp,
|
||||
color = LocalCustomColors.current.dark
|
||||
)
|
||||
},
|
||||
label = { LabelText(stringResource(R.string.transfer_destination)) },
|
||||
textStyle = TextStyle(
|
||||
fontSize = 18.sp,
|
||||
color = LocalCustomColors.current.dark
|
||||
@ -1365,30 +1395,24 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
TextButton(
|
||||
onClick = {
|
||||
showSuggestions = false
|
||||
var uriText = transferUri.trim()
|
||||
if (uriText.isNotEmpty()) {
|
||||
val uris = Contact.contactUris(uriText)
|
||||
if (uris.size > 1) {
|
||||
val destinationBuilder =
|
||||
MaterialAlertDialogBuilder(
|
||||
ctx,
|
||||
R.style.AlertDialogTheme
|
||||
items.value = uris
|
||||
itemAction.value = { index ->
|
||||
val uriText = uris[index]
|
||||
transfer(
|
||||
ua,
|
||||
if (Utils.isTelNumber(uriText)) "tel:$uriText" else uriText,
|
||||
!blindChecked.value
|
||||
)
|
||||
with(destinationBuilder) {
|
||||
setTitle(R.string.choose_destination_uri)
|
||||
setItems(uris.toTypedArray()) { _, which ->
|
||||
uriText = uris[which]
|
||||
transfer(
|
||||
ua,
|
||||
if (Utils.isTelNumber(uriText))
|
||||
"tel:$uriText"
|
||||
else
|
||||
uriText,
|
||||
!blindChecked.value
|
||||
)
|
||||
}
|
||||
showSelectItemDialog.value = false
|
||||
}
|
||||
} else {
|
||||
showSelectItemDialog.value = true
|
||||
}
|
||||
else {
|
||||
if (uris.size == 1)
|
||||
uriText = uris[0]
|
||||
}
|
||||
@ -1441,7 +1465,7 @@ class MainActivity : ComponentActivity() {
|
||||
.focusRequester(focusRequester),
|
||||
enabled = dtmfEnabled.value,
|
||||
textStyle = TextStyle(fontSize = 16.sp),
|
||||
label = { Text(getString(R.string.dtmf), fontSize = 16.sp) },
|
||||
label = { LabelText(stringResource(R.string.dtmf)) },
|
||||
singleLine = true
|
||||
)
|
||||
LaunchedEffect(shouldRequestFocus) {
|
||||
@ -1468,8 +1492,8 @@ class MainActivity : ComponentActivity() {
|
||||
val duration = call.duration()
|
||||
val txCodec = codecs.split(',')[0].split("/")
|
||||
val rxCodec = codecs.split(',')[1].split("/")
|
||||
Utils.alertView(
|
||||
ctx, getString(R.string.call_info),
|
||||
alertTitle.value = getString(R.string.call_info)
|
||||
alertMessage.value =
|
||||
"${String.format(getString(R.string.duration), duration)}\n" +
|
||||
"${getString(R.string.codecs)}: ${txCodec[0]} ch ${txCodec[2]}/" +
|
||||
"${rxCodec[0]} ch ${rxCodec[2]}\n" +
|
||||
@ -1483,12 +1507,11 @@ class MainActivity : ComponentActivity() {
|
||||
"${getString(R.string.packets)}: ${parts[2]}\n" +
|
||||
"${getString(R.string.lost)}: ${parts[3]}\n" +
|
||||
String.format(getString(R.string.jitter), parts[4])
|
||||
)
|
||||
showAlert.value = true
|
||||
} else {
|
||||
Utils.alertView(
|
||||
ctx, getString(R.string.call_info),
|
||||
getString(R.string.call_info_not_available)
|
||||
)
|
||||
alertTitle.value = getString(R.string.call_info)
|
||||
alertMessage.value = getString(R.string.call_info_not_available)
|
||||
showAlert.value = true
|
||||
}
|
||||
},
|
||||
) {
|
||||
@ -1539,91 +1562,91 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
private fun callClick(ctx: Context) {
|
||||
if (viewModel.selectedAor.value != "") {
|
||||
if (Utils.checkPermissions(ctx, arrayOf(RECORD_AUDIO)))
|
||||
makeCall()
|
||||
if (Utils.checkPermissions(ctx, arrayOf(RECORD_AUDIO))) {
|
||||
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
|
||||
Toast.makeText(applicationContext, R.string.no_calls, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeCall(lookForContact: Boolean = true) {
|
||||
val ua = UserAgent.ofAor(viewModel.selectedAor.value)
|
||||
val aor = ua!!.account.aor
|
||||
if (!Call.inCall()) {
|
||||
var uriText = callUri.value.trim()
|
||||
if (uriText.isNotEmpty()) {
|
||||
if (lookForContact) {
|
||||
val uris = Contact.contactUris(uriText)
|
||||
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)
|
||||
private fun makeCall(uriText: String) {
|
||||
val ua = UserAgent.ofAor(viewModel.selectedAor.value)!!
|
||||
val aor = ua.account.aor
|
||||
val peerUri = if (Utils.isTelNumber(uriText))
|
||||
"tel:$uriText"
|
||||
else
|
||||
uriText
|
||||
val uri = if (Utils.isTelUri(peerUri)) {
|
||||
if (ua.account.telProvider == "") {
|
||||
alertTitle.value = getString(R.string.notice)
|
||||
alertMessage.value = String.format(getString(R.string.no_telephony_provider), aor)
|
||||
showAlert.value = true
|
||||
return
|
||||
}
|
||||
Utils.telToSip(uriText, ua.account)
|
||||
} else {
|
||||
Utils.uriComplete(uriText, aor)
|
||||
}
|
||||
if (!Utils.checkUri(uri)) {
|
||||
alertTitle.value = getString(R.string.notice)
|
||||
alertMessage.value = String.format(getString(R.string.invalid_sip_or_tel_uri), uri)
|
||||
showAlert.value = true
|
||||
} else if (!BaresipService.requestAudioFocus(applicationContext)) {
|
||||
Toast.makeText(applicationContext, R.string.audio_focus_denied,
|
||||
Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
callUriEnabled.value = false
|
||||
showCallButton.value = false
|
||||
showHangupButton.value = true
|
||||
if (Build.VERSION.SDK_INT < 31) {
|
||||
Log.d(TAG, "Setting audio mode to MODE_IN_COMMUNICATION")
|
||||
am.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
runCall(ua, uri)
|
||||
} else {
|
||||
if (am.mode == AudioManager.MODE_IN_COMMUNICATION) {
|
||||
runCall(ua, uri)
|
||||
} else {
|
||||
audioModeChangedListener = AudioManager.OnModeChangedListener { mode ->
|
||||
if (mode == AudioManager.MODE_IN_COMMUNICATION) {
|
||||
Log.d(TAG, "Audio mode changed to MODE_IN_COMMUNICATION using " +
|
||||
"device ${am.communicationDevice!!.type}")
|
||||
if (audioModeChangedListener != null) {
|
||||
am.removeOnModeChangedListener(audioModeChangedListener!!)
|
||||
audioModeChangedListener = null
|
||||
}
|
||||
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 == "") {
|
||||
Utils.alertView(this, getString(R.string.notice),
|
||||
String.format(getString(R.string.no_telephony_provider), aor))
|
||||
return
|
||||
}
|
||||
Utils.telToSip(uriText, ua.account)
|
||||
} else {
|
||||
Utils.uriComplete(uriText, aor)
|
||||
}
|
||||
if (!Utils.checkUri(uri)) {
|
||||
Utils.alertView(this, getString(R.string.notice),
|
||||
String.format(getString(R.string.invalid_sip_or_tel_uri), uri))
|
||||
} else if (!BaresipService.requestAudioFocus(applicationContext)) {
|
||||
Toast.makeText(applicationContext, R.string.audio_focus_denied,
|
||||
Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
callUriEnabled.value = false
|
||||
showCallButton.value = false
|
||||
showHangupButton.value = true
|
||||
if (Build.VERSION.SDK_INT < 31) {
|
||||
Log.d(TAG, "Setting audio mode to MODE_IN_COMMUNICATION")
|
||||
am.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
runCall(ua, uri)
|
||||
} else {
|
||||
if (am.mode == AudioManager.MODE_IN_COMMUNICATION) {
|
||||
runCall(ua, uri)
|
||||
} else {
|
||||
audioModeChangedListener = AudioManager.OnModeChangedListener { mode ->
|
||||
if (mode == AudioManager.MODE_IN_COMMUNICATION) {
|
||||
Log.d(TAG, "Audio mode changed to MODE_IN_COMMUNICATION using " +
|
||||
"device ${am.communicationDevice!!.type}")
|
||||
if (audioModeChangedListener != null) {
|
||||
am.removeOnModeChangedListener(audioModeChangedListener!!)
|
||||
audioModeChangedListener = null
|
||||
}
|
||||
runCall(ua, uri)
|
||||
} else {
|
||||
Log.d(TAG, "Audio mode changed to mode ${am.mode} using " +
|
||||
"device ${am.communicationDevice!!.type}")
|
||||
}
|
||||
}
|
||||
am.addOnModeChangedListener(mainExecutor, audioModeChangedListener!!)
|
||||
Log.d(TAG, "Setting audio mode to MODE_IN_COMMUNICATION")
|
||||
am.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
Log.d(TAG, "Audio mode changed to mode ${am.mode} using " +
|
||||
"device ${am.communicationDevice!!.type}")
|
||||
}
|
||||
}
|
||||
am.addOnModeChangedListener(mainExecutor, audioModeChangedListener!!)
|
||||
Log.d(TAG, "Setting audio mode to MODE_IN_COMMUNICATION")
|
||||
am.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
}
|
||||
} else {
|
||||
val latestPeerUri = CallHistoryNew.aorLatestPeerUri(aor)
|
||||
if (latestPeerUri != null)
|
||||
callUri.value = Utils.friendlyUri(this, latestPeerUri, ua.account)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1668,28 +1691,21 @@ class MainActivity : ComponentActivity() {
|
||||
val ua = UserAgent.ofAor(viewModel.selectedAor.value)!!
|
||||
val acc = ua.account
|
||||
if (acc.vmUri != "") {
|
||||
val dialogClickListener = DialogInterface.OnClickListener { _, which ->
|
||||
when (which) {
|
||||
DialogInterface.BUTTON_POSITIVE -> {
|
||||
val i = Intent(ctx, MainActivity::class.java)
|
||||
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK or
|
||||
Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
i.putExtra("action", "call")
|
||||
i.putExtra("uap", ua.uap)
|
||||
i.putExtra("peer", acc.vmUri)
|
||||
startActivity(i)
|
||||
}
|
||||
DialogInterface.BUTTON_NEGATIVE -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
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()
|
||||
dialogTitle.value = getString(R.string.voicemail_messages)
|
||||
dialogMessage.value = acc.vmMessages(ctx)
|
||||
positiveText.value = getString(R.string.listen)
|
||||
onPositiveClicked.value = {
|
||||
val i = Intent(ctx, MainActivity::class.java)
|
||||
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK or
|
||||
Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
i.putExtra("action", "call")
|
||||
i.putExtra("uap", ua.uap)
|
||||
i.putExtra("peer", acc.vmUri)
|
||||
startActivity(i)
|
||||
}
|
||||
negativeText.value = getString(R.string.cancel)
|
||||
onNegativeClicked.value = {}
|
||||
showDialog.value = true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -2074,8 +2090,9 @@ class MainActivity : ComponentActivity() {
|
||||
val ev = action.split(",")
|
||||
when (ev[0]) {
|
||||
"no network" -> {
|
||||
Utils.alertView(this, getString(R.string.notice),
|
||||
getString(R.string.no_network))
|
||||
alertTitle.value = getString(R.string.notice)
|
||||
alertMessage.value = getString(R.string.no_network)
|
||||
showAlert.value = true
|
||||
return
|
||||
}
|
||||
"call", "dial" -> {
|
||||
@ -2205,8 +2222,11 @@ class MainActivity : ComponentActivity() {
|
||||
if (event == "stopped") {
|
||||
Log.d(TAG, "Handling service event 'stopped' with start error '${params[0]}'")
|
||||
if (params[0] != "") {
|
||||
Utils.alertView(this, getString(R.string.notice), getString(R.string.start_failed))
|
||||
} else {
|
||||
alertTitle.value = getString(R.string.notice)
|
||||
alertMessage.value = getString(R.string.start_failed)
|
||||
showAlert.value = true
|
||||
}
|
||||
else {
|
||||
finishAndRemoveTask()
|
||||
if (restart)
|
||||
reStart()
|
||||
@ -2262,19 +2282,17 @@ class MainActivity : ComponentActivity() {
|
||||
String.format(getString(R.string.redirect_notice), target),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
|
||||
setTitle(R.string.redirect_request)
|
||||
setMessage(String.format(getString(R.string.redirect_request_query), target))
|
||||
setPositiveButton(getString(R.string.yes)) { dialog, _ ->
|
||||
redirect(ua, target)
|
||||
dialog.dismiss()
|
||||
}
|
||||
setNeutralButton(getString(R.string.no)) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
show()
|
||||
}
|
||||
else {
|
||||
dialogTitle.value = getString(R.string.redirect_request)
|
||||
dialogMessage.value = String.format(getString(R.string.redirect_request_query), target)
|
||||
positiveText.value = getString(R.string.yes)
|
||||
onPositiveClicked.value = {
|
||||
redirect(ua, target)
|
||||
}
|
||||
negativeText.value = getString(R.string.no)
|
||||
onNegativeClicked.value = {}
|
||||
showDialog.value = true
|
||||
}
|
||||
showCall(ua)
|
||||
}
|
||||
@ -2294,29 +2312,27 @@ class MainActivity : ComponentActivity() {
|
||||
handleNextEvent("Call $callp to be verified is not found")
|
||||
return
|
||||
}
|
||||
with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
|
||||
setTitle(R.string.verify)
|
||||
setMessage(String.format(getString(R.string.verify_sas), ev[1]))
|
||||
setPositiveButton(getString(R.string.yes)) { dialog, _ ->
|
||||
call.security = if (Api.cmd_exec("zrtp_verify ${ev[2]}") != 0) {
|
||||
Log.e(TAG, "Command 'zrtp_verify ${ev[2]}' failed")
|
||||
R.drawable.locked_yellow
|
||||
} else {
|
||||
R.drawable.locked_green
|
||||
}
|
||||
call.zid = ev[2]
|
||||
if (aor == viewModel.selectedAor.value)
|
||||
securityIcon.intValue = call.security
|
||||
dialog.dismiss()
|
||||
dialogTitle.value = getString(R.string.verify)
|
||||
dialogMessage.value = String.format(getString(R.string.verify_sas), call.zid)
|
||||
positiveText.value = getString(R.string.yes)
|
||||
onPositiveClicked.value = {
|
||||
call.security = if (Api.cmd_exec("zrtp_verify ${ev[2]}") != 0) {
|
||||
Log.e(TAG, "Command 'zrtp_verify ${ev[2]}' failed")
|
||||
R.drawable.locked_yellow
|
||||
} else {
|
||||
R.drawable.locked_green
|
||||
}
|
||||
setNeutralButton(getString(R.string.no)) { dialog, _ ->
|
||||
call.security = R.drawable.locked_yellow
|
||||
call.zid = ev[2]
|
||||
if (aor == viewModel.selectedAor.value)
|
||||
securityIcon.intValue = R.drawable.locked_yellow
|
||||
dialog.dismiss()
|
||||
}
|
||||
show()
|
||||
call.zid = ev[2]
|
||||
if (aor == viewModel.selectedAor.value)
|
||||
securityIcon.intValue = call.security
|
||||
}
|
||||
negativeText.value = getString(R.string.no)
|
||||
onNegativeClicked.value = {
|
||||
call.security = R.drawable.locked_yellow
|
||||
call.zid = ev[2]
|
||||
if (aor == viewModel.selectedAor.value)
|
||||
securityIcon.intValue = R.drawable.locked_yellow
|
||||
onNegativeClicked.value = {}
|
||||
}
|
||||
}
|
||||
"call verified", "call secure" -> {
|
||||
@ -2344,40 +2360,28 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
val call = Call.ofCallp(callp)
|
||||
val target = Utils.friendlyUri(this, ev[1], acc)
|
||||
with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
|
||||
if (call != null) {
|
||||
setTitle(R.string.transfer_request)
|
||||
setMessage(
|
||||
String.format(
|
||||
getString(R.string.transfer_request_query),
|
||||
target
|
||||
)
|
||||
)
|
||||
} else {
|
||||
setTitle(R.string.call_request)
|
||||
setMessage(
|
||||
String.format(
|
||||
getString(R.string.call_request_query),
|
||||
target
|
||||
)
|
||||
)
|
||||
dialogTitle.value = if (call != null)
|
||||
getString(R.string.transfer_request)
|
||||
else
|
||||
getString(R.string.call_request)
|
||||
dialogMessage.value = if (call != null)
|
||||
String.format(getString(R.string.transfer_request_query), target)
|
||||
else
|
||||
String.format(getString(R.string.call_request_query), target)
|
||||
positiveText.value = getString(R.string.yes)
|
||||
onPositiveClicked.value = {
|
||||
if (call in Call.calls())
|
||||
acceptTransfer(ua, call!!, ev[1])
|
||||
else
|
||||
makeCall(ev[1])
|
||||
}
|
||||
setPositiveButton(getString(R.string.yes)) { dialog, _ ->
|
||||
if (call in Call.calls())
|
||||
acceptTransfer(ua, call!!, ev[1])
|
||||
else {
|
||||
callUri.value = ev[1]
|
||||
makeCall()
|
||||
}
|
||||
dialog.dismiss()
|
||||
}
|
||||
setNeutralButton(getString(R.string.no)) { dialog, _ ->
|
||||
if (call in Call.calls())
|
||||
call!!.notifySipfrag(603, "Decline")
|
||||
dialog.dismiss()
|
||||
}
|
||||
show()
|
||||
negativeText.value = getString(R.string.no)
|
||||
onNegativeClicked.value = {
|
||||
if (call in Call.calls())
|
||||
call!!.notifySipfrag(603, "Decline")
|
||||
onNegativeClicked.value = {}
|
||||
}
|
||||
showDialog.value = true
|
||||
}
|
||||
"transfer accept" -> {
|
||||
val callp = params[1] as Long
|
||||
@ -2509,9 +2513,11 @@ class MainActivity : ComponentActivity() {
|
||||
else
|
||||
Utils.uriComplete(uriText, ua.account.aor)
|
||||
if (!Utils.checkUri(uri)) {
|
||||
Utils.alertView(this@MainActivity, getString(R.string.notice),
|
||||
String.format(getString(R.string.invalid_sip_or_tel_uri), uri))
|
||||
} else {
|
||||
alertTitle.value = getString(R.string.notice)
|
||||
alertMessage.value = String.format(getString(R.string.invalid_sip_or_tel_uri), uri)
|
||||
showAlert.value = true
|
||||
}
|
||||
else {
|
||||
val call = ua.currentCall()
|
||||
if (call != null) {
|
||||
if (attended) {
|
||||
@ -2519,10 +2525,12 @@ class MainActivity : ComponentActivity() {
|
||||
call.referTo = uri
|
||||
call(ua, uri, call)
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (!call.transfer(uri)) {
|
||||
Utils.alertView(this@MainActivity, getString(R.string.notice),
|
||||
String.format(getString(R.string.transfer_failed)))
|
||||
alertTitle.value = getString(R.string.notice)
|
||||
alertMessage.value = getString(R.string.transfer_failed)
|
||||
showAlert.value = true
|
||||
}
|
||||
}
|
||||
showCall(ua)
|
||||
@ -2550,31 +2558,35 @@ class MainActivity : ComponentActivity() {
|
||||
val zipFilePath = BaresipService.filesPath + "/$zipFile"
|
||||
if (!Utils.zip(files, zipFile)) {
|
||||
Log.w(TAG, "Failed to write zip file '$zipFile'")
|
||||
Utils.alertView(this, getString(R.string.error),
|
||||
String.format(getString(R.string.backup_failed),
|
||||
Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!)))
|
||||
alertTitle.value = getString(R.string.error)
|
||||
alertMessage.value = String.format(getString(R.string.backup_failed),
|
||||
Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))
|
||||
showAlert.value = true
|
||||
downloadsOutputUri = null
|
||||
return
|
||||
}
|
||||
val content = Utils.getFileContents(zipFilePath)
|
||||
if (content == null) {
|
||||
Log.w(TAG, "Failed to read zip file '$zipFile'")
|
||||
Utils.alertView(this, getString(R.string.error),
|
||||
String.format(getString(R.string.backup_failed),
|
||||
Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!)))
|
||||
alertTitle.value = getString(R.string.error)
|
||||
alertMessage.value = String.format(getString(R.string.backup_failed),
|
||||
Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))
|
||||
showAlert.value = true
|
||||
downloadsOutputUri = null
|
||||
return
|
||||
}
|
||||
if (!Utils.encryptToUri(applicationContext, downloadsOutputUri!!, content, password)) {
|
||||
Utils.alertView(this, getString(R.string.error),
|
||||
String.format(getString(R.string.backup_failed),
|
||||
Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!)))
|
||||
alertTitle.value = getString(R.string.error)
|
||||
alertMessage.value = String.format(getString(R.string.backup_failed),
|
||||
Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))
|
||||
showAlert.value = true
|
||||
downloadsOutputUri = null
|
||||
return
|
||||
}
|
||||
Utils.alertView(this, getString(R.string.info),
|
||||
String.format(getString(R.string.backed_up),
|
||||
Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!)))
|
||||
alertTitle.value = getString(R.string.info)
|
||||
alertMessage.value = String.format(getString(R.string.backed_up),
|
||||
Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))
|
||||
showAlert.value = true
|
||||
Utils.deleteFile(File(zipFilePath))
|
||||
downloadsOutputUri = null
|
||||
}
|
||||
@ -2584,34 +2596,27 @@ class MainActivity : ComponentActivity() {
|
||||
val zipFilePath = BaresipService.filesPath + "/$zipFile"
|
||||
val zipData = Utils.decryptFromUri(applicationContext, downloadsInputUri!!, password)
|
||||
if (zipData == null) {
|
||||
Utils.alertView(
|
||||
this, getString(R.string.error),
|
||||
String.format(
|
||||
getString(R.string.restore_failed),
|
||||
Utils.fileNameOfUri(applicationContext, downloadsInputUri!!)
|
||||
)
|
||||
)
|
||||
alertTitle.value = getString(R.string.error)
|
||||
alertMessage.value = String.format(getString(R.string.restore_failed),
|
||||
Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))
|
||||
showAlert.value = true
|
||||
downloadsOutputUri = null
|
||||
return
|
||||
}
|
||||
if (!Utils.putFileContents(zipFilePath, zipData)) {
|
||||
Log.w(TAG, "Failed to write zip file '$zipFile'")
|
||||
Utils.alertView(
|
||||
this, getString(R.string.error),
|
||||
String.format(
|
||||
getString(R.string.restore_failed),
|
||||
Utils.fileNameOfUri(applicationContext, downloadsInputUri!!)
|
||||
)
|
||||
)
|
||||
alertTitle.value = getString(R.string.error)
|
||||
alertMessage.value = String.format(getString(R.string.restore_failed),
|
||||
Utils.fileNameOfUri(applicationContext, downloadsOutputUri!!))
|
||||
showAlert.value = true
|
||||
downloadsOutputUri = null
|
||||
return
|
||||
}
|
||||
if (!Utils.unZip(zipFilePath)) {
|
||||
Log.w(TAG, "Failed to unzip file '$zipFile'")
|
||||
Utils.alertView(
|
||||
this, getString(R.string.error),
|
||||
String.format(getString(R.string.restore_unzip_failed), "baresip", "60.0.0")
|
||||
)
|
||||
alertTitle.value = getString(R.string.error)
|
||||
alertMessage.value = String.format(getString(R.string.restore_unzip_failed), "baresip", BuildConfig.VERSION_NAME)
|
||||
showAlert.value = true
|
||||
downloadsOutputUri = null
|
||||
return
|
||||
}
|
||||
@ -2627,18 +2632,16 @@ class MainActivity : ComponentActivity() {
|
||||
h.recording = arrayOf("", "")
|
||||
CallHistoryNew.save()
|
||||
|
||||
with(MaterialAlertDialogBuilder(this, R.style.AlertDialogTheme)) {
|
||||
setTitle(getString(R.string.info))
|
||||
setMessage(getString(R.string.restored))
|
||||
setPositiveButton(getText(R.string.restart)) { dialog, _ ->
|
||||
quitRestart(true)
|
||||
dialog.dismiss()
|
||||
}
|
||||
setNeutralButton(getText(R.string.cancel)) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
}
|
||||
show()
|
||||
dialogTitle.value = getString(R.string.info)
|
||||
dialogMessage.value = getString(R.string.restored)
|
||||
positiveText.value = getString(R.string.restart)
|
||||
onPositiveClicked.value = {
|
||||
quitRestart(true)
|
||||
}
|
||||
negativeText.value = getString(R.string.cancel)
|
||||
onNegativeClicked.value = {}
|
||||
showDialog.value = true
|
||||
|
||||
downloadsOutputUri = null
|
||||
}
|
||||
|
||||
|
||||
@ -8,8 +8,6 @@ import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Canvas
|
||||
import android.media.AudioAttributes
|
||||
import android.media.AudioDeviceInfo
|
||||
import android.media.AudioManager
|
||||
@ -25,21 +23,14 @@ import android.provider.DocumentsContract
|
||||
import android.provider.MediaStore
|
||||
import android.provider.OpenableColumns
|
||||
import android.text.format.DateUtils
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.createBitmap
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.text.isDigitsOnly
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
@ -92,18 +83,6 @@ object Utils {
|
||||
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 {
|
||||
return if (uri.contains("@")) {
|
||||
uri.substringAfter("@")
|
||||
@ -407,46 +386,6 @@ object Utils {
|
||||
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")
|
||||
fun checkIfName(name: String): Boolean {
|
||||
if ((name.length < 2) || !name.first().isLetter()) return false
|
||||
@ -883,13 +822,6 @@ object Utils {
|
||||
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 {
|
||||
return if (DateUtils.isToday(time.timeInMillis)) {
|
||||
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 {
|
||||
return if (Build.VERSION.SDK_INT >= 31)
|
||||
am.communicationDevice!!.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
|
||||
|
||||
@ -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>
|
||||
Reference in New Issue
Block a user