Added and used PasswordDialog composable

This commit is contained in:
Juha Heinanen
2025-04-04 19:21:40 +03:00
parent 3bff286888
commit 63e4506df9
3 changed files with 198 additions and 265 deletions

View File

@ -3,7 +3,6 @@ package com.tutpro.baresip
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback
import androidx.activity.compose.setContent
@ -14,35 +13,27 @@ import androidx.compose.foundation.layout.Box
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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
@ -53,8 +44,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
@ -130,6 +119,7 @@ class AccountActivity : ComponentActivity() {
private var oldDefaultAccount = false
private var newDefaultAccount = false
private var arrowTint = Color.Unspecified
private var password = mutableStateOf("")
private var showPasswordDialog = mutableStateOf(false)
private var keyboardController: SoftwareKeyboardController? = null
@ -1166,137 +1156,25 @@ class AccountActivity : ComponentActivity() {
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AskPassword(ctx: Context) {
if (showPasswordDialog.value) {
val showPassword = remember { mutableStateOf(false) }
BasicAlertDialog(
onDismissRequest = {
keyboardController?.hide()
showPasswordDialog.value = false
if (showPasswordDialog.value)
CustomElements.PasswordDialog(
ctx = ctx,
showPasswordDialog = showPasswordDialog,
password = password,
keyboardController = keyboardController,
title = getString(R.string.authentication_password),
okAction = {
BaresipService.aorPasswords[acc.aor] = password.value
Api.account_set_auth_pass(acc.accp, password.value)
password.value = ""
reRegister = true
finishActivity()
},
cancelAction = {
reRegister = true
finishActivity()
}
) {
Surface(
modifier = Modifier
.wrapContentWidth()
.wrapContentHeight(),
color = LocalCustomColors.current.background,
shape = MaterialTheme.shapes.large,
tonalElevation = AlertDialogDefaults.TonalElevation
) {
Column(modifier = Modifier.padding(16.dp)) {
CustomElements.Text(
text = getString(R.string.authentication_password),
fontSize = 20.sp,
modifier = Modifier.padding(top = 16.dp, bottom = 16.dp),
color = LocalCustomColors.current.alert,
)
val message = getString(R.string.account) + " " + Utils.plainAor(acc.aor)
CustomElements.Text(
text = message,
fontSize = 16.sp,
modifier = Modifier.padding(16.dp),
color = LocalCustomColors.current.itemText,
)
var password by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
OutlinedTextField(
value = password,
singleLine = true,
colors = OutlinedTextFieldDefaults.colors(
focusedContainerColor = LocalCustomColors.current.textFieldBackground,
unfocusedContainerColor = LocalCustomColors.current.textFieldBackground,
cursorColor = LocalCustomColors.current.primary,
),
onValueChange = {
password = it
},
visualTransformation = if (showPassword.value)
VisualTransformation.None
else
PasswordVisualTransformation(),
trailingIcon = {
IconButton(onClick = {
showPassword.value = !showPassword.value
}) {
Icon(
if (showPassword.value)
ImageVector.vectorResource(R.drawable.visibility)
else
ImageVector.vectorResource(R.drawable.visibility_off),
contentDescription = "Visibility",
tint = LocalCustomColors.current.grayDark
)
}
},
modifier = Modifier
.fillMaxWidth()
.padding(
start = 4.dp,
end = 4.dp,
top = 12.dp,
bottom = 2.dp
)
.focusRequester(focusRequester),
textStyle = TextStyle(
fontSize = 18.sp,
color = LocalCustomColors.current.dark
),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
)
Row(
horizontalArrangement = Arrangement.Start
) {
TextButton(
onClick = {
keyboardController?.hide()
showPasswordDialog.value = false
reRegister = true
finishActivity()
},
modifier = Modifier.padding(8.dp),
) {
CustomElements.Text(
text = stringResource(R.string.cancel),
color = LocalCustomColors.current.gray
)
}
Spacer(modifier = Modifier.weight(1f))
TextButton(
onClick = {
keyboardController?.hide()
showPasswordDialog.value = false
password = password.trim()
if (!Account.checkAuthPass(password)) {
Toast.makeText(
ctx,
String.format(
getString(R.string.invalid_authentication_password),
password
),
Toast.LENGTH_SHORT
).show()
password = ""
showPasswordDialog.value = true
}
else {
BaresipService.aorPasswords[acc.aor] = password
Api.account_set_auth_pass(acc.accp, password)
reRegister = true
finishActivity()
}
},
modifier = Modifier.padding(8.dp),
) {
CustomElements.Text(
text = stringResource(R.string.ok),
color = LocalCustomColors.current.alert
)
}
}
}
}
}
}
)
}
private fun updateAccount() {

View File

@ -1,6 +1,8 @@
package com.tutpro.baresip
import android.content.Context
import android.graphics.Bitmap
import android.widget.Toast
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
@ -11,13 +13,19 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.BasicAlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.Checkbox
@ -26,11 +34,19 @@ import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Surface
import androidx.compose.material3.TextButton
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults.Indicator
import androidx.compose.material3.pulltorefresh.pullToRefresh
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -39,15 +55,25 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
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
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
@ -55,6 +81,8 @@ import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.core.content.ContextCompat.getString
import com.tutpro.baresip.CustomElements.ThreeButtonAlertDialog
object CustomElements {
@ -232,6 +260,129 @@ object CustomElements {
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PasswordDialog(
ctx: Context,
showPasswordDialog: MutableState<Boolean>,
password: MutableState<String>,
keyboardController:SoftwareKeyboardController?,
title: String,
okAction: () -> Unit,
cancelAction: () -> Unit
) {
val showPassword = remember { mutableStateOf(false) }
val focusRequester = remember { FocusRequester() }
BasicAlertDialog(
properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false),
onDismissRequest = {
keyboardController?.hide()
showPasswordDialog.value = false
}
) {
Surface(
modifier = Modifier
.wrapContentWidth()
.wrapContentHeight(),
color = LocalCustomColors.current.background,
shape = MaterialTheme.shapes.large,
tonalElevation = AlertDialogDefaults.TonalElevation
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = title,
fontSize = 20.sp,
modifier = Modifier.padding(top = 16.dp, bottom = 16.dp),
color = LocalCustomColors.current.alert,
)
OutlinedTextField(
value = password.value,
singleLine = true,
colors = OutlinedTextFieldDefaults.colors(
focusedContainerColor = LocalCustomColors.current.textFieldBackground,
unfocusedContainerColor = LocalCustomColors.current.textFieldBackground,
cursorColor = LocalCustomColors.current.primary,
),
onValueChange = {
password.value = it
},
visualTransformation = if (showPassword.value)
VisualTransformation.None
else
PasswordVisualTransformation(),
trailingIcon = {
IconButton(onClick = {
showPassword.value = !showPassword.value
showPasswordDialog.value = true
}) {
Icon(
if (showPassword.value)
ImageVector.vectorResource(R.drawable.visibility)
else
ImageVector.vectorResource(R.drawable.visibility_off),
contentDescription = "Visibility",
tint = LocalCustomColors.current.grayDark
)
}
},
modifier = Modifier
.fillMaxWidth()
.padding(start = 4.dp, end = 4.dp, top = 12.dp, bottom = 2.dp)
.focusRequester(focusRequester),
textStyle = TextStyle(
fontSize = 18.sp,
color = LocalCustomColors.current.dark
),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
)
LaunchedEffect(key1 = Unit) {
focusRequester.requestFocus()
keyboardController?.show()
}
Row(
horizontalArrangement = Arrangement.Start
) {
TextButton(
onClick = {
keyboardController?.hide()
showPasswordDialog.value = false
cancelAction()
},
) {
Text(
text = stringResource(R.string.cancel),
color = LocalCustomColors.current.gray
)
}
Spacer(modifier = Modifier.weight(1f))
TextButton(
onClick = {
keyboardController?.hide()
showPasswordDialog.value = false
password.value = password.value.trim()
if (!Account.checkAuthPass(password.value)) {
Toast.makeText(ctx,
String.format(getString(ctx, R.string.invalid_authentication_password), password.value),
Toast.LENGTH_SHORT
).show()
password.value = ""
}
if (password.value != "")
okAction()
},
) {
Text(
text = stringResource(R.string.ok),
color = LocalCustomColors.current.alert
)
}
}
}
}
}
}
@Composable
fun ThreeButtonAlertDialog(
onDismissRequest: () -> Unit,

View File

@ -130,6 +130,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.DialogProperties
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.lifecycle.Observer
@ -202,6 +203,7 @@ class MainActivity : ComponentActivity() {
private var showHangupButton = mutableStateOf(false)
private var showOnHoldNotice = mutableStateOf(false)
private var showPasswordDialog = mutableStateOf(false)
private var password = mutableStateOf("")
private var showPasswordsDialog = mutableStateOf(false)
private var holdIcon = mutableIntStateOf(R.drawable.call_hold)
private var transferButtonEnabled = mutableStateOf(false)
@ -1814,7 +1816,9 @@ class MainActivity : ComponentActivity() {
if (Utils.paramValue(params, "auth_user") != "" && Utils.paramValue(params, "auth_pass") == "") {
val aor = account.substringAfter("<").substringBefore(">")
val showPassword = remember { mutableStateOf(false) }
val focusRequester = remember { FocusRequester() }
BasicAlertDialog(
properties = DialogProperties(dismissOnBackPress = false, dismissOnClickOutside = false),
onDismissRequest = {
keyboardController?.hide()
showPasswordsDialog.value = false
@ -1844,7 +1848,6 @@ class MainActivity : ComponentActivity() {
color = LocalCustomColors.current.itemText,
)
var password by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
OutlinedTextField(
value = password,
singleLine = true,
@ -1877,19 +1880,18 @@ class MainActivity : ComponentActivity() {
},
modifier = Modifier
.fillMaxWidth()
.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),
textStyle = TextStyle(
textStyle = TextStyle(
fontSize = 18.sp,
color = LocalCustomColors.current.dark
),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
)
LaunchedEffect(key1 = Unit) {
focusRequester.requestFocus()
keyboardController?.show()
}
Row(
horizontalArrangement = Arrangement.Start
) {
@ -1951,124 +1953,26 @@ class MainActivity : ComponentActivity() {
@Composable
fun AskPassword(ctx: Context) {
if (showPasswordDialog.value) {
val showPassword = remember { mutableStateOf(false) }
BasicAlertDialog(
onDismissRequest = {
keyboardController?.hide()
showPasswordDialog.value = false
}
) {
Surface(
modifier = Modifier
.wrapContentWidth()
.wrapContentHeight(),
color = LocalCustomColors.current.background,
shape = MaterialTheme.shapes.large,
tonalElevation = AlertDialogDefaults.TonalElevation
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = passwordTitle,
fontSize = 20.sp,
modifier = Modifier.padding(top = 16.dp, bottom = 16.dp),
color = LocalCustomColors.current.alert,
)
var password by remember { mutableStateOf("") }
val focusRequester = remember { FocusRequester() }
OutlinedTextField(
value = password,
singleLine = true,
colors = OutlinedTextFieldDefaults.colors(
focusedContainerColor = LocalCustomColors.current.textFieldBackground,
unfocusedContainerColor = LocalCustomColors.current.textFieldBackground,
cursorColor = LocalCustomColors.current.primary,
),
onValueChange = {
password = it
},
visualTransformation = if (showPassword.value)
VisualTransformation.None
else
PasswordVisualTransformation(),
trailingIcon = {
IconButton(onClick = {
showPassword.value = !showPassword.value
}) {
Icon(
if (showPassword.value)
ImageVector.vectorResource(R.drawable.visibility)
else
ImageVector.vectorResource(R.drawable.visibility_off),
contentDescription = "Visibility",
tint = LocalCustomColors.current.grayDark
)
}
},
modifier = Modifier
.fillMaxWidth()
.padding(
start = 4.dp,
end = 4.dp,
top = 12.dp,
bottom = 2.dp
)
.focusRequester(focusRequester),
textStyle = TextStyle(
fontSize = 18.sp,
color = LocalCustomColors.current.dark
),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text)
)
Row(
horizontalArrangement = Arrangement.Start
) {
TextButton(
onClick = {
keyboardController?.hide()
showPasswordDialog.value = false
if (downloadsOutputUri != null) {
Utils.deleteFile(ctx, downloadsOutputUri!!)
}
},
) {
Text(
text = stringResource(R.string.cancel),
color = LocalCustomColors.current.gray
)
}
Spacer(modifier = Modifier.weight(1f))
TextButton(
onClick = {
keyboardController?.hide()
showPasswordDialog.value = false
password = password.trim()
if (!Account.checkAuthPass(password)) {
Toast.makeText(ctx,
String.format(getString(R.string.invalid_authentication_password), password),
Toast.LENGTH_SHORT
).show()
password = ""
}
if (password != "") {
if (passwordTitle == getString(R.string.encrypt_password))
backup(password)
else
restore(password)
}
},
) {
Text(
text = stringResource(R.string.ok),
color = LocalCustomColors.current.alert
)
}
}
if (showPasswordDialog.value)
CustomElements.PasswordDialog(
ctx = ctx,
showPasswordDialog = showPasswordDialog,
password = password,
keyboardController = keyboardController,
title = passwordTitle,
okAction = {
if (passwordTitle == getString(R.string.encrypt_password))
backup(password.value)
else
restore(password.value)
password.value = ""
},
cancelAction = {
if (downloadsOutputUri != null) {
Utils.deleteFile(ctx, downloadsOutputUri!!)
}
}
}
}
)
}
private fun calls(ctx: Context) {