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:
parent
e3a2625f68
commit
7b1c0db0cc
@ -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,140 +1,66 @@
|
|||||||
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() {
|
||||||
ExpenseTrackerTheme {
|
ExpenseTrackerTheme {
|
||||||
Main()
|
Main()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
Loading…
x
Reference in New Issue
Block a user