Compare commits
No commits in common. "7b1c0db0cc9d1b8ca7605e36ca4a1033dc2de2d0" and "49eb36514df048cefe1af85c618fb9e1bd3a8a35" have entirely different histories.
7b1c0db0cc
...
49eb36514d
@ -1,13 +1,23 @@
|
|||||||
package com.hyperling.expensetracker
|
package com.hyperling.expensetracker
|
||||||
|
|
||||||
import androidx.room.Entity
|
/*class Expense {
|
||||||
import androidx.room.PrimaryKey
|
var name: String = ""
|
||||||
|
var cost: Double = 0.0
|
||||||
|
var freq: Char = '*'
|
||||||
|
var note: String = ""
|
||||||
|
|
||||||
@Entity
|
/*public Expense (val name: String, val cost: Double, val freq: Char, val note: String) {
|
||||||
data class Expense (
|
this.name = name
|
||||||
val name: String,
|
this.name = name
|
||||||
val cost: String,
|
this.name = name
|
||||||
val rate: Enum<Rate>,
|
this.name = name
|
||||||
@PrimaryKey(autoGenerate = true)
|
}*/
|
||||||
val ID: Int = 0,
|
}*/
|
||||||
)
|
|
||||||
|
class Expense (
|
||||||
|
var name: String = "",
|
||||||
|
var cost: Double = 0.0,
|
||||||
|
var freq: Char = '*',
|
||||||
|
var note: String = "",
|
||||||
|
) {
|
||||||
|
}
|
@ -1,30 +0,0 @@
|
|||||||
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>>
|
|
||||||
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -1,126 +0,0 @@
|
|||||||
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"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
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,
|
|
||||||
)
|
|
@ -1,98 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,140 +0,0 @@
|
|||||||
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,66 +1,140 @@
|
|||||||
package com.hyperling.expensetracker
|
package com.hyperling.expensetracker
|
||||||
|
|
||||||
import com.hyperling.expensetracker.ui.theme.ExpenseTrackerTheme
|
import android.content.Context
|
||||||
|
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.activity.viewModels
|
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.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.lifecycle.ViewModel
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.core.content.ContextCompat.startActivity
|
||||||
import androidx.room.Room
|
import com.hyperling.expensetracker.ui.theme.ExpenseTrackerTheme
|
||||||
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 {
|
||||||
val state by _viewModel.state.collectAsState()
|
Surface {
|
||||||
ExpenseScreen(
|
Main()
|
||||||
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
@ -1,8 +0,0 @@
|
|||||||
package com.hyperling.expensetracker
|
|
||||||
|
|
||||||
enum class Rate {
|
|
||||||
DAILY,
|
|
||||||
WEEKLY,
|
|
||||||
MONTHLY,
|
|
||||||
YEARLY,
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
package com.hyperling.expensetracker
|
|
||||||
|
|
||||||
enum class SortType {
|
|
||||||
NAME,
|
|
||||||
COST,
|
|
||||||
RATE,
|
|
||||||
}
|
|
@ -1,6 +1,3 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Recurring Expenses</string>
|
<string name="app_name">Recurring Expenses</string>
|
||||||
<string name="NAME">Name</string>
|
|
||||||
<string name="COST">Cost</string>
|
|
||||||
<string name="RATE">Rate</string>
|
|
||||||
</resources>
|
</resources>
|
Loading…
x
Reference in New Issue
Block a user