Compare commits
8 Commits
49eb36514d
...
7b1c0db0cc
Author | SHA1 | Date | |
---|---|---|---|
7b1c0db0cc | |||
e3a2625f68 | |||
a4513394d1 | |||
d5d525f65a | |||
bf61d878cc | |||
6c95d33526 | |||
d8ecd7b621 | |||
845715122f |
@ -1,23 +1,13 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
/*class Expense {
|
||||
var name: String = ""
|
||||
var cost: Double = 0.0
|
||||
var freq: Char = '*'
|
||||
var note: String = ""
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
/*public Expense (val name: String, val cost: Double, val freq: Char, val note: String) {
|
||||
this.name = name
|
||||
this.name = name
|
||||
this.name = name
|
||||
this.name = name
|
||||
}*/
|
||||
}*/
|
||||
|
||||
class Expense (
|
||||
var name: String = "",
|
||||
var cost: Double = 0.0,
|
||||
var freq: Char = '*',
|
||||
var note: String = "",
|
||||
) {
|
||||
}
|
||||
@Entity
|
||||
data class Expense (
|
||||
val name: String,
|
||||
val cost: String,
|
||||
val rate: Enum<Rate>,
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val ID: Int = 0,
|
||||
)
|
30
app/src/main/java/com/hyperling/expensetracker/ExpenseDao.kt
Normal file
30
app/src/main/java/com/hyperling/expensetracker/ExpenseDao.kt
Normal file
@ -0,0 +1,30 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Query
|
||||
import androidx.room.Upsert
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface ExpenseDao {
|
||||
|
||||
// 2024-12-28
|
||||
// If these complain about return type errors, try upgrading
|
||||
// the ROOM version, otherwise remove the SUSPEND keyword.
|
||||
@Upsert
|
||||
suspend fun upsertExpense(expense: Expense)
|
||||
|
||||
@Delete
|
||||
suspend fun deleteExpense(expense: Expense)
|
||||
|
||||
@Query("SELECT * FROM expense ORDER BY name ASC")
|
||||
fun selectExpensesName(): Flow<List<Expense>>
|
||||
|
||||
@Query("SELECT * FROM expense ORDER BY rate ASC")
|
||||
fun selectExpensesRate(): Flow<List<Expense>>
|
||||
|
||||
@Query("SELECT * FROM expense ORDER BY cost ASC")
|
||||
fun selectExpensesCost(): Flow<List<Expense>>
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
|
||||
@Database (
|
||||
entities = [Expense::class],
|
||||
version = 1
|
||||
)
|
||||
abstract class ExpenseDatabase: RoomDatabase() {
|
||||
abstract val dao: ExpenseDao
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.style.TextGeometricTransform
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun ExpenseDialogAdd(
|
||||
state: ExpenseState,
|
||||
onEvent: (ExpenseEvent) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = {
|
||||
onEvent(ExpenseEvent.HideDialog)
|
||||
},
|
||||
title = { Text(text = "Add Expense") },
|
||||
text = {
|
||||
Column (
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
){
|
||||
TextField(
|
||||
value = state.name,
|
||||
onValueChange = {
|
||||
onEvent(ExpenseEvent.SetName(it))
|
||||
},
|
||||
placeholder = {
|
||||
Text(text = "Name")
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.Words)
|
||||
)
|
||||
// TBD: Had to make this a String, can we turn it back to a Double somehow?
|
||||
TextField(
|
||||
value = state.cost,
|
||||
onValueChange = {
|
||||
onEvent(ExpenseEvent.SetCost(it))
|
||||
},
|
||||
placeholder = {
|
||||
Text(text = "Cost")
|
||||
},
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal)
|
||||
)
|
||||
/* Unsure what to do here yet, a simple Picker type does not seems to exist?
|
||||
TextField(
|
||||
value = state.rate,
|
||||
onValueChange = {
|
||||
onEvent(ExpenseEvent.SetRate(it))
|
||||
},
|
||||
placeholder = {
|
||||
Text(text = "Rate")
|
||||
},
|
||||
)
|
||||
*/
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Button(onClick = {
|
||||
onEvent(ExpenseEvent.SaveExpense)
|
||||
}) {
|
||||
Text(text = "Save")
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
sealed interface ExpenseEvent {
|
||||
object SaveExpense: ExpenseEvent
|
||||
data class SetName(val name: String): ExpenseEvent
|
||||
data class SetCost(val cost: String): ExpenseEvent
|
||||
data class SetRate(val rate: Rate): ExpenseEvent
|
||||
object ShowDialog: ExpenseEvent
|
||||
object HideDialog: ExpenseEvent
|
||||
data class SortExpenses(val sortType: SortType): ExpenseEvent
|
||||
data class DeleteExpense(val expense: Expense): ExpenseEvent
|
||||
}
|
126
app/src/main/java/com/hyperling/expensetracker/ExpenseScreen.kt
Normal file
126
app/src/main/java/com/hyperling/expensetracker/ExpenseScreen.kt
Normal file
@ -0,0 +1,126 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.RadioButton
|
||||
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.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
fun getEnumStringResourceID(enumName: String): Int {
|
||||
return (
|
||||
when (enumName) {
|
||||
SortType.NAME.toString() -> R.string.NAME
|
||||
SortType.COST.toString() -> R.string.COST
|
||||
SortType.RATE.toString() -> R.string.RATE
|
||||
else -> 0
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ExpenseScreen(
|
||||
state: ExpenseState,
|
||||
onEvent: (ExpenseEvent) -> Unit
|
||||
){
|
||||
Scaffold (
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(onClick = {
|
||||
onEvent(ExpenseEvent.ShowDialog)
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = "Add Expense",
|
||||
)
|
||||
}
|
||||
}
|
||||
){ padding ->
|
||||
if(state.isAddingExpense){
|
||||
ExpenseDialogAdd(state, onEvent)
|
||||
}
|
||||
|
||||
LazyColumn (
|
||||
contentPadding = padding,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
){
|
||||
item {
|
||||
Row (){
|
||||
Text(text = "Sort ascending by:")
|
||||
}
|
||||
Row (
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.horizontalScroll(rememberScrollState()),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
){
|
||||
SortType.values().forEach { sortType ->
|
||||
Row (
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
onEvent(ExpenseEvent.SortExpenses(sortType))
|
||||
},
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
){
|
||||
RadioButton(
|
||||
selected = state.sortType == sortType,
|
||||
onClick = {
|
||||
onEvent(ExpenseEvent.SortExpenses(sortType))
|
||||
}
|
||||
)
|
||||
Text(text = stringResource(getEnumStringResourceID(sortType.name)))
|
||||
}
|
||||
}
|
||||
}
|
||||
Row (){
|
||||
Text(text = "Expenses:")
|
||||
}
|
||||
}
|
||||
items(state.expenses) {expense ->
|
||||
Row (
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Column (
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text (
|
||||
text = expense.name,
|
||||
fontSize = 20.sp
|
||||
)
|
||||
Text(
|
||||
text = "${expense.cost} per ${expense.rate}",
|
||||
fontSize = 12.sp
|
||||
)
|
||||
}
|
||||
IconButton(onClick = {
|
||||
onEvent(ExpenseEvent.DeleteExpense(expense))
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = "Delete Expense"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
data class ExpenseState(
|
||||
val expenses: List<Expense> = emptyList(),
|
||||
val name: String = "",
|
||||
val cost: String = "",
|
||||
val rate: Rate = Rate.MONTHLY,
|
||||
val isAddingExpense: Boolean = false,
|
||||
val sortType: SortType = SortType.NAME,
|
||||
)
|
@ -0,0 +1,98 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ExpenseViewModel (
|
||||
private val dao: ExpenseDao,
|
||||
): ViewModel() {
|
||||
|
||||
private val _sortType = MutableStateFlow(SortType.NAME)
|
||||
private val _state = MutableStateFlow(ExpenseState())
|
||||
|
||||
private val _expenses = _sortType
|
||||
.flatMapLatest { sortType ->
|
||||
when(sortType) {
|
||||
SortType.NAME -> dao.selectExpensesName()
|
||||
SortType.COST -> dao.selectExpensesCost()
|
||||
SortType.RATE -> dao.selectExpensesRate()
|
||||
}
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())
|
||||
|
||||
val state = combine(_state, _sortType, _expenses) { state, sortType, expenses ->
|
||||
state.copy(
|
||||
expenses = expenses,
|
||||
sortType = sortType
|
||||
)
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), ExpenseState())
|
||||
|
||||
fun onEvent(event: ExpenseEvent) {
|
||||
when(event) {
|
||||
is ExpenseEvent.DeleteExpense -> {
|
||||
viewModelScope.launch {
|
||||
dao.deleteExpense(event.expense)
|
||||
}
|
||||
}
|
||||
ExpenseEvent.HideDialog -> {
|
||||
_state.update { it.copy(
|
||||
isAddingExpense = false
|
||||
) }
|
||||
}
|
||||
ExpenseEvent.SaveExpense -> {
|
||||
val name = state.value.name
|
||||
val cost = state.value.cost
|
||||
val rate = state.value.rate
|
||||
|
||||
if (name.isBlank()
|
||||
|| cost.isBlank()
|
||||
//|| rate.isBlank() # TBD, enable this once Rate is working.
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
val expense = Expense(name, cost, rate)
|
||||
|
||||
viewModelScope.launch {
|
||||
dao.upsertExpense(expense)
|
||||
}
|
||||
|
||||
_state.update { it.copy(
|
||||
isAddingExpense = false,
|
||||
name = "",
|
||||
cost = "",
|
||||
rate = Rate.MONTHLY,
|
||||
) }
|
||||
}
|
||||
is ExpenseEvent.SetName -> {
|
||||
_state.update { it.copy(
|
||||
name = event.name
|
||||
) }
|
||||
}
|
||||
is ExpenseEvent.SetCost -> {
|
||||
_state.update { it.copy(
|
||||
cost = event.cost
|
||||
) }
|
||||
}
|
||||
is ExpenseEvent.SetRate -> {
|
||||
_state.update { it.copy(
|
||||
rate = event.rate
|
||||
) }
|
||||
}
|
||||
ExpenseEvent.ShowDialog -> {
|
||||
_state.update { it.copy(
|
||||
isAddingExpense = true
|
||||
) }
|
||||
}
|
||||
is ExpenseEvent.SortExpenses -> {
|
||||
_sortType.value = event.sortType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
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.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import com.hyperling.expensetracker.ui.theme.ExpenseTrackerTheme
|
||||
|
||||
class MainActivityOLD : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
ExpenseTrackerTheme {
|
||||
Surface {
|
||||
Main()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This seems like a great tutorial for what I need to do!
|
||||
// https://www.answertopia.com/jetpack-compose/a-jetpack-compose-room-database-and-repository-tutorial/
|
||||
|
||||
@Composable
|
||||
fun Main() {
|
||||
val context = LocalContext.current
|
||||
|
||||
var sumDaily by remember { mutableStateOf(0.0) }
|
||||
var sumWeekly by remember { mutableStateOf(0.0) }
|
||||
var sumMonthly by remember { mutableStateOf(0.0) }
|
||||
var sumYearly by remember { mutableStateOf(0.0) }
|
||||
|
||||
val sums = mapOf(
|
||||
"Daily" to sumDaily,
|
||||
"Weekly" to sumWeekly,
|
||||
"Monthly" to sumMonthly,
|
||||
"Yearly" to sumYearly,
|
||||
)
|
||||
|
||||
Column (
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = "Current Expense Summary"
|
||||
)
|
||||
|
||||
//for ((name, value) in sums) {
|
||||
sums.forEach { (name, value) ->
|
||||
Row (
|
||||
horizontalArrangement = Arrangement.End,
|
||||
){
|
||||
Text(text = name + ": ")
|
||||
Text(text = value.toString())
|
||||
}
|
||||
}
|
||||
|
||||
// FORTESTING
|
||||
Text (text = String.format("%.2f",sumDaily))
|
||||
Text (text = String.format("%.2f",sumWeekly))
|
||||
Text (text = String.format("%.2f",sumMonthly))
|
||||
Text (text = String.format("%.2f",sumYearly))
|
||||
|
||||
Row (
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Button(onClick = {
|
||||
val intent = Intent(context, NewExpenseActivity::class.java)
|
||||
context.startActivity(intent)
|
||||
}) {
|
||||
Text(text = "Create New Expense")
|
||||
}
|
||||
}
|
||||
|
||||
Text(text = "Expenses")
|
||||
// https://medium.com/@rowaido.game/implementing-the-room-library-with-jetpack-compose-590d13101fa7
|
||||
/* TODO:
|
||||
ForEach over all DB records, show it, and add its value to the summary.
|
||||
*/
|
||||
val expenseArray = listOf(
|
||||
Expense("Test", 180.0, 'Y', "My notes."),
|
||||
Expense("Test2", 20.0, 'M', "My notes, 2!"),
|
||||
)
|
||||
expenseArray.forEach { expense ->
|
||||
when (expense.freq) {
|
||||
'D' -> sumYearly += (expense.cost * 365.25)
|
||||
'W' -> sumYearly += (expense.cost * (365.25 / 7))
|
||||
'M' -> sumYearly += (expense.cost * 12)
|
||||
'Y' -> sumYearly += (expense.cost)
|
||||
}
|
||||
|
||||
sumDaily = sumYearly / 365.25
|
||||
sumWeekly = sumYearly / (365.25 / 7)
|
||||
sumMonthly = sumYearly / 12
|
||||
}
|
||||
|
||||
// FORTESTING
|
||||
Text (text = String.format("$ %.2f",sumDaily))
|
||||
Text (text = String.format("$ %.2f",sumWeekly))
|
||||
Text (text = String.format("$ %.2f",sumMonthly))
|
||||
Text (text = String.format("$ %.2f",sumYearly))
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun MainPreview() {
|
||||
ExpenseTrackerTheme {
|
||||
Main()
|
||||
}
|
||||
}
|
@ -1,136 +1,61 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.hyperling.expensetracker.ui.theme.ExpenseTrackerTheme
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
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.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import com.hyperling.expensetracker.ui.theme.ExpenseTrackerTheme
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.room.Room
|
||||
import androidx.compose.material3.Surface
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
private val _db by lazy {
|
||||
Room.databaseBuilder(
|
||||
applicationContext,
|
||||
ExpenseDatabase::class.java,
|
||||
"expenses.db"
|
||||
).build()
|
||||
}
|
||||
private val _viewModel by viewModels<ExpenseViewModel> (
|
||||
factoryProducer = {
|
||||
object : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ExpenseViewModel(_db.dao) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
ExpenseTrackerTheme {
|
||||
Surface {
|
||||
Main()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This seems like a great tutorial for what I need to do!
|
||||
// https://www.answertopia.com/jetpack-compose/a-jetpack-compose-room-database-and-repository-tutorial/
|
||||
|
||||
@Composable
|
||||
fun Main() {
|
||||
val context = LocalContext.current
|
||||
|
||||
var sumDaily by remember { mutableStateOf(0.0) }
|
||||
var sumWeekly by remember { mutableStateOf(0.0) }
|
||||
var sumMonthly by remember { mutableStateOf(0.0) }
|
||||
var sumYearly by remember { mutableStateOf(0.0) }
|
||||
|
||||
val sums = mapOf(
|
||||
"Daily" to sumDaily,
|
||||
"Weekly" to sumWeekly,
|
||||
"Monthly" to sumMonthly,
|
||||
"Yearly" to sumYearly,
|
||||
val state by _viewModel.state.collectAsState()
|
||||
ExpenseScreen(
|
||||
state = state,
|
||||
onEvent = _viewModel::onEvent
|
||||
)
|
||||
|
||||
Column (
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = "Current Expense Summary"
|
||||
)
|
||||
|
||||
//for ((name, value) in sums) {
|
||||
sums.forEach { (name, value) ->
|
||||
Row (
|
||||
horizontalArrangement = Arrangement.End,
|
||||
){
|
||||
Text(text = name + ": ")
|
||||
Text(text = value.toString())
|
||||
}
|
||||
}
|
||||
|
||||
// FORTESTING
|
||||
Text (text = String.format("%.2f",sumDaily))
|
||||
Text (text = String.format("%.2f",sumWeekly))
|
||||
Text (text = String.format("%.2f",sumMonthly))
|
||||
Text (text = String.format("%.2f",sumYearly))
|
||||
|
||||
Row (
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Button(onClick = {
|
||||
val intent = Intent(context, NewExpenseActivity::class.java)
|
||||
context.startActivity(intent)
|
||||
}) {
|
||||
Text(text = "Create New Expense")
|
||||
}
|
||||
}
|
||||
|
||||
Text(text = "Expenses")
|
||||
// https://medium.com/@rowaido.game/implementing-the-room-library-with-jetpack-compose-590d13101fa7
|
||||
/* TODO:
|
||||
ForEach over all DB records, show it, and add its value to the summary.
|
||||
*/
|
||||
val expenseArray = listOf(
|
||||
Expense("Test", 180.0, 'Y', "My notes."),
|
||||
Expense("Test2", 20.0, 'M', "My notes, 2!"),
|
||||
)
|
||||
expenseArray.forEach { expense ->
|
||||
when (expense.freq) {
|
||||
'D' -> sumYearly += (expense.cost * 365.25)
|
||||
'W' -> sumYearly += (expense.cost * (365.25 / 7))
|
||||
'M' -> sumYearly += (expense.cost * 12)
|
||||
'Y' -> sumYearly += (expense.cost)
|
||||
}
|
||||
|
||||
sumDaily = sumYearly / 365.25
|
||||
sumWeekly = sumYearly / (365.25 / 7)
|
||||
sumMonthly = sumYearly / 12
|
||||
}
|
||||
|
||||
// FORTESTING
|
||||
Text (text = String.format("$ %.2f",sumDaily))
|
||||
Text (text = String.format("$ %.2f",sumWeekly))
|
||||
Text (text = String.format("$ %.2f",sumMonthly))
|
||||
Text (text = String.format("$ %.2f",sumYearly))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun MainPreview() {
|
||||
@ -138,3 +63,4 @@ fun MainPreview() {
|
||||
Main()
|
||||
}
|
||||
}
|
||||
*/
|
8
app/src/main/java/com/hyperling/expensetracker/Rate.kt
Normal file
8
app/src/main/java/com/hyperling/expensetracker/Rate.kt
Normal file
@ -0,0 +1,8 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
enum class Rate {
|
||||
DAILY,
|
||||
WEEKLY,
|
||||
MONTHLY,
|
||||
YEARLY,
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.hyperling.expensetracker
|
||||
|
||||
enum class SortType {
|
||||
NAME,
|
||||
COST,
|
||||
RATE,
|
||||
}
|
@ -1,3 +1,6 @@
|
||||
<resources>
|
||||
<string name="app_name">Recurring Expenses</string>
|
||||
<string name="NAME">Name</string>
|
||||
<string name="COST">Cost</string>
|
||||
<string name="RATE">Rate</string>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user