Fix keyboard padding and better currency management
This commit is contained in:
@@ -117,6 +117,7 @@ fun EditBillScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(padding)
|
||||
.imePadding()
|
||||
.padding(16.dp)
|
||||
.fillMaxSize()
|
||||
.verticalScroll(scrollState)
|
||||
|
||||
+33
-9
@@ -67,8 +67,10 @@ class ManageCurrenciesActivity : AppCompatActivity() {
|
||||
viewModel = viewModel,
|
||||
onBack = { finish() },
|
||||
onSaveMain = { saveMainCurrency() },
|
||||
onAdd = { addCurrency() },
|
||||
onDelete = { deleteCurrency(it) }
|
||||
onAdd = { addOrUpdateCurrency() },
|
||||
onDelete = { deleteCurrency(it) },
|
||||
onEdit = { startEditing(it) },
|
||||
onCancelEdit = { cancelEditing() }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -105,22 +107,44 @@ class ManageCurrenciesActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun addCurrency() {
|
||||
private fun addOrUpdateCurrency() {
|
||||
val exchangeRate = try { viewModel.newCurrencyRate.toDouble() } catch (_: Exception) { 0.0 }
|
||||
val newCurrency = DBCurrency(
|
||||
0, 0, selectedProjectID,
|
||||
viewModel.newCurrencyName, exchangeRate, DBBill.STATE_ADDED
|
||||
)
|
||||
val currencyName = viewModel.newCurrencyName
|
||||
val editingId = viewModel.editingCurrencyId
|
||||
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (editingId != null) {
|
||||
db!!.updateCurrency(editingId, currencyName, exchangeRate)
|
||||
val currency = db!!.getCurrency(editingId)
|
||||
if (currency != null) {
|
||||
db!!.setCurrencyStateSync(editingId, DBBill.STATE_EDITED)
|
||||
}
|
||||
} else {
|
||||
val newCurrency = DBCurrency(
|
||||
0, 0, selectedProjectID,
|
||||
currencyName, exchangeRate, DBBill.STATE_ADDED
|
||||
)
|
||||
db!!.addCurrencyAndSync(newCurrency)
|
||||
}
|
||||
viewModel.newCurrencyName = ""
|
||||
viewModel.newCurrencyRate = ""
|
||||
}
|
||||
cancelEditing()
|
||||
updateCurrenciesList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun startEditing(currency: DBCurrency) {
|
||||
viewModel.editingCurrencyId = currency.id
|
||||
viewModel.newCurrencyName = currency.name ?: ""
|
||||
viewModel.newCurrencyRate = currency.exchangeRate.toString()
|
||||
}
|
||||
|
||||
private fun cancelEditing() {
|
||||
viewModel.editingCurrencyId = null
|
||||
viewModel.newCurrencyName = ""
|
||||
viewModel.newCurrencyRate = ""
|
||||
}
|
||||
|
||||
private fun deleteCurrency(currency: DBCurrency) {
|
||||
lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
|
||||
+168
-58
@@ -1,6 +1,9 @@
|
||||
package net.helcel.cowspent.android.currencies
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
@@ -8,16 +11,25 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Done
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlinx.coroutines.delay
|
||||
import net.helcel.cowspent.R
|
||||
import net.helcel.cowspent.android.helper.AlertDialog
|
||||
import net.helcel.cowspent.android.helper.TextIcon
|
||||
import net.helcel.cowspent.android.helper.TextIconDisplay
|
||||
import net.helcel.cowspent.android.helper.formatAmount
|
||||
import net.helcel.cowspent.model.DBCurrency
|
||||
|
||||
@@ -27,7 +39,9 @@ fun ManageCurrenciesScreen(
|
||||
onBack: () -> Unit,
|
||||
onSaveMain: () -> Unit,
|
||||
onAdd: () -> Unit,
|
||||
onDelete: (DBCurrency) -> Unit
|
||||
onDelete: (DBCurrency) -> Unit,
|
||||
onEdit: (DBCurrency) -> Unit,
|
||||
onCancelEdit: () -> Unit
|
||||
) {
|
||||
val dialogState = viewModel.dialogState
|
||||
if (dialogState != null) {
|
||||
@@ -76,78 +90,152 @@ fun ManageCurrenciesScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(padding)
|
||||
.padding(16.dp)
|
||||
.imePadding()
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colors.onSurface.copy(alpha = 0.02f))
|
||||
) {
|
||||
LaunchedEffect(viewModel.mainCurrencyName) {
|
||||
if (viewModel.mainCurrencyName.isNotEmpty()) {
|
||||
delay(500)
|
||||
onSaveMain()
|
||||
}
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
// Section 1: Main Currency
|
||||
Text(
|
||||
text = stringResource(R.string.main_currency).uppercase(),
|
||||
style = MaterialTheme.typography.overline,
|
||||
color = MaterialTheme.colors.primary,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Card(
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
elevation = 2.dp,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(stringResource(R.string.main_currency), style = MaterialTheme.typography.h6)
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
OutlinedTextField(
|
||||
value = viewModel.mainCurrencyName,
|
||||
onValueChange = { viewModel.mainCurrencyName = it },
|
||||
label = { Text(stringResource(R.string.currency_edit_name)) },
|
||||
modifier = Modifier.weight(1f)
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = { Text("e.g. EUR", fontSize = 14.sp) },
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
focusedBorderColor = Color.Transparent,
|
||||
unfocusedBorderColor = Color.Transparent
|
||||
)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Button(onClick = onSaveMain, enabled = viewModel.mainCurrencyName.isNotEmpty()) {
|
||||
Text(stringResource(R.string.save_or_discard_bill_dialog_save))
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
Text(stringResource(R.string.add_currency_title), style = MaterialTheme.typography.h6)
|
||||
// Section 2: Exchange Rates
|
||||
val isEditing = viewModel.editingCurrencyId != null
|
||||
Text(
|
||||
text = (if (isEditing) "Edit exchange rate" else "Add exchange rate").uppercase(),
|
||||
style = MaterialTheme.typography.overline,
|
||||
color = if (isEditing) MaterialTheme.colors.secondary else MaterialTheme.colors.primary,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
// Visual relationship indicator
|
||||
// Integrated Add/Edit Ribbon
|
||||
Surface(
|
||||
color = MaterialTheme.colors.primary.copy(alpha = 0.05f),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp)
|
||||
color = if (isEditing) MaterialTheme.colors.secondary.copy(alpha = 0.08f) else MaterialTheme.colors.surface,
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
elevation = if (isEditing) 4.dp else 2.dp,
|
||||
border = if (isEditing) BorderStroke(2.dp, MaterialTheme.colors.secondary) else null
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text("1 ", style = MaterialTheme.typography.body1)
|
||||
Text("1 ", style = MaterialTheme.typography.body2, color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f))
|
||||
Text(viewModel.mainCurrencyName.ifEmpty { "$" }, fontWeight = FontWeight.Bold)
|
||||
Text(" = ", style = MaterialTheme.typography.h6)
|
||||
Text(viewModel.newCurrencyRate.ifEmpty { "0.0" }, fontWeight = FontWeight.Bold, color = MaterialTheme.colors.primary)
|
||||
Text(" ")
|
||||
Text(viewModel.newCurrencyName.ifEmpty { "Currency" }, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
}
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
OutlinedTextField(
|
||||
value = viewModel.newCurrencyName,
|
||||
onValueChange = { viewModel.newCurrencyName = it },
|
||||
label = { Text(stringResource(R.string.currency_edit_name)) },
|
||||
modifier = Modifier.weight(1f),
|
||||
placeholder = { Text("e.g. USD") }
|
||||
Text(
|
||||
text = " = ",
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = if (isEditing) MaterialTheme.colors.secondary else MaterialTheme.colors.primary,
|
||||
modifier = Modifier.padding(horizontal = 4.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
value = viewModel.newCurrencyRate,
|
||||
onValueChange = { viewModel.newCurrencyRate = it },
|
||||
label = { Text(stringResource(R.string.currency_rate)) },
|
||||
modifier = Modifier.weight(1f),
|
||||
placeholder = { Text("1.0") }
|
||||
modifier = Modifier.weight(1.2f).height(52.dp),
|
||||
placeholder = { Text("Rate", fontSize = 12.sp) },
|
||||
singleLine = true,
|
||||
textStyle = LocalTextStyle.current.copy(fontWeight = FontWeight.ExtraBold, fontSize = 14.sp),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
focusedBorderColor = if (isEditing) MaterialTheme.colors.secondary else MaterialTheme.colors.primary,
|
||||
unfocusedBorderColor = MaterialTheme.colors.onSurface.copy(alpha = 0.12f),
|
||||
backgroundColor = MaterialTheme.colors.onSurface.copy(alpha = 0.02f)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Button(
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
OutlinedTextField(
|
||||
value = viewModel.newCurrencyName,
|
||||
onValueChange = { viewModel.newCurrencyName = it },
|
||||
modifier = Modifier.weight(1f).height(52.dp),
|
||||
placeholder = { Text("Code", fontSize = 12.sp) },
|
||||
singleLine = true,
|
||||
textStyle = LocalTextStyle.current.copy(fontWeight = FontWeight.Bold, fontSize = 14.sp),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
colors = TextFieldDefaults.outlinedTextFieldColors(
|
||||
focusedBorderColor = if (isEditing) MaterialTheme.colors.secondary else MaterialTheme.colors.primary,
|
||||
unfocusedBorderColor = MaterialTheme.colors.onSurface.copy(alpha = 0.12f),
|
||||
backgroundColor = MaterialTheme.colors.onSurface.copy(alpha = 0.02f)
|
||||
)
|
||||
)
|
||||
|
||||
Row(modifier = Modifier.padding(start = 4.dp)) {
|
||||
IconButton(
|
||||
onClick = onAdd,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = viewModel.isAddEnabled()
|
||||
) {
|
||||
Text(stringResource(R.string.simple_add))
|
||||
Icon(
|
||||
imageVector = if (isEditing) Icons.Default.Done else Icons.Default.Add,
|
||||
contentDescription = null,
|
||||
tint = if (viewModel.isAddEnabled()) {
|
||||
if (isEditing) MaterialTheme.colors.secondary else MaterialTheme.colors.primary
|
||||
} else MaterialTheme.colors.onSurface.copy(alpha = 0.2f)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
if (isEditing) {
|
||||
IconButton(onClick = onCancelEdit) {
|
||||
Icon(Icons.Default.Close, contentDescription = null, tint = MaterialTheme.colors.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LazyColumn(modifier = Modifier.weight(1f)) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = "SAVED RATES",
|
||||
style = MaterialTheme.typography.overline,
|
||||
color = MaterialTheme.colors.onSurface.copy(alpha = 0.4f),
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.weight(1f).fillMaxWidth(),
|
||||
contentPadding = PaddingValues(16.dp, 0.dp, 16.dp, 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
items(viewModel.currencies) { currency ->
|
||||
CurrencyRow(currency, viewModel.mainCurrencyName, onDelete = { onDelete(currency) })
|
||||
CurrencyRow(
|
||||
currency = currency,
|
||||
mainCurrencyName = viewModel.mainCurrencyName,
|
||||
isEditing = viewModel.editingCurrencyId == currency.id,
|
||||
onEdit = { onEdit(currency) },
|
||||
onDelete = { onDelete(currency) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,19 +243,33 @@ fun ManageCurrenciesScreen(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CurrencyRow(currency: DBCurrency, mainCurrencyName: String, onDelete: () -> Unit) {
|
||||
fun CurrencyRow(
|
||||
currency: DBCurrency,
|
||||
mainCurrencyName: String,
|
||||
isEditing: Boolean,
|
||||
onEdit: () -> Unit,
|
||||
onDelete: () -> Unit
|
||||
) {
|
||||
Card(
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
elevation = if (isEditing) 4.dp else 1.dp,
|
||||
border = if (isEditing) BorderStroke(1.dp, MaterialTheme.colors.secondary.copy(alpha = 0.5f)) else null,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 12.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onEdit)
|
||||
.padding(16.dp, 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Table-like layout
|
||||
Row(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = "1 ",
|
||||
style = MaterialTheme.typography.body1,
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f)
|
||||
)
|
||||
Text(
|
||||
@@ -178,29 +280,33 @@ fun CurrencyRow(currency: DBCurrency, mainCurrencyName: String, onDelete: () ->
|
||||
Text(
|
||||
text = " = ",
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.primary,
|
||||
color = if (isEditing) MaterialTheme.colors.secondary else MaterialTheme.colors.primary,
|
||||
modifier = Modifier.padding(horizontal = 8.dp)
|
||||
)
|
||||
Column {
|
||||
Text(
|
||||
text = formatAmount(currency.exchangeRate),
|
||||
style = MaterialTheme.typography.body1,
|
||||
fontWeight = FontWeight.ExtraBold
|
||||
fontWeight = FontWeight.ExtraBold,
|
||||
color = if (isEditing) MaterialTheme.colors.secondary else MaterialTheme.colors.onSurface
|
||||
)
|
||||
Text(
|
||||
text = currency.name ?: "",
|
||||
style = MaterialTheme.typography.caption,
|
||||
text = " ${currency.name ?: ""}",
|
||||
style = MaterialTheme.typography.body1,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colors.onSurface.copy(alpha = 0.6f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
IconButton(onClick = onDelete) {
|
||||
Icon(Icons.Default.Delete, contentDescription = null, tint = MaterialTheme.colors.error)
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colors.error.copy(alpha = 0.7f),
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@@ -210,6 +316,8 @@ fun CurrencyRowPreview() {
|
||||
CurrencyRow(
|
||||
currency = DBCurrency(1, 0, 0, "USD", 1.0, 0),
|
||||
mainCurrencyName = "EUR",
|
||||
isEditing = false,
|
||||
onEdit = {},
|
||||
onDelete = {}
|
||||
)
|
||||
}
|
||||
@@ -231,7 +339,9 @@ fun ManageCurrenciesScreenPreview() {
|
||||
onBack = {},
|
||||
onSaveMain = {},
|
||||
onAdd = {},
|
||||
onDelete = {}
|
||||
onDelete = {},
|
||||
onEdit = {},
|
||||
onCancelEdit = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ class ManageCurrenciesViewModel : ViewModel() {
|
||||
var newCurrencyName by mutableStateOf("")
|
||||
var newCurrencyRate by mutableStateOf("")
|
||||
|
||||
var editingCurrencyId by mutableStateOf<Long?>(null)
|
||||
|
||||
var currencies by mutableStateOf<List<DBCurrency>>(emptyList())
|
||||
|
||||
var dialogState by mutableStateOf<DialogState?>(null)
|
||||
|
||||
@@ -63,6 +63,7 @@ fun NewProjectScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(padding)
|
||||
.imePadding()
|
||||
.padding(16.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.fillMaxSize()
|
||||
|
||||
@@ -2,6 +2,8 @@ package net.helcel.cowspent.android.project.edit
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
@@ -80,11 +82,14 @@ fun EditProjectScreen(
|
||||
}
|
||||
}
|
||||
) { padding ->
|
||||
val scrollState = rememberScrollState()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(padding)
|
||||
.imePadding()
|
||||
.padding(16.dp)
|
||||
.fillMaxSize()
|
||||
.verticalScroll(scrollState)
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = viewModel.name,
|
||||
|
||||
@@ -111,6 +111,7 @@ object ThemeUtils {
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.navigationBarsPadding()
|
||||
.imePadding()
|
||||
) {
|
||||
content()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user