refactoring
This commit is contained in:
parent
fb565c1a9d
commit
dad71108d8
17 changed files with 394 additions and 297 deletions
|
@ -1,4 +1,3 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||||
|
|
|
@ -25,6 +25,17 @@
|
||||||
<option name="screenX" value="1080" />
|
<option name="screenX" value="1080" />
|
||||||
<option name="screenY" value="2160" />
|
<option name="screenY" value="2160" />
|
||||||
</PersistentDeviceSelectionData>
|
</PersistentDeviceSelectionData>
|
||||||
|
<PersistentDeviceSelectionData>
|
||||||
|
<option name="api" value="34" />
|
||||||
|
<option name="brand" value="Lenovo" />
|
||||||
|
<option name="codename" value="TB370FU" />
|
||||||
|
<option name="id" value="TB370FU" />
|
||||||
|
<option name="manufacturer" value="Lenovo" />
|
||||||
|
<option name="name" value="Tab P12" />
|
||||||
|
<option name="screenDensity" value="340" />
|
||||||
|
<option name="screenX" value="1840" />
|
||||||
|
<option name="screenY" value="2944" />
|
||||||
|
</PersistentDeviceSelectionData>
|
||||||
<PersistentDeviceSelectionData>
|
<PersistentDeviceSelectionData>
|
||||||
<option name="api" value="31" />
|
<option name="api" value="31" />
|
||||||
<option name="brand" value="samsung" />
|
<option name="brand" value="samsung" />
|
||||||
|
|
|
@ -23,9 +23,6 @@ android {
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
useSupportLibrary = true
|
useSupportLibrary = true
|
||||||
}
|
}
|
||||||
|
|
||||||
buildConfigField("String", "CURRENCY_UNIT", "\"zł\"")
|
|
||||||
buildConfigField("String", "CURRENCY_CENT", "\"gr\"")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|
|
@ -20,9 +20,9 @@
|
||||||
android:label="@string/title_activity_wallet"
|
android:label="@string/title_activity_wallet"
|
||||||
android:theme="@style/Theme.CoinCounter" />
|
android:theme="@style/Theme.CoinCounter" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".home.HomeActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:label="@string/title_activity_main"
|
android:label="@string/title_activity_home"
|
||||||
android:theme="@style/Theme.CoinCounter">
|
android:theme="@style/Theme.CoinCounter">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
54
app/src/main/java/eu/m724/coincounter/CurrencyUtils.kt
Normal file
54
app/src/main/java/eu/m724/coincounter/CurrencyUtils.kt
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package eu.m724.coincounter
|
||||||
|
|
||||||
|
import android.icu.number.LocalizedNumberFormatter
|
||||||
|
import android.icu.number.Notation
|
||||||
|
import android.icu.number.NumberFormatter
|
||||||
|
import android.icu.number.Precision
|
||||||
|
import android.icu.util.Currency
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
class CurrencyUtils {
|
||||||
|
companion object {
|
||||||
|
private val locale: Locale = Locale.getDefault() // TODO
|
||||||
|
private val currency: Currency = Currency.getInstance(locale)
|
||||||
|
|
||||||
|
private val currencyFormatter: LocalizedNumberFormatter = NumberFormatter
|
||||||
|
.withLocale(locale)
|
||||||
|
.notation(Notation.compactShort())
|
||||||
|
.unit(currency)
|
||||||
|
.precision(Precision.fixedFraction(2))!! // TODO too
|
||||||
|
private val numberFormatter: LocalizedNumberFormatter = NumberFormatter
|
||||||
|
.withLocale(locale) // TODO
|
||||||
|
.notation(Notation.compactShort())
|
||||||
|
.precision(Precision.fixedFraction(2))!! // TODO this too
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Short currency symbol, like €
|
||||||
|
*/
|
||||||
|
val currencySymbol: String = currency.symbol
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Currency name, like euro
|
||||||
|
*/
|
||||||
|
val currencyName: String = currency.displayName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a currency value, like 10,00€
|
||||||
|
*
|
||||||
|
* @param units cents, 100 = 1 unit
|
||||||
|
*/
|
||||||
|
fun formatCurrency(units: Int): String {
|
||||||
|
return currencyFormatter.format(units / 100.0).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats a number without currency unit, like 10,00
|
||||||
|
*
|
||||||
|
* @param units cents, 100 = 1 unit
|
||||||
|
*/
|
||||||
|
fun formatNoCurrency(units: Int): String {
|
||||||
|
return numberFormatter.unit(null).format(units / 100.0).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,279 +0,0 @@
|
||||||
package eu.m724.coincounter
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.activity.compose.setContent
|
|
||||||
import androidx.activity.enableEdgeToEdge
|
|
||||||
import androidx.activity.viewModels
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|
||||||
import androidx.compose.foundation.layout.FlowRow
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.filled.Add
|
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.ButtonDefaults
|
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.CardDefaults
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.LocalTextStyle
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.draw.rotate
|
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
|
||||||
import androidx.compose.ui.focus.focusRequester
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
import eu.m724.coincounter.data.entity.Wallet
|
|
||||||
import eu.m724.coincounter.ui.theme.CoinCounterTheme
|
|
||||||
import eu.m724.coincounter.wallet.WalletActivity
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
// TODO modularize
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class MainActivity : ComponentActivity() {
|
|
||||||
private val viewModel: MainViewModel by viewModels()
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
lifecycleScope.launch {
|
|
||||||
viewModel.openEvent.collect {
|
|
||||||
openWallet(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enableEdgeToEdge()
|
|
||||||
setContent {
|
|
||||||
CoinCounterTheme {
|
|
||||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.padding(innerPadding)
|
|
||||||
) {
|
|
||||||
App(
|
|
||||||
viewModel = viewModel,
|
|
||||||
onClick = {
|
|
||||||
openWallet(it.id)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun openWallet(walletId: Long) {
|
|
||||||
val intent = Intent(application.applicationContext, WalletActivity::class.java)
|
|
||||||
intent.putExtra("walletId", walletId)
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun App(
|
|
||||||
viewModel: MainViewModel,
|
|
||||||
onClick: (Wallet) -> Unit
|
|
||||||
) {
|
|
||||||
val total by viewModel.totalBalance.collectAsState(initial = 0)
|
|
||||||
val wallets by viewModel.wallets.collectAsState(initial = listOf())
|
|
||||||
|
|
||||||
Column {
|
|
||||||
BalanceView(total)
|
|
||||||
WalletList(
|
|
||||||
wallets = wallets,
|
|
||||||
onClick = {
|
|
||||||
onClick(it)
|
|
||||||
},
|
|
||||||
onAdd = {
|
|
||||||
viewModel.addWallet(it)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun BalanceView(balance: Int) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.height(150.dp)
|
|
||||||
.fillMaxWidth(),
|
|
||||||
verticalArrangement = Arrangement.Center,
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.Bottom
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "%.2f".format(balance / 100.0),
|
|
||||||
fontSize = 32.sp
|
|
||||||
)
|
|
||||||
Text(
|
|
||||||
text = BuildConfig.CURRENCY_UNIT,
|
|
||||||
fontSize = 14.sp
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
|
||||||
@Composable
|
|
||||||
fun WalletList(
|
|
||||||
wallets: List<Wallet>,
|
|
||||||
onClick: (Wallet) -> Unit,
|
|
||||||
onAdd: (String) -> Unit
|
|
||||||
) {
|
|
||||||
FlowRow(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.Center,
|
|
||||||
maxItemsInEachRow = 3
|
|
||||||
) {
|
|
||||||
wallets.forEach { wallet ->
|
|
||||||
WalletCard(
|
|
||||||
wallet = wallet,
|
|
||||||
onClick = {
|
|
||||||
onClick(wallet)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
AddWalletButton(
|
|
||||||
onAdd = onAdd
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun WalletCard(
|
|
||||||
wallet: Wallet,
|
|
||||||
onClick: () -> Unit
|
|
||||||
) {
|
|
||||||
Button(
|
|
||||||
modifier = Modifier.padding(8.dp),
|
|
||||||
onClick = onClick,
|
|
||||||
colors = ButtonDefaults.buttonColors().copy(
|
|
||||||
containerColor = CardDefaults.cardColors().containerColor,
|
|
||||||
contentColor = CardDefaults.cardColors().contentColor
|
|
||||||
),
|
|
||||||
shape = RoundedCornerShape(30),
|
|
||||||
contentPadding = PaddingValues(16.dp, 4.dp)
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
Text(wallet.label)
|
|
||||||
Text(formatCurrency(wallet.balance))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun AddWalletButton(
|
|
||||||
onAdd: (String) -> Unit
|
|
||||||
) {
|
|
||||||
var label by remember { mutableStateOf("") }
|
|
||||||
var expanded by remember { mutableStateOf(false) }
|
|
||||||
val angle by animateFloatAsState(
|
|
||||||
targetValue = if (expanded && label.isBlank()) 45f else 0f,
|
|
||||||
label = "Add button rotation"
|
|
||||||
)
|
|
||||||
val focusRequester = remember { FocusRequester() }
|
|
||||||
|
|
||||||
Card(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(8.dp)
|
|
||||||
.height(48.dp)
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
AnimatedVisibility(
|
|
||||||
visible = expanded
|
|
||||||
) {
|
|
||||||
BasicTextField(
|
|
||||||
value = label,
|
|
||||||
onValueChange = { label = it },
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(start = 16.dp)
|
|
||||||
.focusRequester(focusRequester),
|
|
||||||
textStyle = LocalTextStyle.current,
|
|
||||||
keyboardOptions = KeyboardOptions(
|
|
||||||
imeAction = ImeAction.Done
|
|
||||||
),
|
|
||||||
keyboardActions = KeyboardActions(
|
|
||||||
onDone = {
|
|
||||||
expanded = false
|
|
||||||
onAdd(label)
|
|
||||||
label = ""
|
|
||||||
}
|
|
||||||
),
|
|
||||||
singleLine = true
|
|
||||||
)
|
|
||||||
LaunchedEffect(expanded) {
|
|
||||||
focusRequester.requestFocus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button(
|
|
||||||
modifier = Modifier.fillMaxHeight(),
|
|
||||||
onClick = {
|
|
||||||
if (!expanded) {
|
|
||||||
expanded = true
|
|
||||||
} else {
|
|
||||||
if (label.isBlank()) {
|
|
||||||
expanded = false
|
|
||||||
} else {
|
|
||||||
onAdd(label)
|
|
||||||
expanded = false
|
|
||||||
}
|
|
||||||
label = ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
colors = ButtonDefaults.buttonColors().copy(
|
|
||||||
containerColor = CardDefaults.cardColors().containerColor,
|
|
||||||
contentColor = CardDefaults.cardColors().contentColor
|
|
||||||
),
|
|
||||||
shape = RoundedCornerShape(30),
|
|
||||||
contentPadding = PaddingValues(16.dp, 4.dp)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Filled.Add,
|
|
||||||
contentDescription = stringResource(R.string.home_add_wallet),
|
|
||||||
modifier = Modifier.rotate(angle)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun formatCurrency(units: Int): String {
|
|
||||||
if (units < 100) {
|
|
||||||
return "$units ${BuildConfig.CURRENCY_CENT}"
|
|
||||||
}
|
|
||||||
return "%.2f %s".format(units / 100.0, BuildConfig.CURRENCY_UNIT)
|
|
||||||
}
|
|
94
app/src/main/java/eu/m724/coincounter/home/HomeActivity.kt
Normal file
94
app/src/main/java/eu/m724/coincounter/home/HomeActivity.kt
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
package eu.m724.coincounter.home
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import eu.m724.coincounter.CurrencyUtils
|
||||||
|
import eu.m724.coincounter.home.compose.HomeActivityView
|
||||||
|
import eu.m724.coincounter.ui.theme.CoinCounterTheme
|
||||||
|
import eu.m724.coincounter.wallet.WalletActivity
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
// TODO modularize
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class HomeActivity : ComponentActivity() {
|
||||||
|
private val viewModel: HomeViewModel by viewModels()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
viewModel.openEvent.collect {
|
||||||
|
openWallet(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enableEdgeToEdge()
|
||||||
|
setContent {
|
||||||
|
CoinCounterTheme {
|
||||||
|
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.padding(innerPadding)
|
||||||
|
) {
|
||||||
|
HomeActivityView(
|
||||||
|
viewModel = viewModel,
|
||||||
|
onClick = {
|
||||||
|
openWallet(it.id)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun openWallet(walletId: Long) {
|
||||||
|
val intent = Intent(application.applicationContext, WalletActivity::class.java)
|
||||||
|
intent.putExtra("walletId", walletId)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun BalanceView(balance: Int) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.height(150.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.Bottom
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = CurrencyUtils.formatNoCurrency(balance),
|
||||||
|
fontSize = 32.sp
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = CurrencyUtils.currencySymbol,
|
||||||
|
fontSize = 14.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.m724.coincounter
|
package eu.m724.coincounter.home
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
@ -13,7 +13,7 @@ import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class MainViewModel @Inject constructor(
|
class HomeViewModel @Inject constructor(
|
||||||
private val repository: WalletRepository
|
private val repository: WalletRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val wallets: Flow<List<Wallet>> = repository.getAllWallets()
|
val wallets: Flow<List<Wallet>> = repository.getAllWallets()
|
|
@ -0,0 +1,114 @@
|
||||||
|
package eu.m724.coincounter.home.compose
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.LocalTextStyle
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import eu.m724.coincounter.R
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CreateWalletButton(
|
||||||
|
onCreate: (String) -> Unit
|
||||||
|
) {
|
||||||
|
var label by remember { mutableStateOf("") }
|
||||||
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
val angle by animateFloatAsState(
|
||||||
|
targetValue = if (expanded && label.isBlank()) 45f else 0f,
|
||||||
|
label = "Add button rotation"
|
||||||
|
)
|
||||||
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.height(48.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = expanded
|
||||||
|
) {
|
||||||
|
BasicTextField(
|
||||||
|
value = label,
|
||||||
|
onValueChange = { label = it },
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(start = 16.dp)
|
||||||
|
.focusRequester(focusRequester),
|
||||||
|
textStyle = LocalTextStyle.current,
|
||||||
|
keyboardOptions = KeyboardOptions(
|
||||||
|
imeAction = ImeAction.Done
|
||||||
|
),
|
||||||
|
keyboardActions = KeyboardActions(
|
||||||
|
onDone = {
|
||||||
|
expanded = false
|
||||||
|
onCreate(label)
|
||||||
|
label = ""
|
||||||
|
}
|
||||||
|
),
|
||||||
|
singleLine = true
|
||||||
|
)
|
||||||
|
LaunchedEffect(expanded) {
|
||||||
|
focusRequester.requestFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button(
|
||||||
|
modifier = Modifier.fillMaxHeight(),
|
||||||
|
onClick = {
|
||||||
|
if (!expanded) {
|
||||||
|
expanded = true
|
||||||
|
} else {
|
||||||
|
if (label.isBlank()) {
|
||||||
|
expanded = false
|
||||||
|
} else {
|
||||||
|
onCreate(label)
|
||||||
|
expanded = false
|
||||||
|
}
|
||||||
|
label = ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colors = ButtonDefaults.buttonColors().copy(
|
||||||
|
containerColor = CardDefaults.cardColors().containerColor,
|
||||||
|
contentColor = CardDefaults.cardColors().contentColor
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(30),
|
||||||
|
contentPadding = PaddingValues(16.dp, 4.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Add,
|
||||||
|
contentDescription = stringResource(R.string.home_add_wallet),
|
||||||
|
modifier = Modifier.rotate(angle)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package eu.m724.coincounter.home.compose
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import eu.m724.coincounter.data.entity.Wallet
|
||||||
|
import eu.m724.coincounter.home.BalanceView
|
||||||
|
import eu.m724.coincounter.home.HomeViewModel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HomeActivityView(
|
||||||
|
viewModel: HomeViewModel,
|
||||||
|
onClick: (Wallet) -> Unit
|
||||||
|
) {
|
||||||
|
val total by viewModel.totalBalance.collectAsState(initial = 0)
|
||||||
|
val wallets by viewModel.wallets.collectAsState(initial = listOf())
|
||||||
|
|
||||||
|
Column {
|
||||||
|
BalanceView(total)
|
||||||
|
WalletList(
|
||||||
|
wallets = wallets,
|
||||||
|
onClick = {
|
||||||
|
onClick(it)
|
||||||
|
},
|
||||||
|
onCreate = {
|
||||||
|
viewModel.addWallet(it)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package eu.m724.coincounter.home.compose
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import eu.m724.coincounter.CurrencyUtils.Companion.formatCurrency
|
||||||
|
import eu.m724.coincounter.data.entity.Wallet
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun WalletCard(
|
||||||
|
wallet: Wallet,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
modifier = Modifier.padding(8.dp),
|
||||||
|
onClick = onClick,
|
||||||
|
colors = ButtonDefaults.buttonColors().copy(
|
||||||
|
containerColor = CardDefaults.cardColors().containerColor,
|
||||||
|
contentColor = CardDefaults.cardColors().contentColor
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(30),
|
||||||
|
contentPadding = PaddingValues(16.dp, 4.dp)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Text(wallet.label)
|
||||||
|
Text(formatCurrency(wallet.balance))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package eu.m724.coincounter.home.compose
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import eu.m724.coincounter.data.entity.Wallet
|
||||||
|
|
||||||
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
|
@Composable
|
||||||
|
fun WalletList(
|
||||||
|
wallets: List<Wallet>,
|
||||||
|
onClick: (Wallet) -> Unit,
|
||||||
|
onCreate: (String) -> Unit
|
||||||
|
) {
|
||||||
|
FlowRow(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.Center,
|
||||||
|
maxItemsInEachRow = 3
|
||||||
|
) {
|
||||||
|
wallets.forEach { wallet ->
|
||||||
|
WalletCard(
|
||||||
|
wallet = wallet,
|
||||||
|
onClick = {
|
||||||
|
onClick(wallet)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
CreateWalletButton(
|
||||||
|
onCreate = onCreate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,7 @@ import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import eu.m724.coincounter.CurrencyUtils
|
||||||
import eu.m724.coincounter.R
|
import eu.m724.coincounter.R
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,7 +108,7 @@ fun TransactionDialog(
|
||||||
.focusRequester(secondFocus),
|
.focusRequester(secondFocus),
|
||||||
supportingText = {
|
supportingText = {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.create_transaction_value),
|
text = stringResource(R.string.create_transaction_value, CurrencyUtils.currencyName),
|
||||||
color = if (!valueValid) MaterialTheme.colorScheme.error else Color.Unspecified
|
color = if (!valueValid) MaterialTheme.colorScheme.error else Color.Unspecified
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,7 +17,7 @@ import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.m724.coincounter.BuildConfig
|
import eu.m724.coincounter.CurrencyUtils
|
||||||
import eu.m724.coincounter.wallet.WalletViewModel
|
import eu.m724.coincounter.wallet.WalletViewModel
|
||||||
import java.text.DateFormat
|
import java.text.DateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
@ -54,7 +54,7 @@ fun TransactionList(
|
||||||
.weight(1f))
|
.weight(1f))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = "%.2f %s".format(transaction.value / 100.0, BuildConfig.CURRENCY_UNIT),
|
text = CurrencyUtils.formatCurrency(transaction.value),
|
||||||
color = if (transaction.value < 0) MaterialTheme.colorScheme.error else Color.Unspecified
|
color = if (transaction.value < 0) MaterialTheme.colorScheme.error else Color.Unspecified
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import eu.m724.coincounter.BuildConfig
|
import eu.m724.coincounter.CurrencyUtils
|
||||||
import eu.m724.coincounter.wallet.WalletViewModel
|
import eu.m724.coincounter.wallet.WalletViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -86,11 +86,11 @@ fun WalletActivityView(
|
||||||
verticalAlignment = Alignment.Bottom,
|
verticalAlignment = Alignment.Bottom,
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "%.2f".format(wallet.balance / 100.0),
|
text = CurrencyUtils.formatNoCurrency(wallet.balance),
|
||||||
fontSize = 32.sp
|
fontSize = 32.sp
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = BuildConfig.CURRENCY_UNIT,
|
text = CurrencyUtils.currencySymbol,
|
||||||
fontSize = 14.sp
|
fontSize = 14.sp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Coin Counter</string>
|
<string name="app_name">Coin Counter</string>
|
||||||
<string name="title_activity_main">Coin Counter</string>
|
<string name="title_activity_home">Coin Counter</string>
|
||||||
<string name="title_activity_wallet">WalletActivity</string>
|
<string name="title_activity_wallet">WalletActivity</string>
|
||||||
<string name="create_transaction_confirm">Utwórz transakcję</string>
|
<string name="create_transaction_confirm">Utwórz transakcję</string>
|
||||||
<string name="create_transaction_nan">Wartość musi być liczbą</string>
|
<string name="create_transaction_nan">Wartość musi być liczbą</string>
|
||||||
<string name="create_transaction_cancel">Anuluj</string>
|
<string name="create_transaction_cancel">Anuluj</string>
|
||||||
<string name="create_transaction_absolute">Bezwzględna</string>
|
<string name="create_transaction_absolute">Bezwzględna</string>
|
||||||
<string name="create_transaction_value_error">Błąd</string>
|
<string name="create_transaction_value_error">Błąd</string>
|
||||||
<string name="create_transaction_value">Wartość</string>
|
<string name="create_transaction_value">Wartość (%1$s)</string>
|
||||||
<string name="create_transaction_label">Etykieta</string>
|
<string name="create_transaction_label">Etykieta</string>
|
||||||
<string name="wallet_actions_rename">Zmień nazwę portfela</string>
|
<string name="wallet_actions_rename">Zmień nazwę portfela</string>
|
||||||
<string name="wallet_actions_delete">Usuń portfel</string>
|
<string name="wallet_actions_delete">Usuń portfel</string>
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Coin Counter</string>
|
<string name="app_name">Coin Counter</string>
|
||||||
<string name="title_activity_main">Coin Counter</string>
|
<string name="title_activity_home">Coin Counter</string>
|
||||||
<string name="title_activity_wallet">WalletActivity</string>
|
<string name="title_activity_wallet">WalletActivity</string>
|
||||||
<string name="create_transaction_confirm">Create transaction</string>
|
<string name="create_transaction_confirm">Create transaction</string>
|
||||||
<string name="create_transaction_nan">Value must be a number</string>
|
<string name="create_transaction_nan">Value must be a number</string>
|
||||||
<string name="create_transaction_cancel">Cancel</string>
|
<string name="create_transaction_cancel">Cancel</string>
|
||||||
<string name="create_transaction_absolute">Absolute</string>
|
<string name="create_transaction_absolute">Absolute</string>
|
||||||
<string name="create_transaction_value_error">Error</string>
|
<string name="create_transaction_value_error">Error</string>
|
||||||
<string name="create_transaction_value">Value</string>
|
<string name="create_transaction_value">Value (%1$s)</string>
|
||||||
<string name="create_transaction_label">Label</string>
|
<string name="create_transaction_label">Label</string>
|
||||||
<string name="wallet_actions_rename">Rename wallet</string>
|
<string name="wallet_actions_rename">Rename wallet</string>
|
||||||
<string name="wallet_actions_delete">Delete wallet</string>
|
<string name="wallet_actions_delete">Delete wallet</string>
|
||||||
|
|
Loading…
Reference in a new issue