This commit is contained in:
Rafal Wisniewski
2026-03-20 14:32:47 +01:00
parent f625a6975c
commit 96cdd056a0
16 changed files with 596 additions and 124 deletions

View File

@@ -8,9 +8,11 @@ import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import cc.n0th1ng.tripmoney.data.dao.CategoryDao
import cc.n0th1ng.tripmoney.data.dao.ExchangeRateDao
import cc.n0th1ng.tripmoney.data.dao.ExpenseDao
import cc.n0th1ng.tripmoney.data.dao.TripDao
import cc.n0th1ng.tripmoney.data.entity.Category
import cc.n0th1ng.tripmoney.data.entity.ExchangeRate
import cc.n0th1ng.tripmoney.data.entity.Expense
import cc.n0th1ng.tripmoney.data.entity.Trip
import cc.n0th1ng.tripmoney.utils.Icons
@@ -26,11 +28,12 @@ import java.time.LocalDateTime
import javax.inject.Inject
import javax.inject.Singleton
@Database(entities = [Trip::class, Expense::class, Category::class], version = 1)
@Database(entities = [Trip::class, Expense::class, Category::class, ExchangeRate::class], version = 1)
abstract class TripDatabase : RoomDatabase() {
abstract fun tripDao(): TripDao
abstract fun expenseDao(): ExpenseDao
abstract fun categoryDao(): CategoryDao
abstract fun exchangeRateDao(): ExchangeRateDao
}
@@ -74,6 +77,12 @@ object DatabaseModule {
fun provideCategoryDao(database: TripDatabase): CategoryDao {
return database.categoryDao()
}
@Provides
@Singleton
fun provideExchangeRateDao(database: TripDatabase): ExchangeRateDao {
return database.exchangeRateDao()
}
}

View File

@@ -0,0 +1,22 @@
package cc.n0th1ng.tripmoney.data.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Upsert
import cc.n0th1ng.tripmoney.data.entity.Category
import cc.n0th1ng.tripmoney.data.entity.ExchangeRate
import kotlinx.coroutines.flow.Flow
@Dao
interface ExchangeRateDao {
@Upsert
suspend fun insert(exchangeRate: ExchangeRate)
@Query("SELECT * FROM exchange_rate WHERE id = :id")
suspend fun getById(id: String): ExchangeRate?
@Query("DELETE FROM exchange_rate WHERE DATE(date) < :cutoffDate")
suspend fun deleteOldRates(cutoffDate: String)
}

View File

@@ -16,6 +16,7 @@ interface TripDao {
@Query(
"""
SELECT * FROM trip
ORDER BY DATE(trip.start_date) DESC
"""
)
fun tripsPaged(): PagingSource<Int, Trip>

View File

@@ -0,0 +1,20 @@
package cc.n0th1ng.tripmoney.data.entity
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity("exchange_rate")
data class ExchangeRate(
@PrimaryKey
val id: String,
val base: String,
val target: String,
val rate: Double,
val date: String
) {
companion object {
fun buildKey(base: String, target: String, date: String): String {
return "${base}_${target}_${date}"
}
}
}

View File

@@ -0,0 +1,54 @@
package cc.n0th1ng.tripmoney.data.repository
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.annotation.WorkerThread
import cc.n0th1ng.tripmoney.data.dao.CategoryDao
import cc.n0th1ng.tripmoney.data.dao.ExchangeRateDao
import cc.n0th1ng.tripmoney.data.entity.Category
import cc.n0th1ng.tripmoney.data.entity.ExchangeRate
import cc.n0th1ng.tripmoney.service.ExchangeService
import cc.n0th1ng.tripmoney.utils.Currencies
import kotlinx.coroutines.flow.Flow
import java.time.LocalDate
import javax.inject.Inject
class ExchangeRateRepository @Inject constructor(
private val exchangeRateDao: ExchangeRateDao,
private val exchangeService: ExchangeService
) {
@WorkerThread
suspend fun save(exchangeRate: ExchangeRate) {
exchangeRateDao.insert(exchangeRate)
}
@RequiresApi(Build.VERSION_CODES.O)
suspend fun getRate(base: Currencies, target: Currencies, date: LocalDate): Double {
val id = ExchangeRate.buildKey(base.name, target.name, date.toString())
val cachedRate = exchangeRateDao.getById(id)
return if (cachedRate != null) {
cachedRate.rate
} else {
val rate = exchangeService.getRate(base, target, date)
exchangeRateDao.insert(
ExchangeRate(
id = id,
base = base.name,
target = target.name,
rate = rate,
date = date.toString()
)
)
clearOldRates()
rate
}
}
@RequiresApi(Build.VERSION_CODES.O)
private suspend fun clearOldRates(daysToKeep: Int = 180) {
val cutoffDate = LocalDate.now().minusDays(daysToKeep.toLong()).toString()
exchangeRateDao.deleteOldRates(cutoffDate)
}
}