diff --git a/app/build.gradle b/app/build.gradle index f4725b6..973e7fd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -93,4 +93,6 @@ dependencies { // Coroutine implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0' + + implementation "org.jetbrains.kotlin:kotlin-reflect:2.0.20" } \ No newline at end of file diff --git a/app/src/main/java/com/money/expenz/BaseActivity.kt b/app/src/main/java/com/money/expenz/BaseActivity.kt index b3ddef3..c8e4c18 100644 --- a/app/src/main/java/com/money/expenz/BaseActivity.kt +++ b/app/src/main/java/com/money/expenz/BaseActivity.kt @@ -23,7 +23,6 @@ open class BaseActivity : ComponentActivity() { setContent { ExpenzTheme { - val owner = LocalViewModelStoreOwner.current owner?.let { diff --git a/app/src/main/java/com/money/expenz/data/DataSource.kt b/app/src/main/java/com/money/expenz/data/DataSource.kt index 18fca49..41b5ac8 100644 --- a/app/src/main/java/com/money/expenz/data/DataSource.kt +++ b/app/src/main/java/com/money/expenz/data/DataSource.kt @@ -3,7 +3,6 @@ package com.money.expenz.data import com.money.expenz.R class DataSource { - fun loadSubscriptions(): List { return listOf( Subscription(R.string.netflix, R.drawable.ic_category_media), diff --git a/app/src/main/java/com/money/expenz/data/IEDetails.kt b/app/src/main/java/com/money/expenz/data/IEDetails.kt index ea2bdae..55c87a3 100644 --- a/app/src/main/java/com/money/expenz/data/IEDetails.kt +++ b/app/src/main/java/com/money/expenz/data/IEDetails.kt @@ -3,6 +3,7 @@ package com.money.expenz.data import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey +import kotlin.reflect.full.memberProperties @Entity(tableName = "IEDetails") data class IEDetails( @@ -22,3 +23,20 @@ data class IEDetails( @ColumnInfo(name = "userId") var userId: Int = 0 ) + +data class IEDetailsDTO( + val ie: String = "", + val category: String = "", + val amount: Int = 0, + val date: String = "", + val notes: String = "" +) + +fun IEDetails.toIEDetailsDTO(): IEDetailsDTO { + return IEDetailsDTO(ie, category, amount, date, notes) +} + +inline fun T.asMap(): Map { + val props = T::class.memberProperties.associateBy { it.name } + return props.keys.associateWith { props[it]?.get(this) } +} diff --git a/app/src/main/java/com/money/expenz/data/Subscription.kt b/app/src/main/java/com/money/expenz/data/Subscription.kt index b72f7e2..9e28f43 100644 --- a/app/src/main/java/com/money/expenz/data/Subscription.kt +++ b/app/src/main/java/com/money/expenz/data/Subscription.kt @@ -4,5 +4,5 @@ import androidx.annotation.StringRes data class Subscription( @StringRes val stringResourceId: Int, - @DrawableRes val imageResourceId: Int + @DrawableRes val imageResourceId: Int, ) diff --git a/app/src/main/java/com/money/expenz/data/User.kt b/app/src/main/java/com/money/expenz/data/User.kt index db0a1d4..bf9e9b5 100644 --- a/app/src/main/java/com/money/expenz/data/User.kt +++ b/app/src/main/java/com/money/expenz/data/User.kt @@ -20,5 +20,5 @@ data class User( @ColumnInfo(name = "TotalIncome") var totalIncome: Long = 0, @ColumnInfo(name = "TotalExpense") - var totalExpense: Long = 0 + var totalExpense: Long = 0, ) diff --git a/app/src/main/java/com/money/expenz/data/UserWithIEDetails.kt b/app/src/main/java/com/money/expenz/data/UserWithIEDetails.kt index 720abec..26bd73d 100644 --- a/app/src/main/java/com/money/expenz/data/UserWithIEDetails.kt +++ b/app/src/main/java/com/money/expenz/data/UserWithIEDetails.kt @@ -7,7 +7,7 @@ data class UserWithIEDetails( @Embedded val user: User, @Relation( parentColumn = "Id", - entityColumn = "userId" + entityColumn = "userId", ) - val ieDetails: List + val ieDetails: List, ) diff --git a/app/src/main/java/com/money/expenz/database/UserDatabase.kt b/app/src/main/java/com/money/expenz/database/UserDatabase.kt index 69a17a7..0b74551 100644 --- a/app/src/main/java/com/money/expenz/database/UserDatabase.kt +++ b/app/src/main/java/com/money/expenz/database/UserDatabase.kt @@ -4,53 +4,34 @@ import android.content.Context import androidx.room.Database import androidx.room.Room import androidx.room.RoomDatabase -import androidx.sqlite.db.SupportSQLiteDatabase import com.money.expenz.data.IEDetails import com.money.expenz.data.User import com.money.expenz.model.UserDAO import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch -@Database(entities = [User::class, IEDetails::class], version = 2, exportSchema = true) +@Database(entities = [User::class, IEDetails::class], version = 1, exportSchema = true) abstract class UserDatabase : RoomDatabase() { - abstract fun userDAO(): UserDAO - private class UserDatabaseCallBack(private val coroutineScope: CoroutineScope) : Callback() { - override fun onCreate(db: SupportSQLiteDatabase) { - super.onCreate(db) - INSTANCE?.let { database -> - coroutineScope.launch { - populateDatabase(database.userDAO()) - } - } - } - - suspend fun populateDatabase(userDao: UserDAO) { - - val user = User(userName = "Sam", password = "abc@38", email = "vasu@gmail.com", country = "china") - userDao.insertUser(user) - - val ieDetails = IEDetails(ie = "Income", category = "other", amount = 0, userId = 1) - userDao.insertIEDetails(ieDetails) - } - } - companion object { // Singleton prevents multiple instances of database opening at the // same time. @Volatile private var INSTANCE: UserDatabase? = null - fun getInstance(context: Context, coroutineScope: CoroutineScope): UserDatabase { + fun getInstance( + context: Context, + coroutineScope: CoroutineScope, + ): UserDatabase { // if the INSTANCE is not null, then return it, // if it is, then create the database return INSTANCE ?: synchronized(this) { - val instance = Room.databaseBuilder( - context.applicationContext, - UserDatabase::class.java, - "user_database" - ).addCallback(UserDatabaseCallBack(coroutineScope)).build() + val instance = + Room.databaseBuilder( + context.applicationContext, + UserDatabase::class.java, + "user_database", + ).build() INSTANCE = instance instance } diff --git a/app/src/main/java/com/money/expenz/model/ExpenzAppBar.kt b/app/src/main/java/com/money/expenz/model/ExpenzAppBar.kt index d39cbdc..f38d81b 100644 --- a/app/src/main/java/com/money/expenz/model/ExpenzAppBar.kt +++ b/app/src/main/java/com/money/expenz/model/ExpenzAppBar.kt @@ -30,7 +30,6 @@ import com.money.expenz.ui.Screen import com.money.expenz.ui.home.ExpenzViewModel class ExpenzAppBar { - object ExpenzTheme { val colorScheme: ColorScheme @Composable @@ -45,7 +44,7 @@ class ExpenzAppBar { @Composable fun AppBar( viewModel: ExpenzViewModel, - navController: NavHostController = rememberNavController() + navController: NavHostController = rememberNavController(), ) { // Get current back stack entry val backStackEntry by navController.currentBackStackEntryAsState() @@ -54,17 +53,19 @@ class ExpenzAppBar { topBar = { TopAppBar( currentScreen = Screen.valueOf(currentRoute ?: Screen.Home.route), - canNavigateBack = navController.previousBackStackEntry != null && !currentRoute.equals( - Screen.Home.route - ), - navigateUp = { navController.navigateUp() } + canNavigateBack = + navController.previousBackStackEntry != null && + !currentRoute.equals( + Screen.Home.route, + ), + navigateUp = { navController.navigateUp() }, ) }, bottomBar = { BottomNavigationBar(navController = navController) }, backgroundColor = ExpenzTheme.colorScheme.background, - contentColor = ExpenzTheme.colorScheme.onBackground + contentColor = ExpenzTheme.colorScheme.onBackground, ) { innerPadding -> BaseContent(viewModel, innerPadding, navController) } @@ -75,7 +76,7 @@ class ExpenzAppBar { currentScreen: Screen, canNavigateBack: Boolean, navigateUp: () -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, ) { val localContext = LocalContext.current as Activity TopAppBar( @@ -88,7 +89,7 @@ class ExpenzAppBar { description = "Logout", onClick = { localContext.finishAffinity() - } + }, ) }, modifier = modifier, @@ -97,10 +98,10 @@ class ExpenzAppBar { AppBarActionButton( imageVector = Icons.AutoMirrored.Filled.ArrowBack, description = "Back", - onClick = navigateUp + onClick = navigateUp, ) } - } + }, ) } @@ -108,7 +109,7 @@ class ExpenzAppBar { fun AppBarActionButton( imageVector: ImageVector, description: String, - onClick: () -> Unit + onClick: () -> Unit, ) { IconButton(onClick = { onClick() @@ -116,20 +117,19 @@ class ExpenzAppBar { Icon( imageVector = imageVector, contentDescription = description, - tint = ExpenzTheme.colorScheme.onPrimary + tint = ExpenzTheme.colorScheme.onPrimary, ) } } @Composable - fun BottomNavigationBar( - navController: NavController - ) { - val items = listOf( - BottomNavItem.Home, - BottomNavItem.Add, - BottomNavItem.Subscriptions - ) + fun BottomNavigationBar(navController: NavController) { + val items = + listOf( + BottomNavItem.Home, + BottomNavItem.Add, + BottomNavItem.Subscriptions, + ) BottomNavigation( modifier = Modifier.fillMaxWidth(), @@ -144,7 +144,7 @@ class ExpenzAppBar { Icon( imageVector = item.icon, contentDescription = stringResource(id = item.titleResId), - tint = ExpenzTheme.colorScheme.onPrimary + tint = ExpenzTheme.colorScheme.onPrimary, ) }, label = { Text(text = stringResource(id = item.titleResId)) }, @@ -164,7 +164,7 @@ class ExpenzAppBar { // Restore state when re-selecting a previously selected item restoreState = true } - } + }, ) } } @@ -174,19 +174,20 @@ class ExpenzAppBar { fun BaseContent( viewModel: ExpenzViewModel, innerPaddingValues: PaddingValues, - navController: NavHostController + navController: NavHostController, ) { Column( Modifier.padding( - paddingValues = PaddingValues( + paddingValues = + PaddingValues( 10.dp, innerPaddingValues.calculateTopPadding(), 10.dp, - innerPaddingValues.calculateBottomPadding() - ) + innerPaddingValues.calculateBottomPadding(), + ), ), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.Center, ) { NavigationSetup(viewModel, navController = navController, Screen.Home.route) // HomeScreen(viewModel, navController) diff --git a/app/src/main/java/com/money/expenz/model/UserDAO.kt b/app/src/main/java/com/money/expenz/model/UserDAO.kt index 06ba61b..c9373ef 100644 --- a/app/src/main/java/com/money/expenz/model/UserDAO.kt +++ b/app/src/main/java/com/money/expenz/model/UserDAO.kt @@ -14,7 +14,6 @@ import kotlinx.coroutines.flow.Flow @Dao interface UserDAO { - @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertUser(user: User) @@ -22,7 +21,7 @@ interface UserDAO { suspend fun insertIEDetails(ieDetails: IEDetails) @Query("SELECT * FROM User") - fun getAllUsers(): Flow> + fun getAllUsers(): Flow>? @Query("SELECT * FROM User WHERE id LIKE :userId") suspend fun getLoggedInUserDetails(userId: Int): User @@ -31,6 +30,9 @@ interface UserDAO { @Query("SELECT * FROM User") fun getUserWithIEDetails(): Flow> + @Query("SELECT * FROM IEDetails WHERE category LIKE :category") + suspend fun searchIECategory(category: String): List + @Update suspend fun updateUserDetails(user: User) diff --git a/app/src/main/java/com/money/expenz/repository/UserRepository.kt b/app/src/main/java/com/money/expenz/repository/UserRepository.kt index 32e7e92..1884e2c 100644 --- a/app/src/main/java/com/money/expenz/repository/UserRepository.kt +++ b/app/src/main/java/com/money/expenz/repository/UserRepository.kt @@ -11,8 +11,10 @@ import kotlinx.coroutines.flow.Flow * Coroutine used to avoid database operations on main thread */ class UserRepository(private val userDAO: UserDAO) { - - val allUsersList: Flow> = userDAO.getAllUsers() + @WorkerThread + fun getUsers(): Flow>? { + return userDAO.getAllUsers() + } @WorkerThread fun getUserWithIEDetails(): Flow> { @@ -37,4 +39,9 @@ class UserRepository(private val userDAO: UserDAO) { suspend fun updateUserDetails(user: User) { userDAO.updateUserDetails(user) } + + @WorkerThread + suspend fun searchIECategory(category: String): List { + return userDAO.searchIECategory(category) + } } diff --git a/app/src/main/java/com/money/expenz/ui/AddScreen.kt b/app/src/main/java/com/money/expenz/ui/AddScreen.kt index b63115a..03a5839 100644 --- a/app/src/main/java/com/money/expenz/ui/AddScreen.kt +++ b/app/src/main/java/com/money/expenz/ui/AddScreen.kt @@ -28,38 +28,44 @@ import com.money.expenz.R import com.money.expenz.data.IEDetails import com.money.expenz.model.ExpenzAppBar.ExpenzTheme import com.money.expenz.ui.home.ExpenzViewModel -import com.money.expenz.utils.Constants.Companion.expense -import com.money.expenz.utils.Constants.Companion.income -import com.money.expenz.utils.Constants.Companion.subscription +import com.money.expenz.utils.ExpenzUtil.Companion.EXPENSE +import com.money.expenz.utils.ExpenzUtil.Companion.INCOME +import com.money.expenz.utils.ExpenzUtil.Companion.SUBSCRIPTION import java.util.* @Composable -fun AddScreen(navController: NavController, viewModel: ExpenzViewModel) { - var radioValue = remember { mutableStateOf("") } - var category = remember { mutableStateOf("") } - var amount = remember { mutableStateOf("") } - var date = remember { mutableStateOf("") } - var notes = remember { mutableStateOf("") } +fun AddScreen( + navController: NavController, + viewModel: ExpenzViewModel, +) { + val radioValue = remember { mutableStateOf("") } + val category = remember { mutableStateOf("") } + val amount = remember { mutableStateOf("") } + val date = remember { mutableStateOf("") } + val notes = remember { mutableStateOf("") } Box( - modifier = Modifier + modifier = + Modifier .fillMaxSize() .padding(10.dp), ) { Button( - modifier = Modifier + modifier = + Modifier .fillMaxWidth() .align(alignment = Alignment.BottomCenter), onClick = { - val ieDetails = viewModel.loggedInUserId.value?.let { - IEDetails( - ie = radioValue.value, - category = category.value, - amount = amount.value.toInt(), - date = date.value, - notes = notes.value, - userId = it - ) - } + val ieDetails = + viewModel.loggedInUserId.value?.let { + IEDetails( + ie = radioValue.value, + category = category.value, + amount = amount.value.toIntOrNull() ?: 0, + date = date.value, + notes = notes.value, + userId = it, + ) + } viewModel.insertIEDetails(ieDetails!!) viewModel.updateUserDetails(amount.value.toInt(), radioValue.value) navController.navigate(BottomNavItem.Home.route) { @@ -67,48 +73,52 @@ fun AddScreen(navController: NavController, viewModel: ExpenzViewModel) { } }, colors = ButtonDefaults.buttonColors(containerColor = ExpenzTheme.colorScheme.primaryContainer), - shape = CutCornerShape(10) + shape = CutCornerShape(10), ) { Text( text = stringResource(id = R.string.add), color = ExpenzTheme.colorScheme.onSurfaceVariant, - style = ExpenzTheme.typography.labelLarge + style = ExpenzTheme.typography.labelLarge, ) } } Column( - modifier = Modifier + modifier = + Modifier .fillMaxWidth() .fillMaxHeight() - .padding(10.dp) + .padding(10.dp), ) { // set Radio options - val radioOptions = listOf(income, expense, subscription) - val (selectedOption, onOptionSelected) = remember { - mutableStateOf(radioOptions[2]) - } + val radioOptions = listOf(INCOME, EXPENSE, SUBSCRIPTION) + val (selectedOption, onOptionSelected) = + remember { + mutableStateOf(radioOptions[2]) + } Column( - modifier = Modifier - .fillMaxWidth() + modifier = + Modifier + .fillMaxWidth(), ) { radioOptions.forEach { text -> Row( - modifier = Modifier + modifier = + Modifier .selectable( selected = (selectedOption == text), - onClick = { onOptionSelected(text) } + onClick = { onOptionSelected(text) }, ), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center + horizontalArrangement = Arrangement.Center, ) { RadioButton( selected = (text == selectedOption), - onClick = { onOptionSelected(text) } + onClick = { onOptionSelected(text) }, ) Text( text = text, modifier = Modifier.padding(start = 2.dp), - color = ExpenzTheme.colorScheme.onSurfaceVariant + color = ExpenzTheme.colorScheme.onSurfaceVariant, ) radioValue.value = selectedOption } @@ -119,40 +129,42 @@ fun AddScreen(navController: NavController, viewModel: ExpenzViewModel) { var isExpanded by remember { mutableStateOf(false) } - val categories = listOf( - "Media", - "Electricity", - "Travel", - "Food", - "Shopping", - "Gas", - "Internet", - "Medical", - "Pets", - "Others" - ) + val categories = + listOf( + "Media", + "Electricity", + "Travel", + "Food", + "Shopping", + "Gas", + "Internet", + "Medical", + "Pets", + "Others", + ) val icon = if (isExpanded) Icons.Filled.KeyboardArrowDown else Icons.Filled.KeyboardArrowUp Column( - modifier = Modifier + modifier = + Modifier .padding(5.dp), verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.Start + horizontalAlignment = Alignment.Start, ) { OutlinedTextField( value = category.value, onValueChange = { category.value = it }, trailingIcon = { Icon(icon, "", Modifier.clickable { isExpanded = !isExpanded }) }, - modifier = Modifier + modifier = + Modifier .fillMaxWidth(), - label = { Text(text = stringResource(id = R.string.category)) } + label = { Text(text = stringResource(id = R.string.category)) }, ) DropdownMenu( modifier = Modifier.padding(5.dp), expanded = isExpanded, - onDismissRequest = { isExpanded = false } - + onDismissRequest = { isExpanded = false }, ) { categories.forEach { categorySelected -> DropdownMenuItem( @@ -160,7 +172,7 @@ fun AddScreen(navController: NavController, viewModel: ExpenzViewModel) { onClick = { category.value = categorySelected isExpanded = false - } + }, ) } } @@ -171,18 +183,21 @@ fun AddScreen(navController: NavController, viewModel: ExpenzViewModel) { // Set Amount val textFieldValue = remember { mutableStateOf(TextFieldValue()) } TextField( - value = amount.value, onValueChange = { amount.value = it }, - modifier = Modifier + value = amount.value, + onValueChange = { amount.value = it }, + modifier = + Modifier .fillMaxWidth() .padding(5.dp), - keyboardOptions = KeyboardOptions( + keyboardOptions = + KeyboardOptions( keyboardType = KeyboardType.Number, - imeAction = ImeAction.Done + imeAction = ImeAction.Done, ), label = { Text(text = stringResource(id = R.string.amount)) }, - placeholder = { Text(text = stringResource(id = R.string.enter_amount)) } + placeholder = { Text(text = stringResource(id = R.string.enter_amount)) }, ) textFieldValue.value = TextFieldValue(amount.value) @@ -212,22 +227,24 @@ fun AddScreen(navController: NavController, viewModel: ExpenzViewModel) { // Declaring DatePickerDialog and setting // initial values as current values (present year, month and day) - val mDatePickerDialog = DatePickerDialog( - mContext, - { _: DatePicker, year: Int, month: Int, day: Int -> - mDate.value = "$day/${month + 1}/$year" - }, - mYear, - mMonth, - mDay - ) + val mDatePickerDialog = + DatePickerDialog( + mContext, + { _: DatePicker, year: Int, month: Int, day: Int -> + mDate.value = "$day/${month + 1}/$year" + }, + mYear, + mMonth, + mDay, + ) Column( - modifier = Modifier + modifier = + Modifier .fillMaxWidth() .padding(5.dp), verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.Start + horizontalAlignment = Alignment.Start, ) { val textState = remember { mutableStateOf(TextFieldValue()) } @@ -240,7 +257,7 @@ fun AddScreen(navController: NavController, viewModel: ExpenzViewModel) { }, label = { Text(text = "Date") - } + }, ) textState.value = TextFieldValue(mDate.value) date.value = mDate.value @@ -252,15 +269,17 @@ fun AddScreen(navController: NavController, viewModel: ExpenzViewModel) { // Set Notes val textStateNotes = remember { mutableStateOf(TextFieldValue()) } TextField( - value = notes.value, onValueChange = { if (notes.value.length <= 100) notes.value = it }, - modifier = Modifier + value = notes.value, + onValueChange = { if (notes.value.length <= 100) notes.value = it }, + modifier = + Modifier .fillMaxWidth() .height(100.dp) .padding(5.dp), label = { Text(text = stringResource(id = R.string.notes)) }, - placeholder = { Text(text = stringResource(id = R.string.any_notes)) } + placeholder = { Text(text = stringResource(id = R.string.any_notes)) }, ) textStateNotes.value = TextFieldValue(notes.value) } @@ -272,17 +291,18 @@ fun ReadonlyTextField( onValueChange: (TextFieldValue) -> Unit, modifier: Modifier = Modifier, onClick: () -> Unit, - label: @Composable () -> Unit + label: @Composable () -> Unit, ) { Box { TextField( value = value, onValueChange = onValueChange, modifier = modifier, - label = label + label = label, ) Box( - modifier = Modifier + modifier = + Modifier .matchParentSize() .clickable(onClick = onClick), ) diff --git a/app/src/main/java/com/money/expenz/ui/BottomNavItem.kt b/app/src/main/java/com/money/expenz/ui/BottomNavItem.kt index da00faf..00080da 100644 --- a/app/src/main/java/com/money/expenz/ui/BottomNavItem.kt +++ b/app/src/main/java/com/money/expenz/ui/BottomNavItem.kt @@ -10,23 +10,23 @@ import androidx.compose.ui.graphics.vector.ImageVector sealed class BottomNavItem( val route: String, @StringRes val titleResId: Int, - val icon: ImageVector + val icon: ImageVector, ) { data object Home : BottomNavItem( route = Screen.Home.route, titleResId = Screen.Home.title, - icon = Icons.Default.Home + icon = Icons.Default.Home, ) data object Add : BottomNavItem( route = Screen.Add.route, titleResId = Screen.Add.title, - icon = Icons.Default.AddCircle + icon = Icons.Default.AddCircle, ) data object Subscriptions : BottomNavItem( route = Screen.Subscription.route, titleResId = Screen.Subscription.title, - icon = Icons.AutoMirrored.Filled.List + icon = Icons.AutoMirrored.Filled.List, ) } diff --git a/app/src/main/java/com/money/expenz/ui/Constants.kt b/app/src/main/java/com/money/expenz/ui/Constants.kt deleted file mode 100644 index 69c4749..0000000 --- a/app/src/main/java/com/money/expenz/ui/Constants.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.money.expenz.ui - -// Navigation Routes -const val Login_Screen = "login_screen" -const val Register_Screen = "register_screen" -const val Home_Screen = "home_screen" -const val Subscription_Screen = "subscription_screen" -const val Details_Screen = "details_screen" -const val Add_Screen = "add_screen" diff --git a/app/src/main/java/com/money/expenz/ui/DataListScreen.kt b/app/src/main/java/com/money/expenz/ui/DataListScreen.kt new file mode 100644 index 0000000..56dd022 --- /dev/null +++ b/app/src/main/java/com/money/expenz/ui/DataListScreen.kt @@ -0,0 +1,133 @@ +package com.money.expenz.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.OutlinedCard +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.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.em +import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController +import androidx.navigation.compose.rememberNavController +import com.money.expenz.R +import com.money.expenz.data.IEDetails +import com.money.expenz.model.ExpenzAppBar.ExpenzTheme +import com.money.expenz.ui.home.ExpenzViewModel +import com.money.expenz.ui.theme.Typography + +@Composable +fun DataListScreen( + viewModel: ExpenzViewModel, + navController: NavController, +) { + if (viewModel.ieDetailsList.isNotEmpty()) { + DataList(viewModel, viewModel.ieDetailsList, navController = navController) + } else { + EmptyMessage() + } +} + +@Composable +fun EmptyMessage() { + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "No List to show", + modifier = Modifier.padding(10.dp), + style = Typography.bodySmall, + fontSize = 25.sp, + overflow = TextOverflow.Ellipsis, + lineHeight = 1.5.em, + ) + } +} + +@Composable +fun DataList( + viewModel: ExpenzViewModel, + ieDetails: List, + navController: NavController, +) { + LazyColumn(modifier = Modifier.background(ExpenzTheme.colorScheme.surfaceVariant)) { + items(ieDetails) { details -> DataCard(viewModel, details, navController) } + } +} + +@Composable +fun DataCard( + viewModel: ExpenzViewModel, + user: IEDetails, + navController: NavController, +) { + OutlinedCard( + modifier = Modifier + .clickable { + viewModel.getIEDetails(user.ieId) + navController.navigate(Screen.Details.route) + } + .fillMaxWidth() + .padding(top = 5.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + + ) { + Column( + modifier = Modifier.weight(1f) + ) { + Text( + text = user.category, + modifier = Modifier.padding(start = 15.dp, top = 10.dp), + style = Typography.bodySmall, + fontSize = 20.sp, + overflow = TextOverflow.Ellipsis, + lineHeight = 1.5.em, + textAlign = TextAlign.Start + ) + Text( + text = user.date, + modifier = Modifier.padding(start = 15.dp, bottom = 10.dp), + style = Typography.bodySmall, + fontSize = 15.sp, + overflow = TextOverflow.Ellipsis, + lineHeight = 1.5.em, + ) + } + + Text( + text = stringResource(id = R.string.dollar) + user.amount.toString(), + modifier = Modifier.padding(end = 15.dp), + style = Typography.bodySmall, + fontSize = 20.sp, + overflow = TextOverflow.Ellipsis, + lineHeight = 1.5.em, + textAlign = TextAlign.End + ) + } + } +} + +@Preview +@Composable +fun DataListScreenPreview() { + val viewModel: ExpenzViewModel = viewModel() + DataListScreen(viewModel, rememberNavController()) +} diff --git a/app/src/main/java/com/money/expenz/ui/DetailsScreen.kt b/app/src/main/java/com/money/expenz/ui/DetailsScreen.kt index baff814..db75886 100644 --- a/app/src/main/java/com/money/expenz/ui/DetailsScreen.kt +++ b/app/src/main/java/com/money/expenz/ui/DetailsScreen.kt @@ -1,35 +1,80 @@ package com.money.expenz.ui +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items import androidx.compose.material.Divider import androidx.compose.material.Text import androidx.compose.material3.MaterialTheme 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.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.money.expenz.R +import androidx.navigation.NavController +import com.money.expenz.data.asMap +import com.money.expenz.data.toIEDetailsDTO import com.money.expenz.model.ExpenzAppBar +import com.money.expenz.ui.home.ExpenzViewModel @Composable -fun DetailsScreen() { - Row(modifier = Modifier.padding(5.dp)) { +fun DetailsScreen( + viewModel: ExpenzViewModel, + navController: NavController +) { + val map = viewModel.selectedIEDetails.value?.toIEDetailsDTO()?.asMap() + val list = map?.entries?.toList() + LoadDetailsFromList(viewModel, list, navController) +} + +@Composable +fun LoadDetailsFromList( + viewModel: ExpenzViewModel, + list: List>?, + navController: NavController +) { + LazyColumn(modifier = Modifier.background(ExpenzAppBar.ExpenzTheme.colorScheme.onPrimary)) { + if (list != null) { + items(list) { entry -> + ShowDetails(viewModel = viewModel, details = entry, navController = navController) + } + } + } +} + +@Composable +fun ShowDetails( + viewModel: ExpenzViewModel, + details: Map.Entry, + navController: NavController +) { + Row( + modifier = Modifier.padding(5.dp).fillMaxWidth(), + verticalAlignment = Alignment.Top, + horizontalArrangement = Arrangement.SpaceBetween, + + ) { Text( - text = stringResource(id = R.string.total_income), + text = details.key.uppercase(), modifier = Modifier.padding(10.dp), style = MaterialTheme.typography.headlineMedium, - fontSize = 25.sp, - color = ExpenzAppBar.ExpenzTheme.colorScheme.secondary + fontSize = 20.sp, + color = ExpenzAppBar.ExpenzTheme.colorScheme.secondary, + textAlign = TextAlign.Start ) Text( - text = stringResource(id = R.string.dollar), + text = details.value.toString(), modifier = Modifier.padding(10.dp), style = MaterialTheme.typography.headlineMedium, fontSize = 25.sp, - color = ExpenzAppBar.ExpenzTheme.colorScheme.tertiary + color = ExpenzAppBar.ExpenzTheme.colorScheme.tertiary, + textAlign = TextAlign.End ) } Divider() diff --git a/app/src/main/java/com/money/expenz/ui/LoginScreen.kt b/app/src/main/java/com/money/expenz/ui/LoginScreen.kt index da85152..baef7a9 100644 --- a/app/src/main/java/com/money/expenz/ui/LoginScreen.kt +++ b/app/src/main/java/com/money/expenz/ui/LoginScreen.kt @@ -30,8 +30,10 @@ import com.money.expenz.model.ExpenzAppBar.ExpenzTheme import com.money.expenz.ui.home.ExpenzViewModel @Composable -fun LoginScreen(viewModel: ExpenzViewModel, navController: NavController) { - +fun LoginScreen( + viewModel: ExpenzViewModel, + navController: NavController, +) { val username = remember { mutableStateOf("") } val password = remember { mutableStateOf("") } val context = LocalContext.current @@ -39,24 +41,27 @@ fun LoginScreen(viewModel: ExpenzViewModel, navController: NavController) { Box(modifier = Modifier.fillMaxSize()) { ClickableText( text = AnnotatedString("Register here"), - modifier = Modifier + modifier = + Modifier .align(Alignment.BottomCenter) .padding(20.dp), onClick = { navController.navigate(Screen.Register.route) }, - style = TextStyle( + style = + TextStyle( fontSize = 14.sp, fontFamily = FontFamily.Default, textDecoration = TextDecoration.Underline, - color = ExpenzTheme.colorScheme.onSurface - ) + color = ExpenzTheme.colorScheme.onSurface, + ), ) } Column( - modifier = Modifier + modifier = + Modifier .padding(start = 30.dp, end = 30.dp) .fillMaxWidth(), verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { val showPassword by remember { mutableStateOf(false) } @@ -64,7 +69,7 @@ fun LoginScreen(viewModel: ExpenzViewModel, navController: NavController) { Text( text = "Login", fontSize = 30.sp, - color = ExpenzTheme.colorScheme.onSurface + color = ExpenzTheme.colorScheme.onSurface, ) Spacer(modifier = Modifier.height(50.dp)) TextField( @@ -76,9 +81,9 @@ fun LoginScreen(viewModel: ExpenzViewModel, navController: NavController) { trailingIcon = { Icon( imageVector = (Icons.Filled.AccountBox), - contentDescription = "" + contentDescription = "", ) - } + }, ) Spacer(modifier = Modifier.height(20.dp)) @@ -86,7 +91,8 @@ fun LoginScreen(viewModel: ExpenzViewModel, navController: NavController) { modifier = Modifier.fillMaxWidth(), label = { Text(text = "Password") }, value = password.value, - visualTransformation = if (showPassword) { + visualTransformation = + if (showPassword) { VisualTransformation.None } else { PasswordVisualTransformation() @@ -96,23 +102,25 @@ fun LoginScreen(viewModel: ExpenzViewModel, navController: NavController) { onValueChange = { password.value = it }, trailingIcon = { Icon(imageVector = Icons.Filled.Lock, contentDescription = "") - } + }, ) Spacer(modifier = Modifier.height(20.dp)) Box(modifier = Modifier.padding(start = 40.dp, end = 40.dp)) { Button( onClick = { - if (username.value.isEmpty() || password.value.isEmpty()) - Toast.makeText(context, "Enter valid data", Toast.LENGTH_SHORT).show() else if (!viewModel.checkUserInDB(username.value, password.value)) { + if (username.value.isEmpty() || password.value.isEmpty()) { + Toast.makeText(context, "Enter valid data", Toast.LENGTH_SHORT).show() + } else if (!viewModel.checkUserInDB(username.value, password.value)) { Toast.makeText(context, "Invalid user", Toast.LENGTH_SHORT).show() } }, shape = RoundedCornerShape(50.dp), colors = ButtonDefaults.buttonColors(containerColor = ExpenzTheme.colorScheme.primaryContainer), - modifier = Modifier + modifier = + Modifier .fillMaxWidth() - .height(50.dp) + .height(50.dp), ) { Text(text = "Login", color = ExpenzTheme.colorScheme.onPrimaryContainer) } @@ -122,11 +130,12 @@ fun LoginScreen(viewModel: ExpenzViewModel, navController: NavController) { ClickableText( text = AnnotatedString("Forgot password?"), onClick = { }, - style = TextStyle( + style = + TextStyle( fontSize = 14.sp, fontFamily = FontFamily.Default, - color = ExpenzTheme.colorScheme.onBackground - ) + color = ExpenzTheme.colorScheme.onBackground, + ), ) } } diff --git a/app/src/main/java/com/money/expenz/ui/NavigationSetup.kt b/app/src/main/java/com/money/expenz/ui/NavigationSetup.kt index b055b28..38842a8 100644 --- a/app/src/main/java/com/money/expenz/ui/NavigationSetup.kt +++ b/app/src/main/java/com/money/expenz/ui/NavigationSetup.kt @@ -11,7 +11,7 @@ import com.money.expenz.ui.home.HomeScreen fun NavigationSetup( viewModel: ExpenzViewModel, navController: NavHostController, - startDestination: String + startDestination: String, ) { NavHost(navController = navController, startDestination = startDestination) { composable(Screen.Login.route) { @@ -32,7 +32,10 @@ fun NavigationSetup( AddScreen(navController, viewModel) } composable(Screen.Details.route) { - DetailsScreen() + DetailsScreen(viewModel, navController) + } + composable(Screen.DataList.route) { + DataListScreen(viewModel, navController) } } } diff --git a/app/src/main/java/com/money/expenz/ui/RegisterScreen.kt b/app/src/main/java/com/money/expenz/ui/RegisterScreen.kt index 22f8f98..ff4c30f 100644 --- a/app/src/main/java/com/money/expenz/ui/RegisterScreen.kt +++ b/app/src/main/java/com/money/expenz/ui/RegisterScreen.kt @@ -32,44 +32,51 @@ fun RegisterScreen(viewModel: ExpenzViewModel) { val password = remember { mutableStateOf("") } val context = LocalContext.current Box( - modifier = Modifier + modifier = + Modifier .fillMaxSize() - .padding(20.dp) + .padding(20.dp), ) { Button( onClick = { - if (username.value.isEmpty()) Toast.makeText(context, "Enter Username", Toast.LENGTH_SHORT).show() - else if (password.value.isEmpty()) Toast.makeText(context, "Enter password", Toast.LENGTH_SHORT).show() - else if (email.value.isEmpty()) Toast.makeText(context, "Enter Email", Toast.LENGTH_SHORT).show() - else if (country.value.isEmpty()) { + if (username.value.isEmpty()) { + Toast.makeText(context, "Enter Username", Toast.LENGTH_SHORT).show() + } else if (password.value.isEmpty()) { + Toast.makeText(context, "Enter password", Toast.LENGTH_SHORT).show() + } else if (email.value.isEmpty()) { + Toast.makeText(context, "Enter Email", Toast.LENGTH_SHORT).show() + } else if (country.value.isEmpty()) { Toast.makeText(context, "Select Country", Toast.LENGTH_SHORT).show() } else { - val newUser = User( - userName = username.value, - password = password.value, - email = email.value, - country = country.value - ) + val newUser = + User( + userName = username.value, + password = password.value, + email = email.value, + country = country.value, + ) viewModel.registerUser(newUser) } }, shape = RoundedCornerShape(50.dp), colors = ButtonDefaults.buttonColors(containerColor = ExpenzAppBar.ExpenzTheme.colorScheme.primaryContainer), - modifier = Modifier + modifier = + Modifier .fillMaxWidth() .height(50.dp) - .align(Alignment.BottomCenter) + .align(Alignment.BottomCenter), ) { Text(text = "Register", color = ExpenzAppBar.ExpenzTheme.colorScheme.onPrimaryContainer) } } Column( - modifier = Modifier + modifier = + Modifier .padding(start = 20.dp, end = 20.dp) .fillMaxWidth() .background(ExpenzAppBar.ExpenzTheme.colorScheme.background), verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally + horizontalAlignment = Alignment.CenterHorizontally, ) { val showPassword by remember { mutableStateOf(false) } @@ -77,7 +84,7 @@ fun RegisterScreen(viewModel: ExpenzViewModel) { Text( text = "Register", fontSize = 30.sp, - color = ExpenzAppBar.ExpenzTheme.colorScheme.onSurface + color = ExpenzAppBar.ExpenzTheme.colorScheme.onSurface, ) Spacer(modifier = Modifier.height(50.dp)) TextField( @@ -93,7 +100,7 @@ fun RegisterScreen(viewModel: ExpenzViewModel) { contentDescription = "", modifier = Modifier.clickable { username.value = "" }, ) - } + }, ) Spacer(modifier = Modifier.height(20.dp)) @@ -101,7 +108,8 @@ fun RegisterScreen(viewModel: ExpenzViewModel) { modifier = Modifier.fillMaxWidth(), label = { Text(text = "Password") }, value = password.value, - visualTransformation = if (showPassword) { + visualTransformation = + if (showPassword) { VisualTransformation.None } else { PasswordVisualTransformation() @@ -114,9 +122,9 @@ fun RegisterScreen(viewModel: ExpenzViewModel) { Icon( modifier = Modifier.clickable { password.value = "" }, imageVector = Icons.Filled.Clear, - contentDescription = "" + contentDescription = "", ) - } + }, ) Spacer(modifier = Modifier.height(20.dp)) TextField( @@ -131,9 +139,9 @@ fun RegisterScreen(viewModel: ExpenzViewModel) { Icon( modifier = Modifier.clickable { email.value = "" }, imageVector = (Icons.Filled.Clear), - contentDescription = "clear" + contentDescription = "clear", ) - } + }, ) Spacer(modifier = Modifier.height(20.dp)) TextField( @@ -142,7 +150,7 @@ fun RegisterScreen(viewModel: ExpenzViewModel) { value = country.value, onValueChange = { country.value = it }, singleLine = true, - leadingIcon = { Icon(imageVector = Icons.Filled.LocationOn, contentDescription = "") } + leadingIcon = { Icon(imageVector = Icons.Filled.LocationOn, contentDescription = "") }, ) } } diff --git a/app/src/main/java/com/money/expenz/ui/Screen.kt b/app/src/main/java/com/money/expenz/ui/Screen.kt index 4fa87e6..14e5351 100644 --- a/app/src/main/java/com/money/expenz/ui/Screen.kt +++ b/app/src/main/java/com/money/expenz/ui/Screen.kt @@ -2,14 +2,31 @@ package com.money.expenz.ui import androidx.annotation.StringRes import com.money.expenz.R +import com.money.expenz.utils.Add_Screen +import com.money.expenz.utils.Data_List_Screen +import com.money.expenz.utils.Details_Screen +import com.money.expenz.utils.Home_Screen +import com.money.expenz.utils.Login_Screen +import com.money.expenz.utils.Register_Screen +import com.money.expenz.utils.Subscription_Screen -sealed class Screen(val route: String, @StringRes val title: Int) { - object Login : Screen(Login_Screen, R.string.title_login) - object Register : Screen(Register_Screen, R.string.title_login) - object Home : Screen(Home_Screen, R.string.app_name) - object Add : Screen(Add_Screen, R.string.menu_add) - object Subscription : Screen(Subscription_Screen, R.string.subscriptions) - object Details : Screen(Details_Screen, R.string.details) +sealed class Screen( + val route: String, + @StringRes val title: Int, +) { + data object Login : Screen(Login_Screen, R.string.title_login) + + data object Register : Screen(Register_Screen, R.string.title_login) + + data object Home : Screen(Home_Screen, R.string.app_name) + + data object Add : Screen(Add_Screen, R.string.menu_add) + + data object Subscription : Screen(Subscription_Screen, R.string.subscriptions) + + data object Details : Screen(Details_Screen, R.string.details) + + data object DataList : Screen(Data_List_Screen, R.string.data_list) companion object { fun valueOf(route: String?) = @@ -19,6 +36,7 @@ sealed class Screen(val route: String, @StringRes val title: Int) { Home_Screen -> Home Add_Screen -> Add Subscription_Screen -> Subscription + Data_List_Screen -> DataList Details_Screen -> Details else -> Home } diff --git a/app/src/main/java/com/money/expenz/ui/SubscriptionsScreen.kt b/app/src/main/java/com/money/expenz/ui/SubscriptionsScreen.kt index cf0802f..463758d 100644 --- a/app/src/main/java/com/money/expenz/ui/SubscriptionsScreen.kt +++ b/app/src/main/java/com/money/expenz/ui/SubscriptionsScreen.kt @@ -32,24 +32,30 @@ fun SubscriptionsScreen(navController: NavController) { } @Composable -fun SubscriptionList(subscriptions: List, navController: NavController) { +fun SubscriptionList( + subscriptions: List, + navController: NavController, +) { LazyColumn(modifier = Modifier.background(ExpenzTheme.colorScheme.onPrimary)) { items(subscriptions) { subscription -> SubscriptionCard(subscription, navController) } } } @Composable -fun SubscriptionCard(subscription: Subscription, navController: NavController) { +fun SubscriptionCard( + subscription: Subscription, + navController: NavController, +) { Row( modifier = Modifier.clickable { navController.navigate(Screen.Details.route) }, verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Start + horizontalArrangement = Arrangement.Start, ) { Image( painter = painterResource(subscription.imageResourceId), contentDescription = null, modifier = Modifier.padding(5.dp), - contentScale = ContentScale.Inside + contentScale = ContentScale.Inside, ) Text( text = stringResource(subscription.stringResourceId), @@ -57,7 +63,7 @@ fun SubscriptionCard(subscription: Subscription, navController: NavController) { style = Typography.bodySmall, fontSize = 25.sp, overflow = TextOverflow.Ellipsis, - lineHeight = 1.5.em + lineHeight = 1.5.em, ) } Divider() diff --git a/app/src/main/java/com/money/expenz/ui/home/ExpenzViewModel.kt b/app/src/main/java/com/money/expenz/ui/home/ExpenzViewModel.kt index fca855e..9b62cfb 100644 --- a/app/src/main/java/com/money/expenz/ui/home/ExpenzViewModel.kt +++ b/app/src/main/java/com/money/expenz/ui/home/ExpenzViewModel.kt @@ -9,7 +9,7 @@ import com.money.expenz.data.IEDetails import com.money.expenz.data.User import com.money.expenz.data.UserWithIEDetails import com.money.expenz.repository.UserRepository -import com.money.expenz.utils.Constants +import com.money.expenz.utils.ExpenzUtil import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -25,84 +25,117 @@ import kotlinx.coroutines.launch */ class ExpenzViewModel(private val repository: UserRepository) : ViewModel() { - sealed class ViewState { object Loading : ViewState() // hasLoggedIn = unknown + object LoggedIn : ViewState() // hasLoggedIn = true + object NotLoggedIn : ViewState() // hasLoggedIn = false } private val hasLoggedIn = MutableStateFlow(false) - val viewState = hasLoggedIn.map { hasLoggedIn -> - if (hasLoggedIn) { - ViewState.LoggedIn - } else { - ViewState.NotLoggedIn + val viewState = + hasLoggedIn.map { hasLoggedIn -> + if (hasLoggedIn) { + ViewState.LoggedIn + } else { + ViewState.NotLoggedIn + } } - } var loggedInUserId = MutableLiveData(0) - private var dbusers: MutableList = mutableListOf() + private var dbusers: MutableList = mutableListOf() + private var dbUsersWithIE: MutableList = mutableListOf() var _loggedInUser = MutableLiveData() val loggedInUser: LiveData get() = _loggedInUser - var userName = "" - var password = "" + private var _ieDetails: MutableList = mutableListOf() + var ieDetailsList: List = listOf() + + private var _selectedIEDetails = MutableLiveData() + val selectedIEDetails: LiveData get() = _selectedIEDetails + + private var _userName = "" + private var _password = "" private var job: Job? = null init { - getUserWithIEDetails() + getDatabaseUsers() } - private val exceptionHandler = CoroutineExceptionHandler { _, throwable -> - notifyError(throwable) - } + private val exceptionHandler = + CoroutineExceptionHandler { _, throwable -> + notifyError(throwable) + } - private fun getUserWithIEDetails() { - job = viewModelScope.launch { - repository.getUserWithIEDetails().flowOn(Dispatchers.IO).catch { exceptionHandler } - .collect { users -> + private fun getDatabaseUsers() { + viewModelScope.launch { + repository.getUsers()?.catch { exceptionHandler } + ?.collect { users -> dbusers = users.toMutableList() - dbusers.forEach { activeUsers -> - if ((activeUsers.user.userName == userName) && (activeUsers.user.password == password)) { + users.forEach { loggedInUser -> + if (loggedInUser.userName == _userName && loggedInUser.password == _password) { + loggedInUserId.value = loggedInUser.id + _loggedInUser.value = loggedInUser setLoggedIn(true) - _loggedInUser.value = activeUsers.user - } + } else setLoggedIn(false) } } } } + private fun getUserWithIEDetails() { + job = + viewModelScope.launch { + repository.getUserWithIEDetails().flowOn(Dispatchers.IO).catch { exceptionHandler } + .collect { users -> + dbUsersWithIE = users.toMutableList() + users.forEach { activeUser -> + if ((activeUser.user.id == loggedInUserId.value)) { + setLoggedIn(true) + _loggedInUser.value = activeUser.user + _ieDetails = activeUser.ieDetails.toMutableList() + } else + setLoggedIn(false) + } + } + } + } + private fun notifyError(exception: Throwable) { Log.d("ExpenzViewModel", "Exception ${exception.localizedMessage}") } - fun checkUserInDB(userName: String, password: String): Boolean { - dbusers.forEach { activeUsers -> - activeUsers.user - if ((activeUsers.user.userName == userName) && (activeUsers.user.password == password)) { + fun checkUserInDB( + userName: String, + password: String, + ): Boolean { + getDatabaseUsers() + dbusers.forEach { activeUser -> + if ((activeUser.userName == userName) && (activeUser.password == password)) { setLoggedIn(true) - getLoggedInUserDetails(activeUsers.user.id) - loggedInUserId.value = activeUsers.user.id + getLoggedInUserDetails(activeUser.id) + loggedInUserId.value = activeUser.id return true } } return false } - fun getLoggedInUserDetails(loggedInUserId: Int) { + private fun getLoggedInUserDetails(loggedInUserId: Int) { viewModelScope.launch { _loggedInUser.value = repository.getLoggedInUserDetails(loggedInUserId) } + getUserWithIEDetails() } fun registerUser(registerUser: User) { - userName = registerUser.userName - password = registerUser.password + _userName = registerUser.userName + _password = registerUser.password viewModelScope.launch { repository.insertUserData(registerUser) } @@ -111,22 +144,39 @@ class ExpenzViewModel(private val repository: UserRepository) : ViewModel() { fun insertIEDetails(ieDetails: IEDetails) { viewModelScope.launch { repository.insertIEDetails(ieDetails) } - getUserWithIEDetails() } - fun updateUserDetails(amount: Int, ieValue: String) { - if (ieValue == Constants.income) { + fun updateUserDetails( + amount: Int, + ieValue: String, + ) { + if (ieValue == ExpenzUtil.INCOME) { _loggedInUser.value?.totalIncome = _loggedInUser.value?.totalIncome?.plus(amount)!! - } else if (ieValue == Constants.expense || ieValue == Constants.subscription) { + } else if (ieValue == ExpenzUtil.EXPENSE || ieValue == ExpenzUtil.SUBSCRIPTION) { _loggedInUser.value?.totalExpense = _loggedInUser.value?.totalExpense?.plus(amount)!! } viewModelScope.launch { _loggedInUser.value?.let { repository.updateUserDetails(it) } } } - fun setLoggedIn(boolean: Boolean) { + private fun setLoggedIn(boolean: Boolean) { hasLoggedIn.value = boolean } + fun filterIEList(category: String) { + ieDetailsList = _ieDetails.filter { item -> item.ie == category } + // job = viewModelScope.launch { _ieDetails = repository.searchIECategory(category) } + } + + fun getIEDetails(ieID: Int) { + dbUsersWithIE.forEach { + it.ieDetails.forEach { ie -> + if (ie.ieId == ieID) { + _selectedIEDetails.value = ie + } + } + } + } + override fun onCleared() { super.onCleared() job?.cancel() diff --git a/app/src/main/java/com/money/expenz/ui/home/HomeScreen.kt b/app/src/main/java/com/money/expenz/ui/home/HomeScreen.kt index e70be63..3ccc9a0 100644 --- a/app/src/main/java/com/money/expenz/ui/home/HomeScreen.kt +++ b/app/src/main/java/com/money/expenz/ui/home/HomeScreen.kt @@ -41,10 +41,10 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.em import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import androidx.navigation.compose.rememberNavController import com.money.expenz.R -import com.money.expenz.data.DataSource import com.money.expenz.data.Subscription import com.money.expenz.data.User import com.money.expenz.model.ExpenzAppBar.ExpenzTheme @@ -53,15 +53,15 @@ import com.money.expenz.ui.theme.Black import com.money.expenz.ui.theme.Pink80 import com.money.expenz.ui.theme.PurpleGrey80 import com.money.expenz.ui.theme.Typography -import com.money.expenz.utils.Constants.Companion.expense -import com.money.expenz.utils.Constants.Companion.income -import com.money.expenz.utils.Constants.Companion.subscription +import com.money.expenz.utils.ExpenzUtil.Companion.EXPENSE +import com.money.expenz.utils.ExpenzUtil.Companion.INCOME +import com.money.expenz.utils.ExpenzUtil.Companion.SUBSCRIPTION @Composable fun HomeScreen( viewModel: ExpenzViewModel, navController: NavController, - onNavigateToLoginScreen: () -> Unit = {} + onNavigateToLoginScreen: () -> Unit = {}, ) { val viewState by viewModel.viewState.collectAsState(initial = false) when (viewState) { @@ -76,10 +76,10 @@ fun HomeScreen( Column( Modifier.verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.Center, ) { if (user != null) { - PieChart(navController, user) + PieChart(navController, user, viewModel) } } } @@ -87,32 +87,40 @@ fun HomeScreen( } @Composable -internal fun PieChart(navController: NavController, user: User) { - - val chartColors = listOf( - PurpleGrey80, Black, Pink80 - ) +internal fun PieChart( + navController: NavController, + user: User, + viewModel: ExpenzViewModel, +) { + val chartColors = + listOf( + PurpleGrey80, + Black, + Pink80, + ) val chartValues = listOf(90f, 80f, 40f) PieChart( + viewModel = viewModel, navController = navController, user = user, modifier = Modifier.padding(7.dp), colors = chartColors, - inputValues = chartValues + inputValues = chartValues, ) } @SuppressLint("UnusedBoxWithConstraintsScope") @Composable internal fun PieChart( + viewModel: ExpenzViewModel, navController: NavController, user: User, modifier: Modifier = Modifier, colors: List, inputValues: List, - legend: List = listOf(income, expense, subscription) + legend: List = listOf(INCOME, EXPENSE, SUBSCRIPTION), ) { val chartDegrees = 360f // circle shape @@ -120,23 +128,25 @@ internal fun PieChart( var startAngle = 270f // calculate each input percentage - val proportions = inputValues.map { - it * 100 / inputValues.sum() - } + val proportions = + inputValues.map { + it * 100 / inputValues.sum() + } // calculate each input slice degrees - val angleProgress = proportions.map { prop -> - chartDegrees * prop / 100 - } + val angleProgress = + proportions.map { prop -> + chartDegrees * prop / 100 + } BoxWithConstraints(modifier = modifier) { Canvas( - modifier = Modifier + modifier = + Modifier .fillMaxWidth() .height(310.dp) - .padding(start = 20.dp, end = 20.dp) + .padding(start = 20.dp, end = 20.dp), ) { - angleProgress.forEachIndexed { index, angle -> drawArc( color = colors[index], @@ -144,7 +154,7 @@ internal fun PieChart( sweepAngle = angle, useCenter = true, size = size, - style = Fill + style = Fill, ) startAngle += angle } @@ -155,55 +165,67 @@ internal fun PieChart( DisplayLegend(color = colors[i], legend = legend[i]) } } - TotalIncomeExpenseCard(navController, user) + TotalIncomeExpenseCard(navController, user, viewModel) } @Composable -fun DisplayLegend(color: Color, legend: String) { +fun DisplayLegend( + color: Color, + legend: String, +) { Row( horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Divider( modifier = Modifier.width(16.dp), thickness = 4.dp, - color = color + color = color, ) Spacer(modifier = Modifier.width(10.dp)) Text( text = legend, - color = ExpenzTheme.colorScheme.onBackground + color = ExpenzTheme.colorScheme.onBackground, ) } } @Composable -fun TotalIncomeExpenseCard(navController: NavController, user: User) { +fun TotalIncomeExpenseCard( + navController: NavController, + user: User, + viewModel: ExpenzViewModel, +) { Box( - modifier = Modifier + modifier = + Modifier .fillMaxWidth() - .padding(top = 15.dp) + .padding(top = 15.dp), ) { Card( - modifier = Modifier + modifier = + Modifier .height(150.dp) .align(Alignment.CenterStart) - .clickable { navController.navigate(Screen.Details.route) }, + .clickable { + navController.navigate(Screen.DataList.route) + viewModel.filterIEList("Income") + }, elevation = 10.dp, - backgroundColor = ExpenzTheme.colorScheme.primaryContainer + backgroundColor = ExpenzTheme.colorScheme.primaryContainer, ) { Column( horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.Center, ) { Text( text = stringResource(id = R.string.total_income), modifier = Modifier.padding(10.dp), style = MaterialTheme.typography.headlineMedium, fontSize = 25.sp, - color = ExpenzTheme.colorScheme.onPrimaryContainer + color = ExpenzTheme.colorScheme.onPrimaryContainer, ) Text( @@ -211,69 +233,78 @@ fun TotalIncomeExpenseCard(navController: NavController, user: User) { modifier = Modifier.padding(top = 25.dp), style = MaterialTheme.typography.bodyMedium, fontSize = 20.sp, - color = ExpenzTheme.colorScheme.onPrimaryContainer + color = ExpenzTheme.colorScheme.onPrimaryContainer, ) } } Card( - modifier = Modifier + modifier = + Modifier .padding(5.dp) .height(150.dp) .align(Alignment.CenterEnd) - .clickable { navController.navigate(Screen.Details.route) }, + .clickable { + navController.navigate(Screen.DataList.route) + viewModel.filterIEList("Expense") + }, elevation = 10.dp, - backgroundColor = ExpenzTheme.colorScheme.primaryContainer + backgroundColor = ExpenzTheme.colorScheme.primaryContainer, ) { Column( horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.Center, ) { Text( text = stringResource(id = R.string.total_Expense), modifier = Modifier.padding(10.dp), style = ExpenzTheme.typography.headlineMedium, fontSize = 25.sp, - color = ExpenzTheme.colorScheme.onPrimaryContainer + color = ExpenzTheme.colorScheme.onPrimaryContainer, ) Text( text = stringResource(id = R.string.dollar) + user.totalExpense.toString(), modifier = Modifier.padding(top = 25.dp), style = ExpenzTheme.typography.bodyMedium, fontSize = 20.sp, - color = ExpenzTheme.colorScheme.onPrimaryContainer + color = ExpenzTheme.colorScheme.onPrimaryContainer, ) } } } - SubscriptionList(subscriptions = DataSource().loadSubscriptions(), navController) + // SubscriptionList(subscriptions = DataSource().loadSubscriptions(), navController) } @Composable -fun SubscriptionList(subscriptions: List, navController: NavController) { +fun SubscriptionList( + subscriptions: List, + navController: NavController, +) { Box( - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) { Text( text = stringResource(id = R.string.subscriptions), - modifier = Modifier + modifier = + Modifier .padding(5.dp) .align(Alignment.CenterStart), style = ExpenzTheme.typography.headlineLarge, fontSize = 20.sp, - color = ExpenzTheme.colorScheme.onSurface + color = ExpenzTheme.colorScheme.onSurface, ) IconButton( - modifier = Modifier + modifier = + Modifier .padding(5.dp) .align(Alignment.CenterEnd), onClick = { navController.navigate(Screen.Subscription.route) - } + }, ) { Icon( imageVector = Icons.AutoMirrored.Rounded.ArrowForward, contentDescription = "", - tint = ExpenzTheme.colorScheme.onBackground + tint = ExpenzTheme.colorScheme.onBackground, ) } } @@ -284,24 +315,28 @@ fun SubscriptionList(subscriptions: List, navController: NavContro } @Composable -fun SubscriptionCard(subscription: Subscription, modifier: Modifier = Modifier) { +fun SubscriptionCard( + subscription: Subscription, + modifier: Modifier = Modifier, +) { Card( - modifier = modifier + modifier = + modifier .padding(end = 15.dp, top = 5.dp, bottom = 10.dp) .width(180.dp) .height(70.dp), elevation = 8.dp, - backgroundColor = ExpenzTheme.colorScheme.secondary + backgroundColor = ExpenzTheme.colorScheme.secondary, ) { Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Start + horizontalArrangement = Arrangement.Start, ) { Icon( modifier = modifier.padding(5.dp), painter = painterResource(subscription.imageResourceId), contentDescription = stringResource(id = R.string.amazon), - tint = ExpenzTheme.colorScheme.onSecondary + tint = ExpenzTheme.colorScheme.onSecondary, ) Text( text = stringResource(subscription.stringResourceId), @@ -310,7 +345,7 @@ fun SubscriptionCard(subscription: Subscription, modifier: Modifier = Modifier) fontSize = 20.sp, color = ExpenzTheme.colorScheme.onSecondary, overflow = TextOverflow.Ellipsis, - lineHeight = 1.5.em + lineHeight = 1.5.em, ) } } @@ -322,8 +357,9 @@ fun HomeScreenPreview() { Column( Modifier.verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.Center, ) { - PieChart(rememberNavController(), User()) + val viewModel: ExpenzViewModel = viewModel() + PieChart(rememberNavController(), User(), viewModel) } } diff --git a/app/src/main/java/com/money/expenz/ui/theme/Theme.kt b/app/src/main/java/com/money/expenz/ui/theme/Theme.kt index e32454b..f5269f3 100644 --- a/app/src/main/java/com/money/expenz/ui/theme/Theme.kt +++ b/app/src/main/java/com/money/expenz/ui/theme/Theme.kt @@ -11,85 +11,88 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalView import androidx.core.view.WindowCompat -private val LightColorScheme = lightColorScheme( - primary = md_theme_light_primary, - onPrimary = md_theme_light_onPrimary, - primaryContainer = md_theme_light_primaryContainer, - onPrimaryContainer = md_theme_light_onPrimaryContainer, - secondary = md_theme_light_secondary, - onSecondary = md_theme_light_onSecondary, - secondaryContainer = md_theme_light_secondaryContainer, - onSecondaryContainer = md_theme_light_onSecondaryContainer, - tertiary = md_theme_light_tertiary, - onTertiary = md_theme_light_onTertiary, - tertiaryContainer = md_theme_light_tertiaryContainer, - onTertiaryContainer = md_theme_light_onTertiaryContainer, - error = md_theme_light_error, - errorContainer = md_theme_light_errorContainer, - onError = md_theme_light_onError, - onErrorContainer = md_theme_light_onErrorContainer, - background = md_theme_light_background, - onBackground = md_theme_light_onBackground, - outline = md_theme_light_outline, - inverseOnSurface = md_theme_light_inverseOnSurface, - inverseSurface = md_theme_light_inverseSurface, - inversePrimary = md_theme_light_inversePrimary, - surfaceTint = md_theme_light_surfaceTint, - outlineVariant = md_theme_light_outlineVariant, - scrim = md_theme_light_scrim, - surface = md_theme_light_surface, - onSurface = md_theme_light_onSurface, - surfaceVariant = md_theme_light_surfaceVariant, - onSurfaceVariant = md_theme_light_onSurfaceVariant, -) +private val LightColorScheme = + lightColorScheme( + primary = md_theme_light_primary, + onPrimary = md_theme_light_onPrimary, + primaryContainer = md_theme_light_primaryContainer, + onPrimaryContainer = md_theme_light_onPrimaryContainer, + secondary = md_theme_light_secondary, + onSecondary = md_theme_light_onSecondary, + secondaryContainer = md_theme_light_secondaryContainer, + onSecondaryContainer = md_theme_light_onSecondaryContainer, + tertiary = md_theme_light_tertiary, + onTertiary = md_theme_light_onTertiary, + tertiaryContainer = md_theme_light_tertiaryContainer, + onTertiaryContainer = md_theme_light_onTertiaryContainer, + error = md_theme_light_error, + errorContainer = md_theme_light_errorContainer, + onError = md_theme_light_onError, + onErrorContainer = md_theme_light_onErrorContainer, + background = md_theme_light_background, + onBackground = md_theme_light_onBackground, + outline = md_theme_light_outline, + inverseOnSurface = md_theme_light_inverseOnSurface, + inverseSurface = md_theme_light_inverseSurface, + inversePrimary = md_theme_light_inversePrimary, + surfaceTint = md_theme_light_surfaceTint, + outlineVariant = md_theme_light_outlineVariant, + scrim = md_theme_light_scrim, + surface = md_theme_light_surface, + onSurface = md_theme_light_onSurface, + surfaceVariant = md_theme_light_surfaceVariant, + onSurfaceVariant = md_theme_light_onSurfaceVariant, + ) -private val DarkColorScheme = darkColorScheme( - primary = md_theme_dark_primary, - onPrimary = md_theme_dark_onPrimary, - primaryContainer = md_theme_dark_primaryContainer, - onPrimaryContainer = md_theme_dark_onPrimaryContainer, - secondary = md_theme_dark_secondary, - onSecondary = md_theme_dark_onSecondary, - secondaryContainer = md_theme_dark_secondaryContainer, - onSecondaryContainer = md_theme_dark_onSecondaryContainer, - tertiary = md_theme_dark_tertiary, - onTertiary = md_theme_dark_onTertiary, - tertiaryContainer = md_theme_dark_tertiaryContainer, - onTertiaryContainer = md_theme_dark_onTertiaryContainer, - error = md_theme_dark_error, - errorContainer = md_theme_dark_errorContainer, - onError = md_theme_dark_onError, - onErrorContainer = md_theme_dark_onErrorContainer, - background = md_theme_dark_background, - onBackground = md_theme_dark_onBackground, - outline = md_theme_dark_outline, - inverseOnSurface = md_theme_dark_inverseOnSurface, - inverseSurface = md_theme_dark_inverseSurface, - inversePrimary = md_theme_dark_inversePrimary, - surfaceTint = md_theme_dark_surfaceTint, - outlineVariant = md_theme_dark_outlineVariant, - scrim = md_theme_dark_scrim, - surface = md_theme_dark_surface, - onSurface = md_theme_dark_onSurface, - surfaceVariant = md_theme_dark_surfaceVariant, - onSurfaceVariant = md_theme_dark_onSurfaceVariant, -) +private val DarkColorScheme = + darkColorScheme( + primary = md_theme_dark_primary, + onPrimary = md_theme_dark_onPrimary, + primaryContainer = md_theme_dark_primaryContainer, + onPrimaryContainer = md_theme_dark_onPrimaryContainer, + secondary = md_theme_dark_secondary, + onSecondary = md_theme_dark_onSecondary, + secondaryContainer = md_theme_dark_secondaryContainer, + onSecondaryContainer = md_theme_dark_onSecondaryContainer, + tertiary = md_theme_dark_tertiary, + onTertiary = md_theme_dark_onTertiary, + tertiaryContainer = md_theme_dark_tertiaryContainer, + onTertiaryContainer = md_theme_dark_onTertiaryContainer, + error = md_theme_dark_error, + errorContainer = md_theme_dark_errorContainer, + onError = md_theme_dark_onError, + onErrorContainer = md_theme_dark_onErrorContainer, + background = md_theme_dark_background, + onBackground = md_theme_dark_onBackground, + outline = md_theme_dark_outline, + inverseOnSurface = md_theme_dark_inverseOnSurface, + inverseSurface = md_theme_dark_inverseSurface, + inversePrimary = md_theme_dark_inversePrimary, + surfaceTint = md_theme_dark_surfaceTint, + outlineVariant = md_theme_dark_outlineVariant, + scrim = md_theme_dark_scrim, + surface = md_theme_dark_surface, + onSurface = md_theme_dark_onSurface, + surfaceVariant = md_theme_dark_surfaceVariant, + onSurfaceVariant = md_theme_dark_onSurfaceVariant, + ) @Composable fun ExpenzTheme( darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+ dynamicColor: Boolean = false, - content: @Composable () -> Unit + content: @Composable () -> Unit, ) { - val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + val colorScheme = + when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + darkTheme -> DarkColorScheme + else -> LightColorScheme } - darkTheme -> DarkColorScheme - else -> LightColorScheme - } val view = LocalView.current if (!view.isInEditMode) { SideEffect { @@ -102,6 +105,6 @@ fun ExpenzTheme( MaterialTheme( colorScheme = colorScheme, typography = Typography, - content = content + content = content, ) } diff --git a/app/src/main/java/com/money/expenz/ui/theme/Type.kt b/app/src/main/java/com/money/expenz/ui/theme/Type.kt index 9bbfe45..c985974 100644 --- a/app/src/main/java/com/money/expenz/ui/theme/Type.kt +++ b/app/src/main/java/com/money/expenz/ui/theme/Type.kt @@ -7,26 +7,30 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp // Set of Material typography styles to start with -val Typography = Typography( - bodyLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 16.sp, - lineHeight = 24.sp, - letterSpacing = 0.5.sp - ), - titleLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 22.sp, - lineHeight = 28.sp, - letterSpacing = 0.sp - ), - labelSmall = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Medium, - fontSize = 11.sp, - lineHeight = 16.sp, - letterSpacing = 0.5.sp +val Typography = + Typography( + bodyLarge = + TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp, + ), + titleLarge = + TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp, + ), + labelSmall = + TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp, + ), ) -) diff --git a/app/src/main/java/com/money/expenz/utils/Constants.kt b/app/src/main/java/com/money/expenz/utils/Constants.kt index c1357f6..09bb848 100644 --- a/app/src/main/java/com/money/expenz/utils/Constants.kt +++ b/app/src/main/java/com/money/expenz/utils/Constants.kt @@ -1,11 +1,10 @@ package com.money.expenz.utils -class Constants { - - companion object { - // Pie Chart - const val income = "Income" - const val expense = "Expense" - const val subscription = "Subscription" - } -} +// Navigation Routes +const val Login_Screen = "login_screen" +const val Register_Screen = "register_screen" +const val Home_Screen = "home_screen" +const val Subscription_Screen = "subscription_screen" +const val Details_Screen = "details_screen" +const val Add_Screen = "add_screen" +const val Data_List_Screen = "data_list_screen" diff --git a/app/src/main/java/com/money/expenz/utils/ExpenzUtil.kt b/app/src/main/java/com/money/expenz/utils/ExpenzUtil.kt new file mode 100644 index 0000000..a96e7a5 --- /dev/null +++ b/app/src/main/java/com/money/expenz/utils/ExpenzUtil.kt @@ -0,0 +1,10 @@ +package com.money.expenz.utils + +class ExpenzUtil { + companion object { + // Pie Chart + const val INCOME = "Income" + const val EXPENSE = "Expense" + const val SUBSCRIPTION = "Subscription" + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f40465f..1b492cc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -12,6 +12,7 @@ Details Add Subscriptions + Income/Expense // Subscriptions My Subscriptions diff --git a/build.gradle b/build.gradle index a60bc87..8152f83 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } } plugins { - id 'com.android.application' version '7.3.1' apply false - id 'com.android.library' version '7.3.1' apply false + id 'com.android.application' version '7.4.2' apply false + id 'com.android.library' version '7.4.2' apply false id 'org.jetbrains.kotlin.android' version '1.9.22' apply false } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index eb55e49..a007934 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu Mar 30 16:09:29 EDT 2023 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME