Add a basic room database system for further features

This commit is contained in:
Nishant Mishra
2025-07-03 20:21:24 +05:30
parent 9e3978f261
commit 32e3a05df8
12 changed files with 171 additions and 18 deletions

View File

@@ -5,6 +5,7 @@ plugins {
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.ksp)
}
android {
@@ -59,6 +60,10 @@ dependencies {
implementation(libs.androidx.navigation3.runtime)
implementation(libs.androidx.navigation3.ui)
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.room.ktx)
ksp(libs.androidx.room.compiler)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)

View File

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

View File

@@ -0,0 +1,13 @@
package org.nsh07.pomodoro
import android.app.Application
import org.nsh07.pomodoro.data.AppContainer
import org.nsh07.pomodoro.data.DefaultAppContainer
class TomatoApplication : Application() {
lateinit var container: AppContainer
override fun onCreate() {
super.onCreate()
container = DefaultAppContainer(this)
}
}

View File

@@ -0,0 +1,17 @@
package org.nsh07.pomodoro.data
import android.content.Context
interface AppContainer {
val appPreferencesRepository: AppPreferenceRepository
}
class DefaultAppContainer(context: Context) : AppContainer {
override val appPreferencesRepository: AppPreferenceRepository by lazy {
AppPreferenceRepository(
AppDatabase.getDatabase(context).preferenceDao()
)
}
}

View File

@@ -0,0 +1,29 @@
package org.nsh07.pomodoro.data
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@Database(
entities = [IntPreference::class],
version = 1
)
abstract class AppDatabase : RoomDatabase() {
abstract fun preferenceDao(): PreferenceDao
companion object {
@Volatile
private var Instance: AppDatabase? = null
fun getDatabase(context: Context): AppDatabase {
return Instance ?: synchronized(this) {
Room.databaseBuilder(context, AppDatabase::class.java, "app_database")
.build()
.also { Instance = it }
}
}
}
}

View File

@@ -0,0 +1,11 @@
package org.nsh07.pomodoro.data
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "int_preference")
data class IntPreference(
@PrimaryKey
val key: String,
val value: Int
)

View File

@@ -0,0 +1,19 @@
package org.nsh07.pomodoro.data
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import kotlinx.coroutines.flow.Flow
@Dao
interface PreferenceDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertIntPreference(preference: IntPreference)
@Query("DELETE FROM int_preference")
suspend fun resetIntPreferences()
@Query("SELECT value FROM int_preference WHERE `key` = :key")
fun getIntPreference(key: String): Flow<Int?>
}

View File

@@ -0,0 +1,31 @@
package org.nsh07.pomodoro.data
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext
interface PreferencesRepository {
suspend fun saveIntPreference(key: String, value: Int)
fun getIntPreference(key: String): Flow<Int?>
suspend fun resetSettings()
}
class AppPreferenceRepository(
private val preferenceDao: PreferenceDao,
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
): PreferencesRepository {
override suspend fun saveIntPreference(key: String, value: Int) =
withContext(ioDispatcher) {
preferenceDao.insertIntPreference(IntPreference(key, value))
}
override fun getIntPreference(key: String): Flow<Int?> =
preferenceDao.getIntPreference(key)
override suspend fun resetSettings() = withContext(ioDispatcher) {
preferenceDao.resetIntPreferences()
}
}

View File

@@ -40,7 +40,7 @@ import org.nsh07.pomodoro.ui.viewModel.UiViewModel
@Composable
fun AppScreen(
modifier: Modifier = Modifier,
viewModel: UiViewModel = viewModel()
viewModel: UiViewModel = viewModel(factory = UiViewModel.Factory)
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val remainingTime by viewModel.time.collectAsStateWithLifecycle()

View File

@@ -2,7 +2,11 @@ package org.nsh07.pomodoro.ui.viewModel
import android.os.SystemClock
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
@@ -10,10 +14,14 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.nsh07.pomodoro.TomatoApplication
import org.nsh07.pomodoro.data.AppPreferenceRepository
import java.util.Locale
import kotlin.math.ceil
class UiViewModel : ViewModel() {
class UiViewModel(
private val preferenceRepository: AppPreferenceRepository
) : ViewModel() {
val focusTime = 25 * 60 * 1000
val shortBreakTime = 5 * 60 * 1000
val longBreakTime = 15 * 60 * 1000
@@ -153,6 +161,8 @@ class UiViewModel : ViewModel() {
)
}
}
toggleTimer()
} else {
_uiState.update { currentState ->
currentState.copy(
@@ -172,4 +182,14 @@ class UiViewModel : ViewModel() {
val sec = (ceil(t / 1000.0).toInt() % 60)
return String.format(locale = Locale.getDefault(), "%02d:%02d", min, sec)
}
companion object {
val Factory: ViewModelProvider.Factory = viewModelFactory {
initializer {
val application = (this[APPLICATION_KEY] as TomatoApplication)
val appPreferenceRepository = application.container.appPreferencesRepository
UiViewModel(preferenceRepository = appPreferenceRepository)
}
}
}
}

View File

@@ -4,4 +4,5 @@ plugins {
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.compose) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.ksp) apply false
}

View File

@@ -1,37 +1,43 @@
[versions]
activityCompose = "1.10.1"
agp = "8.11.0"
kotlin = "2.2.0"
composeBom = "2025.06.02"
coreKtx = "1.16.0"
espressoCore = "3.6.1"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
kotlin = "2.2.0"
ksp = "2.2.0-2.0.2"
lifecycleRuntimeKtx = "2.9.1"
activityCompose = "1.10.1"
composeBom = "2025.06.02"
navigation3Runtime = "1.0.0-alpha05"
room = "2.7.2"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel", version.ref = "lifecycleRuntimeKtx" }
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" }
androidx-navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "navigation3Runtime" }
androidx-navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "navigation3Runtime" }
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" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom-alpha", version.ref = "composeBom" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel", version.ref = "lifecycleRuntimeKtx" }
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "navigation3Runtime" }
androidx-navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "navigation3Runtime" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin"}
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }