Merge remote-tracking branch 'origin/Florian'

# Conflicts:
#	Ledger/app/src/main/java/at/xaxa/ledger/ui/home/HomeUI.kt
This commit is contained in:
Xaver 2025-01-14 10:33:15 +01:00
commit 9845a7fd75
16 changed files with 199 additions and 49 deletions

View File

@ -1,6 +1,8 @@
plugins { plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android) 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 { android {
@ -50,6 +52,11 @@ android {
} }
dependencies { 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.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.lifecycle.runtime.ktx)
@ -59,9 +66,8 @@ dependencies {
implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3) implementation(libs.androidx.material3)
implementation(libs.androidx.room.common)
implementation(libs.androidx.room.ktx)
implementation(libs.androidx.navigation.compose) implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.lifecycle.runtime.compose.android)
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.espresso.core)

View File

@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<application <application
android:name=".LedgerApplication"
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"

View File

@ -6,8 +6,8 @@ import at.xaxa.ledger.data.db.LedgerDatabase
class LedgerApplication : Application(){ class LedgerApplication : Application(){
val entryRepository by lazy { val entryRepository by lazy {
/*EntryRepository( EntryRepository(
DatabaseInstance.getDatabase(this).LedgerDao() LedgerDatabase.getDatabase(this).ledgerDao()
)*/ )
} }
} }

View File

@ -5,5 +5,5 @@ class Entry (
val name: String, val name: String,
val amount: Float, val amount: Float,
val date: Long, val date: Long,
val categoryName: Int val categoryID: Int
) )

View File

@ -8,28 +8,28 @@ import kotlinx.coroutines.flow.map
class EntryRepository(private val ledgerDao: LedgerDao){ class EntryRepository(private val ledgerDao: LedgerDao){
/*
fun getAllEntries(): Flow<List<Entry>> { fun getAllEntries(): Flow<List<Entry>> {
return ledgerDao.getAllEntries().map { 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 { suspend fun findEntryById(id: Int): Entry {
val entry = LedgerDao.findEntryById(id) val entry = ledgerDao.findEntryById(id)
return Entry( 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) { 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) { 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))
}*/
} }

View File

@ -7,6 +7,6 @@ import androidx.room.PrimaryKey
data class CategoryEntity( data class CategoryEntity(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
val _id: Int = 0, val _id: Int = 0,
val categoryname: String, val categoryName: String,
val icon: Int val icon: Int
) )

View File

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

View File

@ -3,12 +3,12 @@ package at.xaxa.ledger.data.db.Entry
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
@Entity(tableName = "transaction") @Entity(tableName = "_transaction")
data class EntryEntity( data class EntryEntity(
@PrimaryKey(autoGenerate = true) @PrimaryKey(autoGenerate = true)
val _id: Int = 0, val _id: Int = 0,
val name: String, val name: String,
val amount: Float, val amount: Float,
val date: Long, val date: Long,
val categoryName: Int val categoryID: Int,
) )

View File

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

View File

@ -5,6 +5,7 @@ import androidx.room.Delete
import androidx.room.Insert import androidx.room.Insert
import androidx.room.OnConflictStrategy import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update import androidx.room.Update
import at.xaxa.ledger.data.db.Category.CategoryEntity import at.xaxa.ledger.data.db.Category.CategoryEntity
import at.xaxa.ledger.data.db.Entry.EntryEntity import at.xaxa.ledger.data.db.Entry.EntryEntity
@ -13,20 +14,24 @@ import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface LedgerDao { interface LedgerDao {
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun addCategory(entryEntity: CategoryEntity) suspend fun addCategory(categoryEntity: CategoryEntity)
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun addEntry(entryEntity: EntryEntity) suspend fun addEntry(entryEntity: EntryEntity)
@Transaction
@Query("SELECT * FROM _transaction WHERE categoryID = :categoryID")
suspend fun getEntryWithCategoryID(categoryID: String): List<EntryCategoryRelation>
@Update @Update
suspend fun updateCategory(category: CharCategory) suspend fun updateCategory(categoryEntity: CategoryEntity)
@Update @Update
suspend fun updateEntry(entryEntity: EntryEntity) suspend fun updateEntry(entryEntity: EntryEntity)
@Delete @Delete
suspend fun deleteCategory(category: CharCategory) suspend fun deleteCategory(categoryEntity: CategoryEntity)
@Delete @Delete
@ -41,10 +46,11 @@ interface LedgerDao {
fun getAllCategory(): Flow<List<CategoryEntity>> fun getAllCategory(): Flow<List<CategoryEntity>>
@Query("SELECT * FROM transaction WHERE _id = :id") @Query("SELECT * FROM _transaction WHERE _id = :id")
suspend fun findEntryById(id: Int) : EntryEntity suspend fun findEntryById(id: Int) : EntryEntity
@Query("SELECT * FROM transaction") @Query("SELECT * FROM _transaction")
fun getAllEntries(): Flow<List<EntryEntity>> fun getAllEntries(): Flow<List<EntryEntity>>
} }

View File

@ -1,12 +1,34 @@
package at.xaxa.ledger.data.db package at.xaxa.ledger.data.db
import android.content.Context
import androidx.room.Database import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import at.xaxa.ledger.data.db.Category.CategoryEntity import at.xaxa.ledger.data.db.Category.CategoryEntity
import at.xaxa.ledger.data.db.Entry.EntryEntity import at.xaxa.ledger.data.db.Entry.EntryEntity
@Database(entities = [EntryEntity::class, CategoryEntity::class], version = 1, exportSchema = false) @Database(entities = [EntryEntity::class, CategoryEntity::class], version = 1, exportSchema = false)
abstract class LedgerDatabase : RoomDatabase() { abstract class LedgerDatabase : RoomDatabase() {
/*abstract fun entryDao(): EntryDao abstract fun ledgerDao(): LedgerDao
abstract fun categoryDao(): CategoryDao*/
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
}
}
}
} }

View File

@ -1,5 +1,26 @@
package at.xaxa.ledger.ui 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)
}
}
} }

View File

@ -1,2 +1,54 @@
package at.xaxa.ledger.ui.edit 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)
}
}
}

View File

@ -13,17 +13,22 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.collectAsStateWithLifecycle
import at.xaxa.ledger.ui.ButtonSuccess import androidx.lifecycle.viewmodel.compose.viewModel
import at.xaxa.ledger.ui.AppViewModelProvider
import at.xaxa.ledger.ui.HeaderCard import at.xaxa.ledger.ui.HeaderCard
import at.xaxa.ledger.ui.HorizontalCard import at.xaxa.ledger.ui.HorizontalCard
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @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( Column(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally

View File

@ -1,2 +1,38 @@
package at.xaxa.ledger.ui.home 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<List<Entry>> = 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())
)*/
}

View File

@ -9,11 +9,16 @@ lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.9.3" activityCompose = "1.9.3"
composeBom = "2024.04.01" composeBom = "2024.04.01"
roomCommon = "2.6.1" roomCommon = "2.6.1"
roomCompiler = "2.6.1"
roomKtx = "2.6.1" roomKtx = "2.6.1"
navigationCompose = "2.8.5" navigationCompose = "2.8.5"
lifecycleRuntimeComposeAndroid = "2.8.7"
roomRuntime = "2.6.1"
[libraries] [libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } 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" } junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } 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-common = { group = "androidx.room", name = "room-common", version.ref = "roomCommon" }
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "roomKtx" } 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-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] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }