diff --git a/Ledger/app/build.gradle.kts b/Ledger/app/build.gradle.kts index 63bb144..c15a50c 100644 --- a/Ledger/app/build.gradle.kts +++ b/Ledger/app/build.gradle.kts @@ -1,6 +1,8 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) + id("com.google.devtools.ksp") version "1.9.0-1.0.12" + id("org.jetbrains.kotlin.plugin.serialization") version "1.9.10" } android { @@ -50,6 +52,11 @@ android { } dependencies { + implementation(libs.androidx.room.runtime) + implementation(libs.androidx.room.ktx) + ksp(libs.androidx.room.compiler) + + //implementation(libs.androidx.room.common) implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) @@ -59,9 +66,8 @@ dependencies { implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) - implementation(libs.androidx.room.common) - implementation(libs.androidx.room.ktx) implementation(libs.androidx.navigation.compose) + implementation(libs.androidx.lifecycle.runtime.compose.android) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/Ledger/app/src/main/AndroidManifest.xml b/Ledger/app/src/main/AndroidManifest.xml index f6b6eec..4fdf835 100644 --- a/Ledger/app/src/main/AndroidManifest.xml +++ b/Ledger/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> > { return ledgerDao.getAllEntries().map { - it.map {entry -> Entry(entry._id, entry.name, entry.amount, entry.date, entry.categoryName) } + it.map {entry -> Entry(entry._id, entry.name, entry.amount, entry.date, entry.categoryID) } } } suspend fun findEntryById(id: Int): Entry { - val entry = LedgerDao.findEntryById(id) + val entry = ledgerDao.findEntryById(id) return Entry( - entry._id, entry.name, entry.amount, entry.date, entry.categoryName + entry._id, entry.name, entry.amount, entry.date, entry.categoryID ) } suspend fun insertEntry(entry: Entry) { - LedgerDao.addEntry(EntryEntity(_id=0, entry.name, entry.amount, entry.date, entry.categoryName)) + ledgerDao.addEntry(EntryEntity(_id=0, entry.name, entry.amount, entry.date, entry.categoryID)) } suspend fun updateEntry(entry: Entry) { - LedgerDao.updateEntry(EntryEntity(entry.id, entry.name, entry.amount, entry.date, entry.categoryName)) + ledgerDao.updateEntry(EntryEntity(entry.id, entry.name, entry.amount, entry.date, entry.categoryID)) + } + suspend fun deleteEntry(entry: Entry) { + ledgerDao.deleteEntry(EntryEntity(_id = entry.id, entry.name, entry.amount, entry.date, entry.categoryID)) } - suspend fun deletePokemon(entry: Entry) { - LedgerDao.deleteEntry(EntryEntity(_id = entry.id, entry.name, entry.amount, entry.date, entry.categoryName)) - }*/ } \ No newline at end of file diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/data/db/Category/CategoryEntity.kt b/Ledger/app/src/main/java/at/xaxa/ledger/data/db/Category/CategoryEntity.kt index d1aed3c..ec3649b 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/data/db/Category/CategoryEntity.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/data/db/Category/CategoryEntity.kt @@ -7,6 +7,6 @@ import androidx.room.PrimaryKey data class CategoryEntity( @PrimaryKey(autoGenerate = true) val _id: Int = 0, - val categoryname: String, + val categoryName: String, val icon: Int ) \ No newline at end of file diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/data/db/DatabaseInstance.kt b/Ledger/app/src/main/java/at/xaxa/ledger/data/db/DatabaseInstance.kt deleted file mode 100644 index 2ca0847..0000000 --- a/Ledger/app/src/main/java/at/xaxa/ledger/data/db/DatabaseInstance.kt +++ /dev/null @@ -1,20 +0,0 @@ -import android.content.Context -import androidx.room.Room -import at.xaxa.ledger.data.db.LedgerDatabase - -object DatabaseInstance { - @Volatile - private var INSTANCE: LedgerDatabase? = null - - fun getDatabase(context: Context): LedgerDatabase { - return INSTANCE ?: synchronized(this) { - val instance = Room.databaseBuilder( - context.applicationContext, - LedgerDatabase::class.java, - "ledger_database" - ).build() - INSTANCE = instance - instance - } - } -} \ No newline at end of file diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/data/db/Entry/EntryEntity.kt b/Ledger/app/src/main/java/at/xaxa/ledger/data/db/Entry/EntryEntity.kt index aaa68c2..a263807 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/data/db/Entry/EntryEntity.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/data/db/Entry/EntryEntity.kt @@ -3,12 +3,12 @@ package at.xaxa.ledger.data.db.Entry import androidx.room.Entity import androidx.room.PrimaryKey -@Entity(tableName = "transaction") +@Entity(tableName = "_transaction") data class EntryEntity( @PrimaryKey(autoGenerate = true) val _id: Int = 0, val name: String, val amount: Float, val date: Long, - val categoryName: Int + val categoryID: Int, ) \ No newline at end of file diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/data/db/EntryCategoryRelation.kt b/Ledger/app/src/main/java/at/xaxa/ledger/data/db/EntryCategoryRelation.kt new file mode 100644 index 0000000..ec3fc20 --- /dev/null +++ b/Ledger/app/src/main/java/at/xaxa/ledger/data/db/EntryCategoryRelation.kt @@ -0,0 +1,15 @@ +package at.xaxa.ledger.data.db + +import androidx.room.Embedded +import androidx.room.Relation +import at.xaxa.ledger.data.db.Entry.EntryEntity +import at.xaxa.ledger.data.db.Category.CategoryEntity + +data class EntryCategoryRelation( + @Embedded val entry: EntryEntity, + @Relation( + parentColumn = "categoryID", + entityColumn = "_id" + ) + val category: CategoryEntity +) diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/data/db/LedgerDao.kt b/Ledger/app/src/main/java/at/xaxa/ledger/data/db/LedgerDao.kt index de68f4c..2cc5db6 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/data/db/LedgerDao.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/data/db/LedgerDao.kt @@ -5,6 +5,7 @@ import androidx.room.Delete import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +import androidx.room.Transaction import androidx.room.Update import at.xaxa.ledger.data.db.Category.CategoryEntity import at.xaxa.ledger.data.db.Entry.EntryEntity @@ -13,20 +14,24 @@ import kotlinx.coroutines.flow.Flow @Dao interface LedgerDao { @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun addCategory(entryEntity: CategoryEntity) + suspend fun addCategory(categoryEntity: CategoryEntity) @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun addEntry(entryEntity: EntryEntity) + @Transaction + @Query("SELECT * FROM _transaction WHERE categoryID = :categoryID") + suspend fun getEntryWithCategoryID(categoryID: String): List + @Update - suspend fun updateCategory(category: CharCategory) + suspend fun updateCategory(categoryEntity: CategoryEntity) @Update suspend fun updateEntry(entryEntity: EntryEntity) @Delete - suspend fun deleteCategory(category: CharCategory) + suspend fun deleteCategory(categoryEntity: CategoryEntity) @Delete @@ -41,10 +46,11 @@ interface LedgerDao { fun getAllCategory(): Flow> - @Query("SELECT * FROM transaction WHERE _id = :id") + @Query("SELECT * FROM _transaction WHERE _id = :id") suspend fun findEntryById(id: Int) : EntryEntity - @Query("SELECT * FROM transaction") + @Query("SELECT * FROM _transaction") fun getAllEntries(): Flow> + } diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/data/db/LedgerDatabase.kt b/Ledger/app/src/main/java/at/xaxa/ledger/data/db/LedgerDatabase.kt index 5268408..b0a5e80 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/data/db/LedgerDatabase.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/data/db/LedgerDatabase.kt @@ -1,12 +1,34 @@ package at.xaxa.ledger.data.db +import android.content.Context import androidx.room.Database +import androidx.room.Room import androidx.room.RoomDatabase import at.xaxa.ledger.data.db.Category.CategoryEntity import at.xaxa.ledger.data.db.Entry.EntryEntity + @Database(entities = [EntryEntity::class, CategoryEntity::class], version = 1, exportSchema = false) abstract class LedgerDatabase : RoomDatabase() { - /*abstract fun entryDao(): EntryDao - abstract fun categoryDao(): CategoryDao*/ + abstract fun ledgerDao(): LedgerDao + + companion object { + @Volatile + private var Instance: LedgerDatabase? = null + + fun getDatabase(context: Context): LedgerDatabase { + return Instance ?: synchronized(this){ + val instance = + Room.databaseBuilder( + context.applicationContext, + LedgerDatabase::class.java, + "ledger_database" + ).fallbackToDestructiveMigration() + .build() + + Instance = instance + return instance + } + } + } } \ No newline at end of file diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/AppViewModelProvider.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/AppViewModelProvider.kt index 51c31cc..c8b83d7 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/AppViewModelProvider.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/AppViewModelProvider.kt @@ -1,5 +1,26 @@ package at.xaxa.ledger.ui -object AppViewModelProvider { +import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY +import androidx.lifecycle.createSavedStateHandle +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import at.xaxa.ledger.LedgerApplication +import at.xaxa.ledger.ui.edit.EditViewModel +import at.xaxa.ledger.ui.home.HomeViewModel + +object AppViewModelProvider { + val Factory = viewModelFactory { + initializer { + HomeViewModel((this[APPLICATION_KEY] as LedgerApplication).entryRepository) + } + + /*initializer { + AddViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as LedgerApplication).entryRepository) + }*/ + + initializer { + EditViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as LedgerApplication).entryRepository) + } + } } \ No newline at end of file diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/edit/EditViewModel.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/edit/EditViewModel.kt index 772c30d..a028e10 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/edit/EditViewModel.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/edit/EditViewModel.kt @@ -1,2 +1,54 @@ package at.xaxa.ledger.ui.edit +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import at.xaxa.ledger.data.Entry +import at.xaxa.ledger.data.EntryRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + + +data class EditUI( + val entry: Entry = Entry(0, "", 0.0f, 0, 0) +) + +class EditViewModel(private val savedStateHandle: SavedStateHandle, + private val entryRepository: EntryRepository) : ViewModel() { + + private val entryId: Int = checkNotNull(savedStateHandle["entryId"]) + + var editUiState by mutableStateOf(EditUI()) + private set + + init { + viewModelScope.launch { + val entry = withContext(Dispatchers.IO) { + entryRepository.findEntryById(entryId) + } + editUiState = EditUI(entry) + } + } + + fun updateEntry(entry: Entry) { + editUiState = editUiState.copy(entry=entry) + } + fun onDeleteEntry(entry: Entry) { + viewModelScope.launch { + entryRepository.deleteEntry(entry) + } + } + fun saveEntry() { + viewModelScope.launch { + entryRepository.updateEntry(editUiState.entry) + } + } + +} \ No newline at end of file diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/home/HomeUI.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/home/HomeUI.kt index dfaaa0a..d5144a6 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/home/HomeUI.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/home/HomeUI.kt @@ -13,17 +13,22 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import at.xaxa.ledger.ui.ButtonSuccess +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel +import at.xaxa.ledger.ui.AppViewModelProvider import at.xaxa.ledger.ui.HeaderCard import at.xaxa.ledger.ui.HorizontalCard @OptIn(ExperimentalFoundationApi::class) @Composable -fun Home(modifier: Modifier = Modifier, onCardClick: (Int) -> Unit) { +fun Home(modifier: Modifier = Modifier, onCardClick: (Int) -> Unit, HomeViewModel : HomeViewModel = viewModel(factory = AppViewModelProvider.Factory)) { + val state by HomeViewModel.entryUIState.entry.collectAsState(initial = emptyList()) + Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally diff --git a/Ledger/app/src/main/java/at/xaxa/ledger/ui/home/HomeViewModel.kt b/Ledger/app/src/main/java/at/xaxa/ledger/ui/home/HomeViewModel.kt index 1928f38..4a04241 100644 --- a/Ledger/app/src/main/java/at/xaxa/ledger/ui/home/HomeViewModel.kt +++ b/Ledger/app/src/main/java/at/xaxa/ledger/ui/home/HomeViewModel.kt @@ -1,2 +1,38 @@ package at.xaxa.ledger.ui.home +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import at.xaxa.ledger.data.Entry +import at.xaxa.ledger.data.EntryRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + + +data class EntryListUIState(val entry: Flow> = flowOf(emptyList())) + +class HomeViewModel(private val repository: EntryRepository): ViewModel() { + var entryUIState by mutableStateOf(EntryListUIState()) + init{ + viewModelScope.launch { + val entries = withContext(Dispatchers.IO){ + repository.getAllEntries() + } + entryUIState = EntryListUIState(entries) + + } + } + + /* val entryUIState = repository.getAllEntries() + .map {EntryListUIState(it)} + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = EntryListUIState(emptyList()) + )*/ +} \ No newline at end of file diff --git a/Ledger/gradle/libs.versions.toml b/Ledger/gradle/libs.versions.toml index e9314ca..64d5eaf 100644 --- a/Ledger/gradle/libs.versions.toml +++ b/Ledger/gradle/libs.versions.toml @@ -9,11 +9,16 @@ lifecycleRuntimeKtx = "2.8.7" activityCompose = "1.9.3" composeBom = "2024.04.01" roomCommon = "2.6.1" +roomCompiler = "2.6.1" roomKtx = "2.6.1" navigationCompose = "2.8.5" +lifecycleRuntimeComposeAndroid = "2.8.7" +roomRuntime = "2.6.1" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomCompiler" } +androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } @@ -30,6 +35,7 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3" androidx-room-common = { group = "androidx.room", name = "room-common", version.ref = "roomCommon" } androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "roomKtx" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } +androidx-lifecycle-runtime-compose-android = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose-android", version.ref = "lifecycleRuntimeComposeAndroid" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }