Add view model and modify MainActivity. Should be everything needed to start testing near-basic functionality and begin tweaking as needed!

This commit is contained in:
Hyperling 2025-01-09 15:00:35 -07:00
parent e3a2625f68
commit 7b1c0db0cc
3 changed files with 271 additions and 107 deletions

View File

@ -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
}
}
}
}

View File

@ -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()
}
}

View File

@ -1,136 +1,61 @@
package com.hyperling.expensetracker package com.hyperling.expensetracker
import android.content.Context import com.hyperling.expensetracker.ui.theme.ExpenseTrackerTheme
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Arrangement import androidx.activity.viewModels
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding 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.Scaffold
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue 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.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModel
import androidx.core.content.ContextCompat.startActivity import androidx.lifecycle.ViewModelProvider
import com.hyperling.expensetracker.ui.theme.ExpenseTrackerTheme import androidx.room.Room
import androidx.compose.material3.Surface
class MainActivity : ComponentActivity() { 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?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
setContent { setContent {
ExpenseTrackerTheme { ExpenseTrackerTheme {
Surface { val state by _viewModel.state.collectAsState()
Main() ExpenseScreen(
} state = state,
onEvent = _viewModel::onEvent
)
} }
} }
} }
} }
// 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) @Preview(showBackground = true)
@Composable @Composable
fun MainPreview() { fun MainPreview() {
@ -138,3 +63,4 @@ fun MainPreview() {
Main() Main()
} }
} }
*/