Manage Currencies

This commit is contained in:
2026-09-06 02:56:02 +02:00
parent 7904aabbc3
commit c6be8eda6b
3 changed files with 138 additions and 120 deletions
@@ -7,14 +7,8 @@ import androidx.activity.enableEdgeToEdge
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.helcel.cowspent.R
import net.helcel.cowspent.android.helper.showToast
import net.helcel.cowspent.model.DBBill
import net.helcel.cowspent.model.DBCurrency
import net.helcel.cowspent.model.ProjectType
import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
import net.helcel.cowspent.theme.ThemeUtils
@@ -25,7 +19,6 @@ class ManageCurrenciesActivity : AppCompatActivity() {
internal val viewModel: ManageCurrenciesViewModel by viewModels()
private var db: CowspentSQLiteOpenHelper? = null
private var selectedProjectID: Long = -1
private val editMainCurrencyCallBack: ICallback = object : ICallback {
override fun onFinish() {}
@@ -47,131 +40,45 @@ class ManageCurrenciesActivity : AppCompatActivity() {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
intent.extras?.let {
selectedProjectID = it.getLong(EXTRA_PROJECT_ID)
}
val selectedProjectID = intent.getLongExtra(EXTRA_PROJECT_ID, -1L)
if (selectedProjectID == -1L) {
Log.e(TAG, "Missing project id")
finish()
return
}
db = CowspentSQLiteOpenHelper.getInstance(this)
lifecycleScope.launch {
val project = withContext(Dispatchers.IO) { db!!.getProject(selectedProjectID) }
viewModel.mainCurrencyName = project?.currencyName?.let { if (it == "null") "" else it } ?: ""
updateCurrenciesList()
viewModel.projectId = selectedProjectID
viewModel.loadCurrencies()
setContent {
ThemeUtils.CowspentTheme {
ManageCurrenciesScreen(
viewModel = viewModel,
onBack = { finish() },
onSaveMain = { saveMainCurrency() },
onAdd = { addOrUpdateCurrency() },
onDelete = { deleteCurrency(it) },
onEdit = { startEditing(it) },
onCancelEdit = { cancelEditing() }
)
}
db = CowspentSQLiteOpenHelper.getInstance(this)
setContent {
ThemeUtils.CowspentTheme {
ManageCurrenciesScreen(
viewModel = viewModel,
onBack = { finish() },
onSaveMain = { saveMainCurrency() },
onAdd = { viewModel.addCurrency() },
onDelete = { viewModel.deleteCurrency(it.id) },
onEdit = { viewModel.startEditing(it) },
onCancelEdit = { viewModel.cancelEditing() }
)
}
}
}
private fun saveMainCurrency() {
val newMainCurrencyName = viewModel.mainCurrencyName
lifecycleScope.launch {
withContext(Dispatchers.IO) {
db!!.updateProject(
projId = selectedProjectID,
newCurrencyName = newMainCurrencyName
)
val project = db!!.getProject(selectedProjectID)
if (project != null) {
db!!.syncIfRemote(project)
if (project.type == ProjectType.COSPEND) {
withContext(Dispatchers.Main) {
if (!db!!.cowspentServerSyncHelper
.editRemoteProject(
projId = selectedProjectID,
newName = project.name,
newMainCurrencyName = newMainCurrencyName,
callback = editMainCurrencyCallBack
)
) {
showToast(this@ManageCurrenciesActivity, getString(R.string.remote_project_operation_no_network), Toast.LENGTH_LONG)
}
}
} else {
withContext(Dispatchers.Main) {
showToast(this@ManageCurrenciesActivity, getString(R.string.currency_saved_success), Toast.LENGTH_LONG)
}
}
val project = db?.getProject(viewModel.projectId)
if (project != null) {
if (project.type == ProjectType.COSPEND) {
if (!db!!.cowspentServerSyncHelper.isSyncPossible) {
showToast(this, getString(R.string.remote_project_operation_no_network), Toast.LENGTH_LONG)
}
} else {
showToast(this, getString(R.string.currency_saved_success), Toast.LENGTH_LONG)
}
}
}
private fun addOrUpdateCurrency() {
val uiRate = try { viewModel.newCurrencyRate.toDouble() } catch (_: Exception) { 0.0 }
val exchangeRate = if (uiRate != 0.0) 1.0 / uiRate else 0.0
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)
}
}
cancelEditing()
updateCurrenciesList()
}
}
private fun startEditing(currency: DBCurrency) {
viewModel.editingCurrencyId = currency.id
viewModel.newCurrencyName = currency.name ?: ""
val uiRate = if (currency.exchangeRate != 0.0) 1.0 / currency.exchangeRate else 0.0
viewModel.newCurrencyRate = uiRate.toString()
}
private fun cancelEditing() {
viewModel.editingCurrencyId = null
viewModel.newCurrencyName = ""
viewModel.newCurrencyRate = ""
}
private fun deleteCurrency(currency: DBCurrency) {
lifecycleScope.launch {
withContext(Dispatchers.IO) {
db!!.setCurrencyStateSync(currency.id, DBBill.STATE_DELETED)
}
updateCurrenciesList()
}
}
private suspend fun updateCurrenciesList() {
val currenciesDB = withContext(Dispatchers.IO) {
val list = db!!.getCurrenciesOfProjectWithState(selectedProjectID, DBBill.STATE_ADDED).toMutableList()
list.addAll(db!!.getCurrenciesOfProjectWithState(selectedProjectID, DBBill.STATE_EDITED))
list.addAll(db!!.getCurrenciesOfProjectWithState(selectedProjectID, DBBill.STATE_OK))
list
}
withContext(Dispatchers.Main) {
viewModel.currencies = currenciesDB
}
viewModel.saveMainCurrency(editMainCurrencyCallBack)
}
override fun onSupportNavigateUp(): Boolean {
@@ -1,6 +1,7 @@
package net.helcel.cowspent.android.currencies
import android.annotation.SuppressLint
import android.app.Application
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
@@ -351,9 +352,10 @@ fun CurrencyRowPreview() {
@Preview(showBackground = true)
@Composable
fun ManageCurrenciesScreenPreview() {
val application = LocalContext.current.applicationContext as Application
MaterialTheme {
ManageCurrenciesScreen(
viewModel = ManageCurrenciesViewModel().apply {
viewModel = ManageCurrenciesViewModel(application).apply {
mainCurrencyName = "EUR"
currencies = listOf(
DBCurrency(1, 0, 0, "USD", 1.1, 0),
@@ -1,14 +1,24 @@
package net.helcel.cowspent.android.currencies
import android.app.Application
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.lifecycle.ViewModel
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.helcel.cowspent.android.helper.DialogState
import net.helcel.cowspent.model.DBBill
import net.helcel.cowspent.model.DBCurrency
import net.helcel.cowspent.model.ProjectType
import net.helcel.cowspent.persistence.CowspentSQLiteOpenHelper
import net.helcel.cowspent.util.ICallback
class ManageCurrenciesViewModel : ViewModel() {
class ManageCurrenciesViewModel(application: Application) : AndroidViewModel(application) {
var projectId: Long = -1
var mainCurrencyName by mutableStateOf("")
var newCurrencyName by mutableStateOf("")
var newCurrencyRate by mutableStateOf("")
@@ -19,6 +29,105 @@ class ManageCurrenciesViewModel : ViewModel() {
var dialogState by mutableStateOf<DialogState?>(null)
private val db = CowspentSQLiteOpenHelper.getInstance(application)
fun loadCurrencies() {
if (projectId == -1L) return
viewModelScope.launch {
val project = withContext(Dispatchers.IO) { db.getProject(projectId) }
mainCurrencyName = project?.currencyName?.let { if (it == "null") "" else it } ?: ""
updateCurrenciesList()
}
}
suspend fun updateCurrenciesList() {
val currenciesDB = withContext(Dispatchers.IO) {
val list = db.getCurrenciesOfProjectWithState(projectId, DBBill.STATE_ADDED).toMutableList()
list.addAll(db.getCurrenciesOfProjectWithState(projectId, DBBill.STATE_EDITED))
list.addAll(db.getCurrenciesOfProjectWithState(projectId, DBBill.STATE_OK))
list
}
withContext(Dispatchers.Main) {
currencies = currenciesDB
}
}
fun saveMainCurrency(callback: ICallback) {
val newMainCurrencyName = mainCurrencyName
viewModelScope.launch {
withContext(Dispatchers.IO) {
db.updateProject(
projId = projectId,
newCurrencyName = newMainCurrencyName
)
val project = db.getProject(projectId)
if (project != null) {
db.syncIfRemote(project)
if (project.type == ProjectType.COSPEND) {
withContext(Dispatchers.Main) {
if (!db.cowspentServerSyncHelper
.editRemoteProject(
projId = projectId,
newName = project.name,
newMainCurrencyName = newMainCurrencyName,
callback = callback
)
) {
// Handled by activity showing toast
}
}
}
}
}
}
}
fun addCurrency() {
val uiRate = try { newCurrencyRate.toDouble() } catch (_: Exception) { 0.0 }
val exchangeRate = if (uiRate != 0.0) 1.0 / uiRate else 0.0
val currencyName = newCurrencyName
val editingId = editingCurrencyId
viewModelScope.launch {
withContext(Dispatchers.IO) {
if (editingId != null) {
db.updateCurrency(editingId, currencyName, exchangeRate)
db.setCurrencyStateSync(editingId, DBBill.STATE_EDITED)
} else {
val newCurrency = DBCurrency(
0, 0, projectId,
currencyName, exchangeRate, DBBill.STATE_ADDED
)
db.addCurrencyAndSync(newCurrency)
}
}
cancelEditing()
updateCurrenciesList()
}
}
fun deleteCurrency(currencyId: Long) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
db.setCurrencyStateSync(currencyId, DBBill.STATE_DELETED)
}
updateCurrenciesList()
}
}
fun startEditing(currency: DBCurrency) {
editingCurrencyId = currency.id
newCurrencyName = currency.name ?: ""
val uiRate = if (currency.exchangeRate != 0.0) 1.0 / currency.exchangeRate else 0.0
newCurrencyRate = uiRate.toString()
}
fun cancelEditing() {
editingCurrencyId = null
newCurrencyName = ""
newCurrencyRate = ""
}
fun showDialog(
title: String? = null,
message: String? = null,