From 1d5ee976f0f69e9adfc617188b6a16b93a89d792 Mon Sep 17 00:00:00 2001 From: sadighasanzade Date: Wed, 8 Nov 2023 22:08:47 +0200 Subject: [PATCH 1/5] ui improved --- .../com/sadig/spendtracker/MainActivity.kt | 5 +- .../spendtracker/ui/screens/MainScreen.kt | 318 +++++++++++------- .../com/sadig/spendtracker/ui/theme/Theme.kt | 12 +- 3 files changed, 209 insertions(+), 126 deletions(-) diff --git a/app/src/main/java/com/sadig/spendtracker/MainActivity.kt b/app/src/main/java/com/sadig/spendtracker/MainActivity.kt index cb19f97..18b438f 100644 --- a/app/src/main/java/com/sadig/spendtracker/MainActivity.kt +++ b/app/src/main/java/com/sadig/spendtracker/MainActivity.kt @@ -39,7 +39,9 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) val viewmodel by viewModels() setContent { - SpendTrackerTheme { + SpendTrackerTheme( + dynamicColor = false + ) { ScreenWithBottomAppBar(viewModel = viewmodel) } } @@ -55,6 +57,7 @@ fun ScreenWithBottomAppBar(viewModel: HomeViewModel) { bottomBar = { BottomNavigationBar(navController = navController) }, floatingActionButton = { AddSpendingButton { + navController.navigate(BottomNavigationItem.Home.route) viewModel.onEvent(HomeViewModel.EventType.OnAddSpendButtonClicked) } } diff --git a/app/src/main/java/com/sadig/spendtracker/ui/screens/MainScreen.kt b/app/src/main/java/com/sadig/spendtracker/ui/screens/MainScreen.kt index 43ac4cc..b199210 100644 --- a/app/src/main/java/com/sadig/spendtracker/ui/screens/MainScreen.kt +++ b/app/src/main/java/com/sadig/spendtracker/ui/screens/MainScreen.kt @@ -1,5 +1,7 @@ package com.sadig.spendtracker.ui.screens +import android.annotation.SuppressLint +import android.util.Log import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -9,28 +11,36 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.DateRange import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Card +import androidx.compose.material3.DatePicker +import androidx.compose.material3.DatePickerDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.rememberDatePickerState 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.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue @@ -39,6 +49,10 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import com.sadig.spendtracker.ui.theme.* import com.sadig.spendtracker.ui.viewmodel.HomeViewModel +import java.text.SimpleDateFormat +import java.time.Instant +import java.time.LocalDate +import java.time.format.DateTimeFormatter import java.util.Date @Composable @@ -89,82 +103,74 @@ fun SpinnerDialog( Dialog( onDismissRequest = onDismiss, content = { - Column( - modifier = Modifier - .padding(all = 16.dp) - .background(color = Color.White, shape = RoundedCornerShape(12.dp)) - .padding(all = 16.dp), - horizontalAlignment = androidx.compose.ui.Alignment.CenterHorizontally + Surface( + shape = RoundedCornerShape(12.dp), ) { - Text( - text = "Select your currency", - fontSize = 20.sp, - modifier = Modifier.padding(bottom = 16.dp) - ) + Column(horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(16.dp)) { + Text( + text = "Select your currency", + fontSize = 20.sp, + modifier = Modifier.padding(bottom = 16.dp) + ) + + // Spinner + Row(modifier = Modifier.fillMaxWidth()) { + Text( + text = "Selected: ", + fontSize = 16.sp, + modifier = Modifier + .clickable { isDropdownVisible = true } + .padding(16.dp) + .padding(8.dp), + ) + Spacer(modifier = Modifier.width(10.dp)) + Text( + text = selectedValue, + fontSize = 16.sp, + modifier = Modifier + .clickable { isDropdownVisible = true } + .padding(16.dp) + .padding(8.dp), + style = androidx.compose.ui.text.TextStyle(fontWeight = FontWeight.Bold) + ) + } - // Spinner - Row(modifier = Modifier.fillMaxWidth()) { - Text( - text = "Selected: ", - fontSize = 16.sp, - modifier = Modifier - .clickable { isDropdownVisible = true } - .padding(16.dp) - .padding(8.dp), - ) - Spacer(modifier = Modifier.width(10.dp)) - Text( - text = selectedValue, - fontSize = 16.sp, - modifier = Modifier - .clickable { isDropdownVisible = true } - .padding(16.dp) - .padding(8.dp), - style = androidx.compose.ui.text.TextStyle(fontWeight = FontWeight.Bold) - ) - } + if (isDropdownVisible) { + options.forEach { option -> + OutlinedButton( + onClick = { + selectedValue = option + isDropdownVisible = false + }, + modifier = Modifier + .fillMaxWidth() + .padding(4.dp) + ) { + Text(text = option) + } + } + } - if (isDropdownVisible) { - options.forEach { option -> - OutlinedButton( - onClick = { - selectedValue = option - isDropdownVisible = false - }, - colors = ButtonDefaults.buttonColors( - contentColor = Color.Red, - containerColor = Color.White - ), - modifier = Modifier - .fillMaxWidth() - .padding(4.dp) + // OK button + Button( + onClick = { onOkClick(selectedValue) }, + shape = RoundedCornerShape(10.dp), + + modifier = Modifier.padding(top = 16.dp) ) { - Text(text = option) + Text(text = "OK") } } - } - - // OK button - Button( - onClick = { onOkClick(selectedValue) }, - shape = RoundedCornerShape(10.dp), - colors = ButtonDefaults.buttonColors( - containerColor = Color.Red, - contentColor = Color.White - ), - modifier = Modifier.padding(top = 16.dp) - ) { - Text(text = "OK") - } } } ) } } -@OptIn(ExperimentalMaterial3Api::class) +@SuppressLint("SimpleDateFormat") @Composable fun InputDialog( currency: String, @@ -175,70 +181,90 @@ fun InputDialog( var description by remember { mutableStateOf(TextFieldValue()) } var amount by remember { mutableStateOf(TextFieldValue()) } var selectedDate by remember { mutableStateOf(Date()) } - - AlertDialog( - onDismissRequest = { onDismiss() }, - title = { Text("Enter Details") }, - confirmButton = { - Button( - onClick = { - val titleText = title.text - val amountText = amount.text.toDoubleOrNull() - if (amountText != null) { - onConfirm(titleText, description.text, amountText, selectedDate) - onDismiss() - } - }, - ) { - Text(text = "Save") - } - }, - dismissButton = { - Button( - onClick = { onDismiss() }, - ) { - Text(text = "Cancel") - } - }, - text = { - Column( - modifier = Modifier.padding(16.dp) - ) { - OutlinedTextField( - value = title, - onValueChange = { - title = it - }, - label = { Text("Title") } - ) - - OutlinedTextField( - value = description, - onValueChange = { - description = it + var showDateDialog by remember { mutableStateOf(false) } + Surface( shape = RoundedCornerShape(16.dp)){ + AlertDialog( + onDismissRequest = { onDismiss() }, + title = { Text("Enter Details") }, + confirmButton = { + Button( + onClick = { + val titleText = title.text + val amountText = amount.text.toDoubleOrNull() + if (amountText != null) { + onConfirm(titleText, description.text, amountText, selectedDate) + onDismiss() + } }, - label = { Text("Description") } - ) - Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp), - horizontalArrangement = Arrangement.SpaceBetween + ) { + Text(text = "Save") + } + }, + dismissButton = { + Button( + onClick = { onDismiss() }, + ) { + Text(text = "Cancel") + } + }, + text = { + Column( + modifier = Modifier.padding(16.dp) ) { OutlinedTextField( - value = amount, + value = title, onValueChange = { - amount = it + title = it }, - label = { Text("Amount in $currency") }, - keyboardOptions = KeyboardOptions.Default.copy( - keyboardType = KeyboardType.Number - ) + label = { Text("Title") } ) + + OutlinedTextField( + value = description, + onValueChange = { + description = it + }, + label = { Text("Description") } + ) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + OutlinedTextField( + value = amount, + onValueChange = { + amount = it + }, + label = { Text("Amount in $currency") }, + keyboardOptions = KeyboardOptions.Default.copy( + keyboardType = KeyboardType.Number + ) + ) + } + + TextWithEndIcon( + icon = Icons.Filled.DateRange, + text = java.sql.Date(selectedDate.time).toString(), + modifier = Modifier.fillMaxWidth(), + description = "date" + ) { + showDateDialog = true + } + if (showDateDialog) { + SpendingDatePickerDialog(onDateSelected = { + selectedDate = it + showDateDialog = false + }) { + showDateDialog = false + } + } } } - } - ) + ) + } + } @Composable @@ -252,3 +278,57 @@ fun AddSpendingButton(onClick: () -> Unit) { } } +@Composable +fun TextWithEndIcon( + icon: ImageVector, + text: String, + modifier: Modifier, + description: String, + onIconClicked: () -> Unit +) { + Row(modifier = modifier) { + Text(text = text, fontSize = 16.sp) + Spacer(Modifier.size(20.dp)) + Icon(imageVector = icon, contentDescription = description, Modifier.clickable { + onIconClicked() + }) + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun SpendingDatePickerDialog( + onDateSelected: (Date) -> Unit, + onDismiss: () -> Unit +) { + val datePickerState = rememberDatePickerState(Instant.now().toEpochMilli()) + + val selectedDate = datePickerState.selectedDateMillis?.let { + Date(it) + } ?: Date(Instant.now().toEpochMilli()) + + DatePickerDialog( + onDismissRequest = { onDismiss() }, + confirmButton = { + Button(onClick = { + onDateSelected(selectedDate) + onDismiss() + } + + ) { + Text(text = "OK") + } + }, + dismissButton = { + Button(onClick = { + onDismiss() + }) { + Text(text = "Cancel") + } + } + ) { + DatePicker( + state = datePickerState + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sadig/spendtracker/ui/theme/Theme.kt b/app/src/main/java/com/sadig/spendtracker/ui/theme/Theme.kt index ec7396f..8c20d87 100644 --- a/app/src/main/java/com/sadig/spendtracker/ui/theme/Theme.kt +++ b/app/src/main/java/com/sadig/spendtracker/ui/theme/Theme.kt @@ -19,19 +19,19 @@ import androidx.core.view.WindowCompat private val DarkColorScheme = darkColorScheme( primary = Color.Red, onPrimary = Color.White , - primaryContainer = LightGray, - secondaryContainer = LightGray, +// primaryContainer = LightGray, +// secondaryContainer = LightGray, secondary = LightRed, - tertiary = Pink80 +// tertiary = Pink80 ) private val LightColorScheme = lightColorScheme( primary = Color.Red, onPrimary = Color.White , - primaryContainer = LightGray, - secondaryContainer = LightGray, +// primaryContainer = LightGray, +// secondaryContainer = LightGray, secondary = LightRed, - tertiary = Pink40 +// tertiary = Pink40 /* Other default colors to override background = Color(0xFFFFFBFE), From d299ec86ebb5c55b437c8ca2968b269c1bee48f8 Mon Sep 17 00:00:00 2001 From: sadighasanzade Date: Thu, 9 Nov 2023 00:21:03 +0200 Subject: [PATCH 2/5] Db implementation added --- app/build.gradle.kts | 12 +++++++ .../sadig/spendtracker/data/db/AppDatabase.kt | 11 +++++++ .../sadig/spendtracker/data/db/SpendingDAO.kt | 12 +++++++ .../sadig/spendtracker/data/model/Spending.kt | 18 +++++++++++ .../data/repository/SpendingRepositoryImpl.kt | 8 ++++- .../source/local/SpendingDataSourceImpl.kt | 9 +++++- ...sitoryComponent.kt => RepositoryModule.kt} | 2 +- .../spendtracker/di/module/RoomModule.kt | 31 +++++++++++++++++++ ...ckerComponent.kt => SpendTrackerModule.kt} | 15 +++++++-- .../domain/repository/SpendingRepository.kt | 3 ++ .../domain/source/local/SpendingDataSource.kt | 3 ++ .../domain/usecase/PutSpendingInteractor.kt | 11 +++++++ .../ui/viewmodel/HomeViewModel.kt | 12 ++++++- build.gradle.kts | 1 - 14 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/sadig/spendtracker/data/db/AppDatabase.kt create mode 100644 app/src/main/java/com/sadig/spendtracker/data/db/SpendingDAO.kt create mode 100644 app/src/main/java/com/sadig/spendtracker/data/model/Spending.kt rename app/src/main/java/com/sadig/spendtracker/di/module/{RepositoryComponent.kt => RepositoryModule.kt} (95%) create mode 100644 app/src/main/java/com/sadig/spendtracker/di/module/RoomModule.kt rename app/src/main/java/com/sadig/spendtracker/di/module/{SpendTrackerComponent.kt => SpendTrackerModule.kt} (78%) create mode 100644 app/src/main/java/com/sadig/spendtracker/domain/usecase/PutSpendingInteractor.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d012291..5ed5360 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,6 +4,7 @@ plugins { id("com.google.dagger.hilt.android") id ("kotlin-android") id ("kotlin-kapt") + } android { @@ -21,6 +22,14 @@ android { vectorDrawables { useSupportLibrary = true } + javaCompileOptions{ + annotationProcessorOptions { + arguments += mapOf( + "room.schemaLocation" to "$projectDir/schemas", + "room.incremental" to "true" + ) + } + } } buildTypes { @@ -80,6 +89,8 @@ dependencies { implementation("androidx.room:room-runtime:$room_version") annotationProcessor("androidx.room:room-compiler:$room_version") implementation("androidx.room:room-ktx:$room_version") + kapt("androidx.room:room-compiler:$room_version") + implementation("com.google.dagger:hilt-android:2.44") kapt("com.google.dagger:hilt-android-compiler:2.44") @@ -91,6 +102,7 @@ dependencies { implementation("androidx.navigation:navigation-compose:$nav_version") implementation("androidx.datastore:datastore-preferences:1.0.0") + } kapt { correctErrorTypes = true diff --git a/app/src/main/java/com/sadig/spendtracker/data/db/AppDatabase.kt b/app/src/main/java/com/sadig/spendtracker/data/db/AppDatabase.kt new file mode 100644 index 0000000..612b30a --- /dev/null +++ b/app/src/main/java/com/sadig/spendtracker/data/db/AppDatabase.kt @@ -0,0 +1,11 @@ +package com.sadig.spendtracker.data.db + +import androidx.room.Database +import androidx.room.RoomDatabase +import com.sadig.spendtracker.data.model.Spending +import javax.inject.Inject + +@Database(entities = [Spending::class], version = 1) +abstract class AppDatabase() : RoomDatabase() { + abstract fun spendingDao(): SpendingDAO +} \ No newline at end of file diff --git a/app/src/main/java/com/sadig/spendtracker/data/db/SpendingDAO.kt b/app/src/main/java/com/sadig/spendtracker/data/db/SpendingDAO.kt new file mode 100644 index 0000000..cdbe5ce --- /dev/null +++ b/app/src/main/java/com/sadig/spendtracker/data/db/SpendingDAO.kt @@ -0,0 +1,12 @@ +package com.sadig.spendtracker.data.db + +import androidx.room.Dao +import androidx.room.Insert +import com.sadig.spendtracker.data.model.Spending + +@Dao +interface SpendingDAO { + @Insert + fun insertAll(vararg spendings: Spending) + +} \ No newline at end of file diff --git a/app/src/main/java/com/sadig/spendtracker/data/model/Spending.kt b/app/src/main/java/com/sadig/spendtracker/data/model/Spending.kt new file mode 100644 index 0000000..2f312ca --- /dev/null +++ b/app/src/main/java/com/sadig/spendtracker/data/model/Spending.kt @@ -0,0 +1,18 @@ +package com.sadig.spendtracker.data.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.util.Date + +@Entity +data class Spending( + @ColumnInfo(name = "title") val title: String, + @ColumnInfo(name = "description") val description: String?, + @ColumnInfo(name = "amount") val amount: Double, + @ColumnInfo(name = "date") val date: Date, +){ + @PrimaryKey(autoGenerate = true) + var uid: Int = 0 + +} diff --git a/app/src/main/java/com/sadig/spendtracker/data/repository/SpendingRepositoryImpl.kt b/app/src/main/java/com/sadig/spendtracker/data/repository/SpendingRepositoryImpl.kt index 4f1a713..427dd51 100644 --- a/app/src/main/java/com/sadig/spendtracker/data/repository/SpendingRepositoryImpl.kt +++ b/app/src/main/java/com/sadig/spendtracker/data/repository/SpendingRepositoryImpl.kt @@ -1,7 +1,13 @@ package com.sadig.spendtracker.data.repository +import com.sadig.spendtracker.data.model.Spending import com.sadig.spendtracker.domain.repository.SpendingRepository +import com.sadig.spendtracker.domain.source.local.SpendingDataSource import javax.inject.Inject -class SpendingRepositoryImpl @Inject constructor(): SpendingRepository { +class SpendingRepositoryImpl @Inject constructor(val spendingDataSource: SpendingDataSource) : + SpendingRepository { + override fun putSpending(spending: Spending) { + spendingDataSource.putSpending(spending = spending) + } } \ No newline at end of file diff --git a/app/src/main/java/com/sadig/spendtracker/data/source/local/SpendingDataSourceImpl.kt b/app/src/main/java/com/sadig/spendtracker/data/source/local/SpendingDataSourceImpl.kt index e61d632..8c31829 100644 --- a/app/src/main/java/com/sadig/spendtracker/data/source/local/SpendingDataSourceImpl.kt +++ b/app/src/main/java/com/sadig/spendtracker/data/source/local/SpendingDataSourceImpl.kt @@ -1,6 +1,13 @@ package com.sadig.spendtracker.data.source.local +import com.sadig.spendtracker.data.db.SpendingDAO +import com.sadig.spendtracker.data.model.Spending import com.sadig.spendtracker.domain.source.local.SpendingDataSource +import javax.inject.Inject + +class SpendingDataSourceImpl @Inject constructor(val spendingDAO: SpendingDAO) : SpendingDataSource { + override fun putSpending(spending: Spending) { + spendingDAO.insertAll(spending) + } -class SpendingDataSourceImpl: SpendingDataSource { } \ No newline at end of file diff --git a/app/src/main/java/com/sadig/spendtracker/di/module/RepositoryComponent.kt b/app/src/main/java/com/sadig/spendtracker/di/module/RepositoryModule.kt similarity index 95% rename from app/src/main/java/com/sadig/spendtracker/di/module/RepositoryComponent.kt rename to app/src/main/java/com/sadig/spendtracker/di/module/RepositoryModule.kt index 7c0c553..6f5cd16 100644 --- a/app/src/main/java/com/sadig/spendtracker/di/module/RepositoryComponent.kt +++ b/app/src/main/java/com/sadig/spendtracker/di/module/RepositoryModule.kt @@ -12,7 +12,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -abstract class RepositoryComponent { +abstract class RepositoryModule { @Singleton @Binds abstract fun bindDataStoreRepository( diff --git a/app/src/main/java/com/sadig/spendtracker/di/module/RoomModule.kt b/app/src/main/java/com/sadig/spendtracker/di/module/RoomModule.kt new file mode 100644 index 0000000..b36fb5c --- /dev/null +++ b/app/src/main/java/com/sadig/spendtracker/di/module/RoomModule.kt @@ -0,0 +1,31 @@ +package com.sadig.spendtracker.di.module + +import android.content.Context +import androidx.room.Room +import com.sadig.spendtracker.data.db.AppDatabase +import com.sadig.spendtracker.data.db.SpendingDAO +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object RoomModule { + + @Provides + @Singleton + fun providesDb(@ApplicationContext context: Context): AppDatabase { + return Room.databaseBuilder( + context, + AppDatabase::class.java, "spendings" + ).build() + } + @Provides + @Singleton + fun provides(db: AppDatabase): SpendingDAO { + return db.spendingDao() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sadig/spendtracker/di/module/SpendTrackerComponent.kt b/app/src/main/java/com/sadig/spendtracker/di/module/SpendTrackerModule.kt similarity index 78% rename from app/src/main/java/com/sadig/spendtracker/di/module/SpendTrackerComponent.kt rename to app/src/main/java/com/sadig/spendtracker/di/module/SpendTrackerModule.kt index d13981d..791775f 100644 --- a/app/src/main/java/com/sadig/spendtracker/di/module/SpendTrackerComponent.kt +++ b/app/src/main/java/com/sadig/spendtracker/di/module/SpendTrackerModule.kt @@ -5,12 +5,15 @@ import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.PreferenceDataStoreFactory import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.preferencesDataStoreFile +import com.sadig.spendtracker.data.db.SpendingDAO import com.sadig.spendtracker.data.source.local.SpendingDataSourceImpl import com.sadig.spendtracker.data.source.local.UserPreferencesDataSourceImpl import com.sadig.spendtracker.domain.repository.DataStoreRepository +import com.sadig.spendtracker.domain.repository.SpendingRepository import com.sadig.spendtracker.domain.source.local.SpendingDataSource import com.sadig.spendtracker.domain.source.local.UserPreferencesDataSource import com.sadig.spendtracker.domain.usecase.PutCurrencyInteractor +import com.sadig.spendtracker.domain.usecase.PutSpendingInteractor import com.sadig.spendtracker.domain.usecase.ReadCurrencyInteractor import dagger.Module import dagger.Provides @@ -21,7 +24,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) -object SpendTrackerComponent { +object SpendTrackerModule { @Provides @Singleton @@ -33,7 +36,9 @@ object SpendTrackerComponent { @Provides @Singleton - fun providesSpendingDataSource(): SpendingDataSource = SpendingDataSourceImpl() + fun providesSpendingDataSource(spendingDAO: SpendingDAO): SpendingDataSource { + return SpendingDataSourceImpl(spendingDAO) + } @Provides @Singleton @@ -52,4 +57,10 @@ object SpendTrackerComponent { fun providesPutCurrencyInteractor(dataStoreRepository: DataStoreRepository): PutCurrencyInteractor { return PutCurrencyInteractor(dataStoreRepository) } + + @Provides + @Singleton + fun providesPutSpendingInteractor(spendingRepository: SpendingRepository): PutSpendingInteractor { + return PutSpendingInteractor(spendingRepository) + } } diff --git a/app/src/main/java/com/sadig/spendtracker/domain/repository/SpendingRepository.kt b/app/src/main/java/com/sadig/spendtracker/domain/repository/SpendingRepository.kt index a90d349..b8f5c29 100644 --- a/app/src/main/java/com/sadig/spendtracker/domain/repository/SpendingRepository.kt +++ b/app/src/main/java/com/sadig/spendtracker/domain/repository/SpendingRepository.kt @@ -1,4 +1,7 @@ package com.sadig.spendtracker.domain.repository +import com.sadig.spendtracker.data.model.Spending + interface SpendingRepository { + fun putSpending(spending: Spending) } \ No newline at end of file diff --git a/app/src/main/java/com/sadig/spendtracker/domain/source/local/SpendingDataSource.kt b/app/src/main/java/com/sadig/spendtracker/domain/source/local/SpendingDataSource.kt index bf6a897..beae6db 100644 --- a/app/src/main/java/com/sadig/spendtracker/domain/source/local/SpendingDataSource.kt +++ b/app/src/main/java/com/sadig/spendtracker/domain/source/local/SpendingDataSource.kt @@ -1,4 +1,7 @@ package com.sadig.spendtracker.domain.source.local +import com.sadig.spendtracker.data.model.Spending + interface SpendingDataSource { + fun putSpending(spending: Spending) } \ No newline at end of file diff --git a/app/src/main/java/com/sadig/spendtracker/domain/usecase/PutSpendingInteractor.kt b/app/src/main/java/com/sadig/spendtracker/domain/usecase/PutSpendingInteractor.kt new file mode 100644 index 0000000..739b4f2 --- /dev/null +++ b/app/src/main/java/com/sadig/spendtracker/domain/usecase/PutSpendingInteractor.kt @@ -0,0 +1,11 @@ +package com.sadig.spendtracker.domain.usecase + +import com.sadig.spendtracker.data.model.Spending +import com.sadig.spendtracker.domain.repository.SpendingRepository +import javax.inject.Inject + +class PutSpendingInteractor @Inject constructor(private val spendingRepository: SpendingRepository) { + operator fun invoke(spending: Spending) { + spendingRepository.putSpending(spending = spending) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sadig/spendtracker/ui/viewmodel/HomeViewModel.kt b/app/src/main/java/com/sadig/spendtracker/ui/viewmodel/HomeViewModel.kt index 70dca35..fa54e33 100644 --- a/app/src/main/java/com/sadig/spendtracker/ui/viewmodel/HomeViewModel.kt +++ b/app/src/main/java/com/sadig/spendtracker/ui/viewmodel/HomeViewModel.kt @@ -4,11 +4,13 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.sadig.spendtracker.data.model.Spending import com.sadig.spendtracker.data.source.local.UserPreferencesDataSourceImpl import com.sadig.spendtracker.domain.repository.DataStoreRepository import com.sadig.spendtracker.domain.repository.SpendingRepository import com.sadig.spendtracker.domain.source.local.UserPreferencesDataSource import com.sadig.spendtracker.domain.usecase.PutCurrencyInteractor +import com.sadig.spendtracker.domain.usecase.PutSpendingInteractor import com.sadig.spendtracker.domain.usecase.ReadCurrencyInteractor import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.Flow @@ -21,7 +23,8 @@ import javax.inject.Inject @HiltViewModel class HomeViewModel @Inject constructor( val putCurrencyInteractor: PutCurrencyInteractor, - val getCurrencyInteractor: ReadCurrencyInteractor + val getCurrencyInteractor: ReadCurrencyInteractor, + val putSpendingInteractor: PutSpendingInteractor ) : ViewModel() { private val _shouldShowAddDialog: MutableStateFlow = MutableStateFlow(false) val shouldShowAddDialog: StateFlow = _shouldShowAddDialog @@ -37,6 +40,13 @@ class HomeViewModel @Inject constructor( fun saveSpending(title: String, description: String, amount: Double, date: Date) = viewModelScope.launch { _shouldShowAddDialog.emit(false) + val spending = Spending( + title = title, + description = description, + amount = amount, + date = date + ) + putSpendingInteractor(spending) } fun onEvent(event: EventType) = viewModelScope.launch { diff --git a/build.gradle.kts b/build.gradle.kts index 5654855..6a43035 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,5 +3,4 @@ plugins { id("com.android.application") version "8.1.0" apply false id("org.jetbrains.kotlin.android") version "1.8.10" apply false id("com.google.dagger.hilt.android") version "2.44" apply false - } \ No newline at end of file From d943ff1c9cfc17de879c8675434dc1f0d086015b Mon Sep 17 00:00:00 2001 From: sadighasanzade Date: Fri, 10 Nov 2023 03:11:56 +0200 Subject: [PATCH 3/5] KAPT is migrated to KSP --- .idea/kotlinc.xml | 2 +- app/build.gradle.kts | 22 ++++++++++--------- .../com/sadig/spendtracker/SpendTracker.kt | 10 +++++++-- .../sadig/spendtracker/data/model/Spending.kt | 4 ++++ .../type_converter/SpendingTypeConverter.kt | 16 ++++++++++++++ .../data/repository/SpendingRepositoryImpl.kt | 2 +- .../source/local/SpendingDataSourceImpl.kt | 2 +- .../spendtracker/di/module/RoomModule.kt | 4 ++-- .../domain/repository/SpendingRepository.kt | 2 +- .../domain/source/local/SpendingDataSource.kt | 2 +- .../domain/usecase/PutSpendingInteractor.kt | 2 +- build.gradle.kts | 6 +++-- 12 files changed, 52 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/com/sadig/spendtracker/data/model/type_converter/SpendingTypeConverter.kt diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 0fc3113..fdf8d99 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 5ed5360..8319968 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -3,7 +3,7 @@ plugins { id("org.jetbrains.kotlin.android") id("com.google.dagger.hilt.android") id ("kotlin-android") - id ("kotlin-kapt") + id("com.google.devtools.ksp") } @@ -18,6 +18,7 @@ android { versionCode = 1 versionName = "1.0" + multiDexEnabled = true testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { useSupportLibrary = true @@ -56,7 +57,7 @@ android { compose = true } composeOptions { - kotlinCompilerExtensionVersion = "1.4.3" + kotlinCompilerExtensionVersion = "1.5.0" } packaging { resources { @@ -69,7 +70,7 @@ dependencies { implementation("androidx.core:core-ktx:1.9.0") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2") - implementation("androidx.activity:activity-compose:1.7.2") + implementation("androidx.activity:activity-compose:1.8.0") implementation(platform("androidx.compose:compose-bom:2023.03.00")) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") @@ -89,11 +90,12 @@ dependencies { implementation("androidx.room:room-runtime:$room_version") annotationProcessor("androidx.room:room-compiler:$room_version") implementation("androidx.room:room-ktx:$room_version") - kapt("androidx.room:room-compiler:$room_version") + ksp("androidx.room:room-compiler:$room_version") - - implementation("com.google.dagger:hilt-android:2.44") - kapt("com.google.dagger:hilt-android-compiler:2.44") + val hilt_version = "2.48" + implementation("com.google.dagger:hilt-android:$hilt_version") + ksp("com.google.dagger:dagger-compiler:$hilt_version") // Dagger compiler + ksp("com.google.dagger:hilt-compiler:$hilt_version") implementation("io.coil-kt:coil-compose:2.4.0") val nav_version = "2.7.4" @@ -102,8 +104,8 @@ dependencies { implementation("androidx.navigation:navigation-compose:$nav_version") implementation("androidx.datastore:datastore-preferences:1.0.0") + implementation("androidx.multidex:multidex:2.0.1") + } -kapt { - correctErrorTypes = true -} + diff --git a/app/src/main/java/com/sadig/spendtracker/SpendTracker.kt b/app/src/main/java/com/sadig/spendtracker/SpendTracker.kt index 2e8f574..cf6c769 100644 --- a/app/src/main/java/com/sadig/spendtracker/SpendTracker.kt +++ b/app/src/main/java/com/sadig/spendtracker/SpendTracker.kt @@ -1,8 +1,14 @@ package com.sadig.spendtracker -import android.app.Application +import android.content.Context +import androidx.multidex.MultiDex +import androidx.multidex.MultiDexApplication import dagger.hilt.android.HiltAndroidApp @HiltAndroidApp -class SpendTracker: Application() { +class SpendTracker: MultiDexApplication() { + override fun attachBaseContext(base: Context?) { + super.attachBaseContext(base) + MultiDex.install(base) + } } \ No newline at end of file diff --git a/app/src/main/java/com/sadig/spendtracker/data/model/Spending.kt b/app/src/main/java/com/sadig/spendtracker/data/model/Spending.kt index 2f312ca..d030bd8 100644 --- a/app/src/main/java/com/sadig/spendtracker/data/model/Spending.kt +++ b/app/src/main/java/com/sadig/spendtracker/data/model/Spending.kt @@ -3,9 +3,13 @@ package com.sadig.spendtracker.data.model import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey +import androidx.room.TypeConverter +import androidx.room.TypeConverters +import com.sadig.spendtracker.data.model.type_converter.SpendingTypeConverter import java.util.Date @Entity +@TypeConverters(SpendingTypeConverter::class) data class Spending( @ColumnInfo(name = "title") val title: String, @ColumnInfo(name = "description") val description: String?, diff --git a/app/src/main/java/com/sadig/spendtracker/data/model/type_converter/SpendingTypeConverter.kt b/app/src/main/java/com/sadig/spendtracker/data/model/type_converter/SpendingTypeConverter.kt new file mode 100644 index 0000000..cbc580f --- /dev/null +++ b/app/src/main/java/com/sadig/spendtracker/data/model/type_converter/SpendingTypeConverter.kt @@ -0,0 +1,16 @@ +package com.sadig.spendtracker.data.model.type_converter + +import androidx.room.TypeConverter +import java.util.Date + +public class SpendingTypeConverter { + @TypeConverter + fun toDate(dateLong: Long?): Date? { + return dateLong?.let { Date(it) } + } + + @TypeConverter + fun fromDate(date: Date?): Long? { + return date?.time + } +} \ No newline at end of file diff --git a/app/src/main/java/com/sadig/spendtracker/data/repository/SpendingRepositoryImpl.kt b/app/src/main/java/com/sadig/spendtracker/data/repository/SpendingRepositoryImpl.kt index 427dd51..dd6efdf 100644 --- a/app/src/main/java/com/sadig/spendtracker/data/repository/SpendingRepositoryImpl.kt +++ b/app/src/main/java/com/sadig/spendtracker/data/repository/SpendingRepositoryImpl.kt @@ -7,7 +7,7 @@ import javax.inject.Inject class SpendingRepositoryImpl @Inject constructor(val spendingDataSource: SpendingDataSource) : SpendingRepository { - override fun putSpending(spending: Spending) { + override suspend fun putSpending(spending: Spending) { spendingDataSource.putSpending(spending = spending) } } \ No newline at end of file diff --git a/app/src/main/java/com/sadig/spendtracker/data/source/local/SpendingDataSourceImpl.kt b/app/src/main/java/com/sadig/spendtracker/data/source/local/SpendingDataSourceImpl.kt index 8c31829..43c2d36 100644 --- a/app/src/main/java/com/sadig/spendtracker/data/source/local/SpendingDataSourceImpl.kt +++ b/app/src/main/java/com/sadig/spendtracker/data/source/local/SpendingDataSourceImpl.kt @@ -6,7 +6,7 @@ import com.sadig.spendtracker.domain.source.local.SpendingDataSource import javax.inject.Inject class SpendingDataSourceImpl @Inject constructor(val spendingDAO: SpendingDAO) : SpendingDataSource { - override fun putSpending(spending: Spending) { + override suspend fun putSpending(spending: Spending) { spendingDAO.insertAll(spending) } diff --git a/app/src/main/java/com/sadig/spendtracker/di/module/RoomModule.kt b/app/src/main/java/com/sadig/spendtracker/di/module/RoomModule.kt index b36fb5c..3833c20 100644 --- a/app/src/main/java/com/sadig/spendtracker/di/module/RoomModule.kt +++ b/app/src/main/java/com/sadig/spendtracker/di/module/RoomModule.kt @@ -20,8 +20,8 @@ object RoomModule { fun providesDb(@ApplicationContext context: Context): AppDatabase { return Room.databaseBuilder( context, - AppDatabase::class.java, "spendings" - ).build() + AppDatabase::class.java, "spendings_db" + ).allowMainThreadQueries().build() } @Provides @Singleton diff --git a/app/src/main/java/com/sadig/spendtracker/domain/repository/SpendingRepository.kt b/app/src/main/java/com/sadig/spendtracker/domain/repository/SpendingRepository.kt index b8f5c29..76f7227 100644 --- a/app/src/main/java/com/sadig/spendtracker/domain/repository/SpendingRepository.kt +++ b/app/src/main/java/com/sadig/spendtracker/domain/repository/SpendingRepository.kt @@ -3,5 +3,5 @@ package com.sadig.spendtracker.domain.repository import com.sadig.spendtracker.data.model.Spending interface SpendingRepository { - fun putSpending(spending: Spending) + suspend fun putSpending(spending: Spending) } \ No newline at end of file diff --git a/app/src/main/java/com/sadig/spendtracker/domain/source/local/SpendingDataSource.kt b/app/src/main/java/com/sadig/spendtracker/domain/source/local/SpendingDataSource.kt index beae6db..dbae2ff 100644 --- a/app/src/main/java/com/sadig/spendtracker/domain/source/local/SpendingDataSource.kt +++ b/app/src/main/java/com/sadig/spendtracker/domain/source/local/SpendingDataSource.kt @@ -3,5 +3,5 @@ package com.sadig.spendtracker.domain.source.local import com.sadig.spendtracker.data.model.Spending interface SpendingDataSource { - fun putSpending(spending: Spending) + suspend fun putSpending(spending: Spending) } \ No newline at end of file diff --git a/app/src/main/java/com/sadig/spendtracker/domain/usecase/PutSpendingInteractor.kt b/app/src/main/java/com/sadig/spendtracker/domain/usecase/PutSpendingInteractor.kt index 739b4f2..9e2f3b5 100644 --- a/app/src/main/java/com/sadig/spendtracker/domain/usecase/PutSpendingInteractor.kt +++ b/app/src/main/java/com/sadig/spendtracker/domain/usecase/PutSpendingInteractor.kt @@ -5,7 +5,7 @@ import com.sadig.spendtracker.domain.repository.SpendingRepository import javax.inject.Inject class PutSpendingInteractor @Inject constructor(private val spendingRepository: SpendingRepository) { - operator fun invoke(spending: Spending) { + operator suspend fun invoke(spending: Spending) { spendingRepository.putSpending(spending = spending) } } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 6a43035..f46356e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,8 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { id("com.android.application") version "8.1.0" apply false - id("org.jetbrains.kotlin.android") version "1.8.10" apply false - id("com.google.dagger.hilt.android") version "2.44" apply false + id("org.jetbrains.kotlin.android") version "1.9.0" apply false + id("com.google.dagger.hilt.android") version "2.48.1" apply false + id("com.google.devtools.ksp") version "1.9.0-1.0.12" apply false + } \ No newline at end of file From 2a8fc05b685b99dd635354961596a621d18e4b2c Mon Sep 17 00:00:00 2001 From: sadighasanzade Date: Sat, 11 Nov 2023 06:42:35 +0200 Subject: [PATCH 4/5] First version is completed --- .idea/inspectionProfiles/Project_Default.xml | 9 + app/build.gradle.kts | 3 +- .../com/sadig/spendtracker/MainActivity.kt | 9 +- .../sadig/spendtracker/data/db/SpendingDAO.kt | 6 + .../data/model/query/GetSpendingQuery.kt | 6 + .../data/repository/SpendingRepositoryImpl.kt | 14 ++ .../source/local/SpendingDataSourceImpl.kt | 9 + .../di/module/SpendTrackerModule.kt | 7 + .../domain/repository/SpendingRepository.kt | 3 + .../domain/source/local/SpendingDataSource.kt | 2 + .../domain/usecase/GetSpendingsInteractor.kt | 45 +++++ .../spendtracker/ui/screens/MainScreen.kt | 169 +++++++++++----- .../ui/screens/MonthlySpendings.kt | 14 +- .../ui/screens/VizualizationScreen.kt | 188 +++++++++++++++++- .../com/sadig/spendtracker/ui/theme/Color.kt | 1 + .../com/sadig/spendtracker/ui/theme/Theme.kt | 4 +- .../ui/viewmodel/HomeViewModel.kt | 32 ++- .../com/sadig/spendtracker/utils/Utils.kt | 14 ++ 18 files changed, 469 insertions(+), 66 deletions(-) create mode 100644 app/src/main/java/com/sadig/spendtracker/data/model/query/GetSpendingQuery.kt create mode 100644 app/src/main/java/com/sadig/spendtracker/domain/usecase/GetSpendingsInteractor.kt create mode 100644 app/src/main/java/com/sadig/spendtracker/utils/Utils.kt diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 103e00c..44ca2d9 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -3,30 +3,39 @@