Compare commits

...

22 Commits

Author SHA1 Message Date
e8d5794cf7 Refactor dialog name. 2025-01-09 16:14:35 -07:00
f2fe268b5b Final change necessary to get a successful build. Still needs to be tested on a device to see if it really runs. 2025-01-09 16:05:03 -07:00
6e05298f3d Change rate to a String since Java is mad about the enum. 2025-01-09 16:03:35 -07:00
8b8ef62cbf Change order of the functions. 2025-01-09 16:03:15 -07:00
a6b96a0bc0 Make it match the other project entirely. 2025-01-09 15:58:07 -07:00
39b82fec3b Had to update the file, `.jetbrains. was not found. 2025-01-09 15:57:31 -07:00
57c0ebce9e Lol, ok, NOW it wants the plugins section updated to be like the other project. We may be getting somewhere! 2025-01-09 15:57:04 -07:00
ee37ba6673 Android Studio did this. Thanks for starting to aid me! 2025-01-09 15:55:34 -07:00
65e685eef9 Replace the entire file to see if it fixes new errors about Kotlin 2.0 upgrade needing "the Compose Compiler Gradle plugin". 2025-01-09 15:55:15 -07:00
cdc505b478 Update versions to match the Example project. Unsure why Android Studio would have put incompatible versions in there. 2025-01-09 15:50:48 -07:00
c4aa13b608 Copied over more from the Example project. Still not having luck. 2025-01-09 15:43:35 -07:00
0717eb0639 Try fixing internal "java.lang.IllegalAccessError" errors when building. Says it "cannot access class com.sun.tools.javac.main.JavaCompiler (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.main". Why did they make Android development so dumb? Lol. I miss the good'ol Java/XML days. 2025-01-09 15:40:09 -07:00
343a963e3f Update version name since the project has been re-done, but is still going to be the 1st version upon release. 2025-01-09 15:31:59 -07:00
0e7de44a47 Change TBD's to also be TODO's. 2025-01-09 15:04:21 -07:00
7b1c0db0cc Add view model and modify MainActivity. Should be everything needed to start testing near-basic functionality and begin tweaking as needed! 2025-01-09 15:00:35 -07:00
e3a2625f68 Add screen code, convert cost to a string, ignoring how to add rates on the screen for now. 2025-01-09 14:47:39 -07:00
a4513394d1 Add state as well as fix SortType needs. 2025-01-09 14:19:03 -07:00
d5d525f65a Remove extra newline. 2025-01-09 14:18:44 -07:00
bf61d878cc Create event file. 2025-01-09 14:15:15 -07:00
6c95d33526 Create database file. 2025-01-09 14:10:45 -07:00
d8ecd7b621 Create DAO file. 2025-01-09 14:10:38 -07:00
845715122f Finalize the Expense class and add an Enum for the rate. 2025-01-09 14:02:26 -07:00
17 changed files with 594 additions and 175 deletions

2
.idea/kotlinc.xml generated
View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.0" />
<option name="version" value="2.0.0" />
</component>
</project>

View File

@ -1,9 +1,7 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.jetbrains.kotlin.android)
// https://medium.com/@rowaido.game/implementing-the-room-library-with-jetpack-compose-590d13101fa7
id("com.google.devtools.ksp")
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
// For Room (2024-12-17)
kotlin("kapt")
@ -11,19 +9,16 @@ plugins {
android {
namespace = "com.hyperling.expensetracker"
compileSdk = 34
compileSdk = 35
defaultConfig {
applicationId = "com.hyperling.expensetracker"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "0.0.1"
versionName = "0.0.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}
buildTypes {
@ -36,26 +31,15 @@ android {
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "1.8"
jvmTarget = "11"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
// https://medium.com/@rowaido.game/implementing-the-room-library-with-jetpack-compose-590d13101fa7
//kotlinCompilerExtensionVersion = "1.5.11"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}
dependencies {
@ -76,19 +60,10 @@ dependencies {
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
// https://medium.com/@rowaido.game/implementing-the-room-library-with-jetpack-compose-590d13101fa7
/* This is crazy? How are people supposed to know how to do this, and especially "fix" it when it doesn't work?
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
ksp(libs.androidx.room.compiler)
implementation(libs.androidx.lifecycle.viewmodel.compose)
*/
// For Room (2024-12-17)
// For Room
// https://www.youtube.com/watch?v=bOd3wO0uFr8&themeRefresh=1
/* build.gradle version * /
def room_version: String = "2.6.1"
def room_version: String = "2.5.0"
implementation"androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"
// */
@ -97,4 +72,4 @@ dependencies {
implementation("androidx.room:room-ktx:$room_version")
kapt("androidx.room:room-compiler:$room_version")
// */
}
}

View File

@ -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: String, //Enum<Rate>,
@PrimaryKey(autoGenerate = true)
val ID: Int = 0,
)

View 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 cost ASC")
fun selectExpensesCost(): Flow<List<Expense>>
@Query("SELECT * FROM expense ORDER BY rate ASC")
fun selectExpensesRate(): Flow<List<Expense>>
}

View File

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

View File

@ -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: String): ExpenseEvent // TBD / TODO: Make this the enum.
object ShowDialog: ExpenseEvent
object HideDialog: ExpenseEvent
data class SortExpenses(val sortType: SortType): ExpenseEvent
data class DeleteExpense(val expense: Expense): ExpenseEvent
}

View 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){
ExpenseScreenDialogAdd(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"
)
}
}
}
}
}
}

View File

@ -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.unit.dp
@Composable
fun ExpenseScreenDialogAdd(
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 / TODO: 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)
)
// TBD / TODO: Unsure what to do here yet so that an Enum is possible.
// A simple Picker type does not seems to exist? Why? Whyyy???
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")
}
}
}
)
}

View File

@ -0,0 +1,10 @@
package com.hyperling.expensetracker
data class ExpenseState(
val expenses: List<Expense> = emptyList(),
val name: String = "",
val cost: String = "",
val rate: String = "", // TBD / TODO: Rate = Rate.MONTHLY,
val isAddingExpense: Boolean = false,
val sortType: SortType = SortType.NAME,
)

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 / TODO: 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 = "", // TBD / TODO: 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,144 @@
package com.hyperling.expensetracker
/* TBD / TODO: This file exists temporarily for viewing what the old version was doing.
// TBD / TODO: Remove this file.
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,140 +1,66 @@
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()
}
val state by _viewModel.state.collectAsState()
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)
@Composable
fun MainPreview() {
ExpenseTrackerTheme {
Main()
}
}
}
*/

View File

@ -0,0 +1,8 @@
package com.hyperling.expensetracker
enum class Rate {
DAILY,
WEEKLY,
MONTHLY,
YEARLY,
}

View File

@ -0,0 +1,7 @@
package com.hyperling.expensetracker
enum class SortType {
NAME,
COST,
RATE,
}

View File

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

View File

@ -1,8 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.jetbrains.kotlin.android) apply false
// https://medium.com/@rowaido.game/implementing-the-room-library-with-jetpack-compose-590d13101fa7
id("com.google.devtools.ksp") version "1.9.23-1.0.19" apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.compose) apply false
}

View File

@ -1,16 +1,13 @@
[versions]
agp = "8.7.3"
kotlin = "1.9.0"
coreKtx = "1.10.1"
kotlin = "2.0.0"
coreKtx = "1.15.0"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.6.1"
activityCompose = "1.8.0"
composeBom = "2024.04.01"
roomCompiler = "2.6.1"
roomKtx = "2.6.1"
roomRuntime = "2.6.1"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@ -30,5 +27,6 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3"
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }