diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index b4897244..933fbfe3 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -47,6 +47,7 @@ dependencies {
implementation(projects.feature.mission)
implementation(projects.feature.setting)
implementation(projects.feature.webview)
+ implementation(platform(libs.firebase.bom))
implementation(libs.firebase.analytics)
implementation(libs.firebase.crashlytics)
implementation(libs.play.services.ads)
diff --git a/build-logic/src/main/java/com/yapp/convention/TestAndroid.kt b/build-logic/src/main/java/com/yapp/convention/TestAndroid.kt
index 55bb6d48..4e6afa62 100644
--- a/build-logic/src/main/java/com/yapp/convention/TestAndroid.kt
+++ b/build-logic/src/main/java/com/yapp/convention/TestAndroid.kt
@@ -3,9 +3,9 @@ package com.yapp.convention
import org.gradle.api.Project
import org.gradle.kotlin.dsl.dependencies
-
internal fun Project.configureTestAndroid() {
- // feature 모듈에만 테스트 관련 설정 적용
+ configureJUnitAndroid()
+ // feature 모듈에만 UI 테스트 관련 설정 적용
if (path.startsWith(":feature:")) {
configureComposeUiTest()
}
@@ -14,13 +14,21 @@ internal fun Project.configureTestAndroid() {
internal fun Project.configureComposeUiTest() {
val libs = extensions.libs
dependencies {
- // Jetpack Compose UI 테스트용
"androidTestImplementation"(libs.findLibrary("compose-ui-test-junit4").get())
- // 테스트용 AndroidManifest 제공해주는 거 (debug 빌드에서만 사용, 테스트 시 Activity 실행 지원)
"debugImplementation"(libs.findLibrary("compose-ui-test-manifest").get())
- // 테스트를 실제로 돌려주는 실행기
- "androidTestImplementation"(libs.findLibrary("androidx-test-runner").get())
- // JUnit4 기능을 안드로이드 테스트에 연결해주는 어댑터
- "androidTestImplementation"(libs.findLibrary("androidx-test-ext-junit").get())
+ }
+}
+
+@Suppress("UnstableApiUsage")
+internal fun Project.configureJUnitAndroid() {
+ androidExtension.apply {
+ testOptions { unitTests.all { it.useJUnitPlatform() } }
+ defaultConfig { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" }
+
+ val libs = extensions.libs
+ dependencies {
+ "androidTestImplementation"(libs.findLibrary("androidx-test-ext-junit").get())
+ "androidTestImplementation"(libs.findLibrary("androidx-test-runner").get())
+ }
}
}
diff --git a/build-logic/src/main/java/orbit.android.feature.gradle.kts b/build-logic/src/main/java/orbit.android.feature.gradle.kts
index 9fa57170..aa5803c4 100644
--- a/build-logic/src/main/java/orbit.android.feature.gradle.kts
+++ b/build-logic/src/main/java/orbit.android.feature.gradle.kts
@@ -6,12 +6,6 @@ plugins {
id("orbit.android.compose")
}
-android {
- defaultConfig {
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- }
-}
-
configureHiltAndroid()
dependencies {
diff --git a/build.gradle.kts b/build.gradle.kts
index 46e42947..3f1d3f7f 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -7,12 +7,12 @@ plugins {
alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.kotlin.serialization) apply false
alias(libs.plugins.ksp) apply false
+ alias(libs.plugins.room) apply false
alias(libs.plugins.hilt) apply false
alias(libs.plugins.compose.compiler) apply false
alias(libs.plugins.google.service) apply false
alias(libs.plugins.firebase.app.distribution) apply false
alias(libs.plugins.firebase.crashlytics) apply false
-// alias(libs.plugins.sentry) apply false
}
apply {
diff --git a/core/database/.gitignore b/core/database/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/core/database/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts
new file mode 100644
index 00000000..5796214b
--- /dev/null
+++ b/core/database/build.gradle.kts
@@ -0,0 +1,26 @@
+import com.yapp.convention.setNamespace
+
+plugins {
+ id("orbit.android.library")
+ id("androidx.room")
+}
+
+android {
+ setNamespace("core.database")
+
+ sourceSets { getByName("androidTest").assets.srcDir("$projectDir/schemas") }
+}
+
+room {
+ schemaDirectory("$projectDir/schemas")
+}
+
+dependencies {
+ implementation(projects.domain)
+
+ ksp(libs.androidx.room.compiler)
+ implementation(libs.androidx.room.ktx)
+ implementation(libs.androidx.room.runtime)
+
+ androidTestImplementation(libs.androidx.room.testing)
+}
diff --git a/core/database/consumer-rules.pro b/core/database/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/core/database/proguard-rules.pro b/core/database/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/core/database/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/core/database/schemas/com.yapp.database.AlarmDatabase/1.json b/core/database/schemas/com.yapp.database.AlarmDatabase/1.json
new file mode 100644
index 00000000..f700d6a5
--- /dev/null
+++ b/core/database/schemas/com.yapp.database.AlarmDatabase/1.json
@@ -0,0 +1,118 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 1,
+ "identityHash": "d9643e982a8885da158bcd94c55931ff",
+ "entities": [
+ {
+ "tableName": "alarm_database",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `isAm` INTEGER NOT NULL, `hour` INTEGER NOT NULL, `minute` INTEGER NOT NULL, `second` INTEGER NOT NULL, `repeatDays` INTEGER NOT NULL, `isHolidayAlarmOff` INTEGER NOT NULL, `isSnoozeEnabled` INTEGER NOT NULL, `snoozeInterval` INTEGER NOT NULL, `snoozeCount` INTEGER NOT NULL, `isVibrationEnabled` INTEGER NOT NULL, `isSoundEnabled` INTEGER NOT NULL, `soundUri` TEXT NOT NULL, `soundVolume` INTEGER NOT NULL, `isAlarmActive` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAm",
+ "columnName": "isAm",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hour",
+ "columnName": "hour",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "minute",
+ "columnName": "minute",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "second",
+ "columnName": "second",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatDays",
+ "columnName": "repeatDays",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isHolidayAlarmOff",
+ "columnName": "isHolidayAlarmOff",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isSnoozeEnabled",
+ "columnName": "isSnoozeEnabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "snoozeInterval",
+ "columnName": "snoozeInterval",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "snoozeCount",
+ "columnName": "snoozeCount",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isVibrationEnabled",
+ "columnName": "isVibrationEnabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isSoundEnabled",
+ "columnName": "isSoundEnabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "soundUri",
+ "columnName": "soundUri",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "soundVolume",
+ "columnName": "soundVolume",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAlarmActive",
+ "columnName": "isAlarmActive",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd9643e982a8885da158bcd94c55931ff')"
+ ]
+ }
+}
diff --git a/core/database/schemas/com.yapp.database.AlarmDatabase/2.json b/core/database/schemas/com.yapp.database.AlarmDatabase/2.json
new file mode 100644
index 00000000..6e6e50a1
--- /dev/null
+++ b/core/database/schemas/com.yapp.database.AlarmDatabase/2.json
@@ -0,0 +1,127 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 2,
+ "identityHash": "557f9b1e0c2913a691c2aed7587e243c",
+ "entities": [
+ {
+ "tableName": "alarm_database",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `isAm` INTEGER NOT NULL, `hour` INTEGER NOT NULL, `minute` INTEGER NOT NULL, `second` INTEGER NOT NULL, `repeatDays` INTEGER NOT NULL, `isHolidayAlarmOff` INTEGER NOT NULL, `isSnoozeEnabled` INTEGER NOT NULL, `snoozeInterval` INTEGER NOT NULL, `snoozeCount` INTEGER NOT NULL, `isVibrationEnabled` INTEGER NOT NULL, `isSoundEnabled` INTEGER NOT NULL, `soundUri` TEXT NOT NULL, `soundVolume` INTEGER NOT NULL, `isAlarmActive` INTEGER NOT NULL, `missionType` INTEGER NOT NULL, `missionCount` INTEGER NOT NULL)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAm",
+ "columnName": "isAm",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "hour",
+ "columnName": "hour",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "minute",
+ "columnName": "minute",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "second",
+ "columnName": "second",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "repeatDays",
+ "columnName": "repeatDays",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isHolidayAlarmOff",
+ "columnName": "isHolidayAlarmOff",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isSnoozeEnabled",
+ "columnName": "isSnoozeEnabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "snoozeInterval",
+ "columnName": "snoozeInterval",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "snoozeCount",
+ "columnName": "snoozeCount",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isVibrationEnabled",
+ "columnName": "isVibrationEnabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isSoundEnabled",
+ "columnName": "isSoundEnabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "soundUri",
+ "columnName": "soundUri",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "soundVolume",
+ "columnName": "soundVolume",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "isAlarmActive",
+ "columnName": "isAlarmActive",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "missionType",
+ "columnName": "missionType",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "missionCount",
+ "columnName": "missionCount",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ }
+ }
+ ],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '557f9b1e0c2913a691c2aed7587e243c')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/core/database/src/androidTest/java/com/yapp/database/MigrationTest.kt b/core/database/src/androidTest/java/com/yapp/database/MigrationTest.kt
new file mode 100644
index 00000000..8f9a76a1
--- /dev/null
+++ b/core/database/src/androidTest/java/com/yapp/database/MigrationTest.kt
@@ -0,0 +1,80 @@
+package com.yapp.database
+
+import androidx.room.testing.MigrationTestHelper
+import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Assert.assertEquals
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.io.IOException
+
+@RunWith(AndroidJUnit4::class)
+class MigrationTest {
+
+ private val testDbName = "test_alarm_database"
+
+ @get:Rule
+ val helper: MigrationTestHelper = MigrationTestHelper(
+ InstrumentationRegistry.getInstrumentation(),
+ AlarmDatabase::class.java,
+ emptyList(),
+ FrameworkSQLiteOpenHelperFactory(),
+ )
+
+ @Test
+ @Throws(IOException::class)
+ fun `버전1에서_버전2로_마이그레이션시_새_컬럼이_기본값으로_채워짐`() {
+ helper.createDatabase(testDbName, 1).apply {
+ execSQL(
+ """
+ INSERT INTO alarm_database (
+ id,
+ isAm,
+ hour,
+ minute,
+ second,
+ repeatDays,
+ isHolidayAlarmOff,
+ isSnoozeEnabled,
+ snoozeInterval,
+ snoozeCount,
+ isVibrationEnabled,
+ isSoundEnabled,
+ soundUri,
+ soundVolume,
+ isAlarmActive
+ ) VALUES (
+ null, -- id (autoGenerate)
+ 1, -- isAm = true
+ 7, -- hour
+ 30, -- minute
+ 0, -- second
+ 0, -- repeatDays
+ 0, -- isHolidayAlarmOff = false
+ 1, -- isSnoozeEnabled = true
+ 5, -- snoozeInterval
+ 3, -- snoozeCount
+ 1, -- isVibrationEnabled = true
+ 1, -- isSoundEnabled = true
+ 'alarm.mp3', -- soundUri
+ 70, -- soundVolume
+ 1 -- isAlarmActive = true
+ )
+ """.trimIndent(),
+ )
+ close()
+ }
+
+ val db = helper.runMigrationsAndValidate(testDbName, 2, true, DatabaseMigrations.MIGRATION_1_2)
+
+ val cursor = db.query("SELECT missionType, missionCount FROM ${AlarmDatabase.DATABASE_NAME}")
+ cursor.use {
+ assertEquals(1, it.count)
+ it.moveToFirst()
+ assertEquals("TAP", it.getString(it.getColumnIndexOrThrow("missionType")))
+ assertEquals(10, it.getInt(it.getColumnIndexOrThrow("missionCount")))
+ }
+ }
+}
diff --git a/core/database/src/main/AndroidManifest.xml b/core/database/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..e1000761
--- /dev/null
+++ b/core/database/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/data/src/main/java/com/yapp/data/local/AlarmDao.kt b/core/database/src/main/java/com/yapp/database/AlarmDao.kt
similarity index 98%
rename from data/src/main/java/com/yapp/data/local/AlarmDao.kt
rename to core/database/src/main/java/com/yapp/database/AlarmDao.kt
index 41f0291a..6bb80174 100644
--- a/data/src/main/java/com/yapp/data/local/AlarmDao.kt
+++ b/core/database/src/main/java/com/yapp/database/AlarmDao.kt
@@ -1,4 +1,4 @@
-package com.yapp.data.local
+package com.yapp.database
import androidx.room.Dao
import androidx.room.Insert
diff --git a/data/src/main/java/com/yapp/data/local/AlarmDatabase.kt b/core/database/src/main/java/com/yapp/database/AlarmDatabase.kt
similarity index 56%
rename from data/src/main/java/com/yapp/data/local/AlarmDatabase.kt
rename to core/database/src/main/java/com/yapp/database/AlarmDatabase.kt
index 027f62d3..988912e2 100644
--- a/data/src/main/java/com/yapp/data/local/AlarmDatabase.kt
+++ b/core/database/src/main/java/com/yapp/database/AlarmDatabase.kt
@@ -1,9 +1,11 @@
-package com.yapp.data.local
+package com.yapp.database
import androidx.room.Database
import androidx.room.RoomDatabase
+import androidx.room.TypeConverters
-@Database(entities = [AlarmEntity::class], version = 1, exportSchema = false)
+@Database(entities = [AlarmEntity::class], version = 2, exportSchema = true)
+@TypeConverters(MissionTypeConverter::class)
abstract class AlarmDatabase : RoomDatabase() {
abstract fun alarmDao(): AlarmDao
diff --git a/data/src/main/java/com/yapp/data/local/AlarmEntity.kt b/core/database/src/main/java/com/yapp/database/AlarmEntity.kt
similarity index 91%
rename from data/src/main/java/com/yapp/data/local/AlarmEntity.kt
rename to core/database/src/main/java/com/yapp/database/AlarmEntity.kt
index 56ce2472..f8bc636e 100644
--- a/data/src/main/java/com/yapp/data/local/AlarmEntity.kt
+++ b/core/database/src/main/java/com/yapp/database/AlarmEntity.kt
@@ -1,8 +1,9 @@
-package com.yapp.data.local
+package com.yapp.database
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.yapp.domain.model.Alarm
+import com.yapp.domain.model.MissionType
@Entity(tableName = AlarmDatabase.DATABASE_NAME)
data class AlarmEntity(
@@ -10,7 +11,6 @@ data class AlarmEntity(
val id: Long = 0,
val isAm: Boolean = true,
-
val hour: Int = 6,
val minute: Int = 0,
val second: Int = 0,
@@ -31,6 +31,9 @@ data class AlarmEntity(
val soundVolume: Int = 70,
val isAlarmActive: Boolean = true,
+
+ val missionType: MissionType = MissionType.TAP,
+ val missionCount: Int = 10,
)
fun AlarmEntity.toDomain() = Alarm(
diff --git a/core/database/src/main/java/com/yapp/database/DatabaseMigrations.kt b/core/database/src/main/java/com/yapp/database/DatabaseMigrations.kt
new file mode 100644
index 00000000..60209b5d
--- /dev/null
+++ b/core/database/src/main/java/com/yapp/database/DatabaseMigrations.kt
@@ -0,0 +1,14 @@
+package com.yapp.database
+
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+
+internal object DatabaseMigrations {
+
+ val MIGRATION_1_2 = object : Migration(1, 2) {
+ override fun migrate(database: SupportSQLiteDatabase) {
+ database.execSQL("ALTER TABLE ${AlarmDatabase.DATABASE_NAME} ADD COLUMN missionType TEXT NOT NULL DEFAULT 'TAP'")
+ database.execSQL("ALTER TABLE ${AlarmDatabase.DATABASE_NAME} ADD COLUMN missionCount INTEGER NOT NULL DEFAULT 10")
+ }
+ }
+}
diff --git a/core/database/src/main/java/com/yapp/database/MissionTypeConverter.kt b/core/database/src/main/java/com/yapp/database/MissionTypeConverter.kt
new file mode 100644
index 00000000..aeb59503
--- /dev/null
+++ b/core/database/src/main/java/com/yapp/database/MissionTypeConverter.kt
@@ -0,0 +1,17 @@
+package com.yapp.database
+
+import androidx.room.TypeConverter
+import com.yapp.domain.model.MissionType
+
+class MissionTypeConverter {
+
+ @TypeConverter
+ fun fromInt(value: Int): MissionType {
+ return MissionType.fromInt(value)
+ }
+
+ @TypeConverter
+ fun toInt(missionType: MissionType): Int {
+ return missionType.value
+ }
+}
diff --git a/data/src/main/java/com/yapp/data/local/di/DatabaseModule.kt b/core/database/src/main/java/com/yapp/database/di/DatabaseModule.kt
similarity index 76%
rename from data/src/main/java/com/yapp/data/local/di/DatabaseModule.kt
rename to core/database/src/main/java/com/yapp/database/di/DatabaseModule.kt
index 80bcd808..7c6339f2 100644
--- a/data/src/main/java/com/yapp/data/local/di/DatabaseModule.kt
+++ b/core/database/src/main/java/com/yapp/database/di/DatabaseModule.kt
@@ -1,9 +1,10 @@
-package com.yapp.data.local.di
+package com.yapp.database.di
import android.content.Context
import androidx.room.Room
-import com.yapp.data.local.AlarmDao
-import com.yapp.data.local.AlarmDatabase
+import com.yapp.database.AlarmDao
+import com.yapp.database.AlarmDatabase
+import com.yapp.database.DatabaseMigrations
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -24,7 +25,9 @@ class DatabaseModule {
context.applicationContext,
AlarmDatabase::class.java,
AlarmDatabase.DATABASE_NAME,
- ).build()
+ )
+ .addMigrations(DatabaseMigrations.MIGRATION_1_2)
+ .build()
}
@Provides
diff --git a/core/database/src/test/java/com/yapp/database/ExampleUnitTest.kt b/core/database/src/test/java/com/yapp/database/ExampleUnitTest.kt
new file mode 100644
index 00000000..47e4e45c
--- /dev/null
+++ b/core/database/src/test/java/com/yapp/database/ExampleUnitTest.kt
@@ -0,0 +1,16 @@
+package com.yapp.database
+
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+ @Test
+ fun addition_isCorrect() {
+ assertEquals(4, 2 + 2)
+ }
+}
diff --git a/data/src/main/java/com/yapp/data/local/di/MediaModule.kt b/core/media/src/main/java/com/yapp/media/di/MediaModule.kt
similarity index 64%
rename from data/src/main/java/com/yapp/data/local/di/MediaModule.kt
rename to core/media/src/main/java/com/yapp/media/di/MediaModule.kt
index 1b9e9168..ff75332b 100644
--- a/data/src/main/java/com/yapp/data/local/di/MediaModule.kt
+++ b/core/media/src/main/java/com/yapp/media/di/MediaModule.kt
@@ -1,9 +1,8 @@
-package com.yapp.data.local.di
+package com.yapp.media.di
import android.content.ContentResolver
import android.content.Context
-import com.yapp.data.local.datasource.ImageLocalDataSource
-import com.yapp.data.local.datasource.ImageLocalDataSourceImpl
+import com.yapp.media.storage.ImageSaver
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -23,7 +22,9 @@ object MediaModule {
@Provides
@Singleton
- fun provideImageLocalDataSource(contentResolver: ContentResolver): ImageLocalDataSource {
- return ImageLocalDataSourceImpl(contentResolver)
+ fun provideImageSaver(
+ contentResolver: ContentResolver,
+ ): ImageSaver {
+ return ImageSaver(contentResolver)
}
}
diff --git a/data/src/main/java/com/yapp/data/local/datasource/ImageLocalDataSourceImpl.kt b/core/media/src/main/java/com/yapp/media/storage/ImageSaver.kt
similarity index 76%
rename from data/src/main/java/com/yapp/data/local/datasource/ImageLocalDataSourceImpl.kt
rename to core/media/src/main/java/com/yapp/media/storage/ImageSaver.kt
index 6b1a324f..0c1d98d8 100644
--- a/data/src/main/java/com/yapp/data/local/datasource/ImageLocalDataSourceImpl.kt
+++ b/core/media/src/main/java/com/yapp/media/storage/ImageSaver.kt
@@ -1,4 +1,4 @@
-package com.yapp.data.local.datasource
+package com.yapp.media.storage
import android.content.ContentResolver
import android.content.ContentValues
@@ -9,11 +9,11 @@ import android.util.Log
import java.io.IOException
import javax.inject.Inject
-class ImageLocalDataSourceImpl @Inject constructor(
+class ImageSaver @Inject constructor(
private val contentResolver: ContentResolver,
-) : ImageLocalDataSource {
+) {
- override suspend fun saveImage(byteArray: ByteArray, fileName: String): Boolean {
+ fun saveImage(byteArray: ByteArray, fileName: String): Boolean {
return try {
val bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size)
@@ -32,10 +32,10 @@ class ImageLocalDataSourceImpl @Inject constructor(
true
} catch (e: SecurityException) {
- Log.e("ImageLocalDataSource", "권한 없음: ${e.message}")
+ Log.e("ImageSaver", "권한 없음: ${e.message}")
false
} catch (e: IOException) {
- Log.e("ImageLocalDataSource", "파일 저장 실패: ${e.message}")
+ Log.e("ImageSaver", "파일 저장 실패: ${e.message}")
false
}
}
diff --git a/data/src/main/java/com/yapp/data/remote/utils/ApiError.kt b/core/network/src/main/java/com/yapp/network/model/ApiError.kt
similarity index 67%
rename from data/src/main/java/com/yapp/data/remote/utils/ApiError.kt
rename to core/network/src/main/java/com/yapp/network/model/ApiError.kt
index 947ef498..6bbcb596 100644
--- a/data/src/main/java/com/yapp/data/remote/utils/ApiError.kt
+++ b/core/network/src/main/java/com/yapp/network/model/ApiError.kt
@@ -1,4 +1,4 @@
-package com.yapp.data.remote.utils
+package com.yapp.network.model
data class ApiError(
override val message: String,
diff --git a/core/network/src/main/java/com/yapp/network/utils/ApiCallUtils.kt b/core/network/src/main/java/com/yapp/network/utils/ApiCallUtils.kt
new file mode 100644
index 00000000..e80dc731
--- /dev/null
+++ b/core/network/src/main/java/com/yapp/network/utils/ApiCallUtils.kt
@@ -0,0 +1,33 @@
+package com.yapp.network.utils
+
+import com.yapp.network.model.ApiError
+import kotlinx.coroutines.CancellationException
+import retrofit2.HttpException
+import java.io.IOException
+
+inline fun safeApiCall(action: () -> T): Result {
+ return try {
+ Result.success(action())
+ } catch (exception: Throwable) {
+ if (exception is CancellationException) throw exception
+
+ val mappedException = when (exception) {
+ is HttpException -> mapHttpException(exception)
+ is IOException -> ApiError("네트워크 오류 발생")
+ else -> ApiError("알 수 없는 오류 발생")
+ }
+
+ Result.failure(mappedException)
+ }
+}
+
+fun mapHttpException(exception: HttpException): ApiError {
+ return when (exception.code()) {
+ 400 -> ApiError("잘못된 요청")
+ 401 -> ApiError("인증이 필요합니다")
+ 403 -> ApiError("권한이 없습니다")
+ 404 -> ApiError("요청한 리소스를 찾을 수 없습니다")
+ in 500..599 -> ApiError("서버 오류")
+ else -> ApiError("알 수 없는 서버 오류가 발생했습니다.")
+ }
+}
diff --git a/data/build.gradle.kts b/data/build.gradle.kts
index f2c52f14..354e0002 100644
--- a/data/build.gradle.kts
+++ b/data/build.gradle.kts
@@ -10,20 +10,16 @@ android {
}
dependencies {
- implementation(projects.domain)
implementation(projects.core.network)
+ implementation(projects.core.database)
implementation(projects.core.datastore)
+
+ implementation(projects.domain)
implementation(projects.core.media)
implementation(projects.core.remoteconfig)
- ksp(libs.androidx.room.compiler)
- implementation(libs.androidx.room.ktx)
- implementation(libs.androidx.room.runtime)
- implementation(libs.androidx.room.paging)
-
implementation(libs.kotlinx.serialization.json)
implementation(libs.retrofit.core)
implementation(libs.retrofit.kotlin.serialization)
implementation(libs.okhttp.logging)
- implementation(libs.androidx.datastore)
}
diff --git a/data/src/main/java/com/yapp/data/di/RepositoryModule.kt b/data/src/main/java/com/yapp/data/di/RepositoryModule.kt
index e50b2ef6..8e3cc519 100644
--- a/data/src/main/java/com/yapp/data/di/RepositoryModule.kt
+++ b/data/src/main/java/com/yapp/data/di/RepositoryModule.kt
@@ -2,13 +2,11 @@ package com.yapp.data.di
import com.yapp.data.repositoryimpl.AlarmRepositoryImpl
import com.yapp.data.repositoryimpl.FortuneRepositoryImpl
-import com.yapp.data.repositoryimpl.ImageRepositoryImpl
import com.yapp.data.repositoryimpl.RemoteConfigRepositoryImpl
import com.yapp.data.repositoryimpl.SignUpRepositoryImpl
import com.yapp.data.repositoryimpl.UserInfoRepositoryImpl
import com.yapp.domain.repository.AlarmRepository
import com.yapp.domain.repository.FortuneRepository
-import com.yapp.domain.repository.ImageRepository
import com.yapp.domain.repository.RemoteConfigRepository
import com.yapp.domain.repository.SignUpRepository
import com.yapp.domain.repository.UserInfoRepository
@@ -33,12 +31,6 @@ abstract class RepositoryModule {
fortuneRepository: FortuneRepositoryImpl,
): FortuneRepository
- @Binds
- @Singleton
- abstract fun bindsImageRepository(
- imageRepository: ImageRepositoryImpl,
- ): ImageRepository
-
@Binds
@Singleton
abstract fun bindsUserInfoRepository(
diff --git a/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSource.kt b/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSource.kt
index 0579eab5..2ff1a748 100644
--- a/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSource.kt
+++ b/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSource.kt
@@ -1,6 +1,6 @@
package com.yapp.data.local.datasource
-import com.yapp.data.local.AlarmEntity
+import com.yapp.database.AlarmEntity
import com.yapp.domain.model.Alarm
import kotlinx.coroutines.flow.Flow
diff --git a/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSourceImpl.kt b/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSourceImpl.kt
index 18a86ce5..6c109877 100644
--- a/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSourceImpl.kt
+++ b/data/src/main/java/com/yapp/data/local/datasource/AlarmLocalDataSourceImpl.kt
@@ -1,8 +1,8 @@
package com.yapp.data.local.datasource
-import com.yapp.data.local.AlarmDao
-import com.yapp.data.local.AlarmEntity
-import com.yapp.data.local.toDomain
+import com.yapp.database.AlarmDao
+import com.yapp.database.AlarmEntity
+import com.yapp.database.toDomain
import com.yapp.datastore.UserPreferences
import com.yapp.domain.model.Alarm
import kotlinx.coroutines.flow.Flow
diff --git a/data/src/main/java/com/yapp/data/local/datasource/ImageLocalDataSource.kt b/data/src/main/java/com/yapp/data/local/datasource/ImageLocalDataSource.kt
deleted file mode 100644
index 607ee87a..00000000
--- a/data/src/main/java/com/yapp/data/local/datasource/ImageLocalDataSource.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.yapp.data.local.datasource
-
-interface ImageLocalDataSource {
- suspend fun saveImage(byteArray: ByteArray, fileName: String = "fortune_${System.currentTimeMillis()}.png"): Boolean
-}
diff --git a/data/src/main/java/com/yapp/data/remote/datasource/FortuneDataSourceImpl.kt b/data/src/main/java/com/yapp/data/remote/datasource/FortuneDataSourceImpl.kt
index f549500d..d8dc2dd7 100644
--- a/data/src/main/java/com/yapp/data/remote/datasource/FortuneDataSourceImpl.kt
+++ b/data/src/main/java/com/yapp/data/remote/datasource/FortuneDataSourceImpl.kt
@@ -2,7 +2,7 @@ package com.yapp.data.remote.datasource
import com.yapp.data.remote.dto.response.FortuneResponse
import com.yapp.data.remote.service.ApiService
-import com.yapp.data.remote.utils.safeApiCall
+import com.yapp.network.utils.safeApiCall
import javax.inject.Inject
class FortuneDataSourceImpl @Inject constructor(
diff --git a/data/src/main/java/com/yapp/data/remote/datasource/SignUpDataSourceImpl.kt b/data/src/main/java/com/yapp/data/remote/datasource/SignUpDataSourceImpl.kt
index 2acfa4ff..d6023c3d 100644
--- a/data/src/main/java/com/yapp/data/remote/datasource/SignUpDataSourceImpl.kt
+++ b/data/src/main/java/com/yapp/data/remote/datasource/SignUpDataSourceImpl.kt
@@ -3,8 +3,8 @@ package com.yapp.data.remote.datasource
import android.util.Log
import com.yapp.data.remote.dto.request.SignUpRequest
import com.yapp.data.remote.service.ApiService
-import com.yapp.data.remote.utils.ApiError
-import com.yapp.data.remote.utils.safeApiCall
+import com.yapp.network.model.ApiError
+import com.yapp.network.utils.safeApiCall
import javax.inject.Inject
class SignUpDataSourceImpl @Inject constructor(
diff --git a/data/src/main/java/com/yapp/data/remote/datasource/UserInfoDataSourceImpl.kt b/data/src/main/java/com/yapp/data/remote/datasource/UserInfoDataSourceImpl.kt
index 3c6cb580..d81e9189 100644
--- a/data/src/main/java/com/yapp/data/remote/datasource/UserInfoDataSourceImpl.kt
+++ b/data/src/main/java/com/yapp/data/remote/datasource/UserInfoDataSourceImpl.kt
@@ -3,7 +3,7 @@ package com.yapp.data.remote.datasource
import com.yapp.data.remote.dto.request.UpdateUserInfoRequest
import com.yapp.data.remote.dto.response.UserResponse
import com.yapp.data.remote.service.ApiService
-import com.yapp.data.remote.utils.safeApiCall
+import com.yapp.network.utils.safeApiCall
import javax.inject.Inject
class UserInfoDataSourceImpl @Inject constructor(
diff --git a/data/src/main/java/com/yapp/data/remote/utils/ApiCallUtils.kt b/data/src/main/java/com/yapp/data/remote/utils/ApiCallUtils.kt
deleted file mode 100644
index b1efd325..00000000
--- a/data/src/main/java/com/yapp/data/remote/utils/ApiCallUtils.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.yapp.data.remote.utils
-
-import retrofit2.HttpException
-import java.io.IOException
-
-internal inline fun safeApiCall(action: () -> T): Result =
- runCatching(action).recoverCatching { exception ->
- when (exception) {
- is HttpException -> throw mapHttpException(exception)
- is IOException -> throw ApiError("네트워크 오류 발생")
- else -> throw exception
- }
- }
-
-private fun mapHttpException(exception: HttpException): ApiError {
- return when (exception.code()) {
- 400 -> ApiError("잘못된 요청")
- 401 -> ApiError("인증이 필요합니다")
- 403 -> ApiError("권한이 없습니다")
- 404 -> ApiError("요청한 리소스를 찾을 수 없습니다")
- in 500..599 -> ApiError("서버 오류")
- else -> ApiError("알 수 없는 서버 오류가 발생했습니다.")
- }
-}
diff --git a/data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt b/data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt
index 07efaa22..112938be 100644
--- a/data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt
+++ b/data/src/main/java/com/yapp/data/repositoryimpl/AlarmRepositoryImpl.kt
@@ -2,7 +2,7 @@ package com.yapp.data.repositoryimpl
import android.net.Uri
import com.yapp.data.local.datasource.AlarmLocalDataSource
-import com.yapp.data.local.toEntity
+import com.yapp.database.toEntity
import com.yapp.domain.model.Alarm
import com.yapp.domain.model.AlarmSound
import com.yapp.domain.repository.AlarmRepository
diff --git a/data/src/main/java/com/yapp/data/repositoryimpl/ImageRepositoryImpl.kt b/data/src/main/java/com/yapp/data/repositoryimpl/ImageRepositoryImpl.kt
deleted file mode 100644
index 09658171..00000000
--- a/data/src/main/java/com/yapp/data/repositoryimpl/ImageRepositoryImpl.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.yapp.data.repositoryimpl
-
-import com.yapp.data.local.datasource.ImageLocalDataSource
-import com.yapp.domain.repository.ImageRepository
-import javax.inject.Inject
-
-class ImageRepositoryImpl @Inject constructor(
- private val imageLocalDataSource: ImageLocalDataSource,
-) : ImageRepository {
-
- override suspend fun saveImage(byteArray: ByteArray): Boolean {
- return imageLocalDataSource.saveImage(byteArray)
- }
-}
diff --git a/domain/src/main/java/com/yapp/domain/model/MissionType.kt b/domain/src/main/java/com/yapp/domain/model/MissionType.kt
index 45388ee6..3146d233 100644
--- a/domain/src/main/java/com/yapp/domain/model/MissionType.kt
+++ b/domain/src/main/java/com/yapp/domain/model/MissionType.kt
@@ -1,16 +1,21 @@
package com.yapp.domain.model
-sealed class MissionType {
- data object Shake : MissionType()
- data object Click : MissionType()
+enum class MissionType(val value: Int) {
+ TAP(0),
+ SHAKE(1),
+ ;
companion object {
+ fun fromInt(value: Int): MissionType {
+ return MissionType.entries.find { it.value == value } ?: TAP
+ }
+
fun fromRemoteValue(value: String): MissionType {
return when (value) {
- "tap_mission" -> Click
- "shake_mission" -> Shake
+ "tap_mission" -> TAP
+ "shake_mission" -> SHAKE
else -> {
- Click
+ TAP
}
}
}
diff --git a/domain/src/main/java/com/yapp/domain/repository/ImageRepository.kt b/domain/src/main/java/com/yapp/domain/repository/ImageRepository.kt
deleted file mode 100644
index 9abddf8a..00000000
--- a/domain/src/main/java/com/yapp/domain/repository/ImageRepository.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.yapp.domain.repository
-
-interface ImageRepository {
- suspend fun saveImage(byteArray: ByteArray): Boolean
-}
diff --git a/feature/fortune/src/main/java/com/yapp/fortune/FortuneViewModel.kt b/feature/fortune/src/main/java/com/yapp/fortune/FortuneViewModel.kt
index b0774c04..7759dbd1 100644
--- a/feature/fortune/src/main/java/com/yapp/fortune/FortuneViewModel.kt
+++ b/feature/fortune/src/main/java/com/yapp/fortune/FortuneViewModel.kt
@@ -5,9 +5,9 @@ import android.util.Log
import androidx.annotation.DrawableRes
import androidx.lifecycle.viewModelScope
import com.yapp.domain.repository.FortuneRepository
-import com.yapp.domain.repository.ImageRepository
import com.yapp.fortune.page.toFortunePages
import com.yapp.media.decoder.ImageUtils
+import com.yapp.media.storage.ImageSaver
import com.yapp.ui.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.firstOrNull
@@ -23,7 +23,7 @@ import javax.inject.Inject
class FortuneViewModel @Inject constructor(
private val application: Application,
private val fortuneRepository: FortuneRepository,
- private val imageRepository: ImageRepository,
+ private val imageSaver: ImageSaver,
) : BaseViewModel(
FortuneContract.State(),
) {
@@ -99,7 +99,7 @@ class FortuneViewModel @Inject constructor(
val bitmap = ImageUtils.getBitmapFromResource(application, resId)
val byteArray = ImageUtils.bitmapToByteArray(bitmap)
- val isSuccess = imageRepository.saveImage(byteArray)
+ val isSuccess = imageSaver.saveImage(byteArray, "fortune_${System.currentTimeMillis()}.png")
if (isSuccess) {
emitSideEffect(
diff --git a/feature/mission/src/main/java/com/yapp/mission/MissionContract.kt b/feature/mission/src/main/java/com/yapp/mission/MissionContract.kt
index f1812c49..a2af97ef 100644
--- a/feature/mission/src/main/java/com/yapp/mission/MissionContract.kt
+++ b/feature/mission/src/main/java/com/yapp/mission/MissionContract.kt
@@ -5,7 +5,7 @@ import com.yapp.domain.model.MissionType
sealed class MissionContract {
data class State(
- val missionType: MissionType = MissionType.Click,
+ val missionType: MissionType = MissionType.TAP,
val isMissionTypeLoading: Boolean = true,
val isMissionCompleted: Boolean = false,
val shakeCount: Int = 0,
diff --git a/feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt b/feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt
index cd42cdd1..a958e72e 100644
--- a/feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt
+++ b/feature/mission/src/main/java/com/yapp/mission/MissionScreen.kt
@@ -148,7 +148,7 @@ fun MissionContent(
Spacer(modifier = Modifier.heightForScreenPercentage(0.0665f))
when (state.missionType) {
- is MissionType.Shake -> {
+ MissionType.SHAKE -> {
if (state.shakeCount == 0) {
MissionShakeInitialImage()
} else {
@@ -156,7 +156,7 @@ fun MissionContent(
}
}
- is MissionType.Click -> {
+ MissionType.TAP -> {
MissionClickCard(state, eventDispatcher)
}
}
@@ -209,8 +209,8 @@ fun MissionProgressBarSection(state: MissionContract.State) {
Spacer(modifier = Modifier.heightForScreenPercentage(0.0246f))
MissionProgressBar(
currentProgress = when (state.missionType) {
- is MissionType.Shake -> state.shakeCount
- is MissionType.Click -> state.clickCount
+ MissionType.SHAKE -> state.shakeCount
+ MissionType.TAP -> state.clickCount
},
totalProgress = 10,
modifier = Modifier
@@ -227,8 +227,8 @@ fun MissionProgressBarSection(state: MissionContract.State) {
@Composable
fun MissionLabel(state: MissionContract.State) {
val instruction =
- if (state.missionType is MissionType.Shake) "10회를 흔들어 부적을 뒤집어줘" else "10회를 눌러 편지를 열어줘"
- val count = if (state.missionType is MissionType.Shake) state.shakeCount else state.clickCount
+ if (state.missionType == MissionType.SHAKE) "10회를 흔들어 부적을 뒤집어줘" else "10회를 눌러 편지를 열어줘"
+ val count = if (state.missionType == MissionType.SHAKE) state.shakeCount else state.clickCount
Text(
text = instruction,
@@ -316,8 +316,8 @@ fun ExitDialog(
type = "mission_fail",
properties = mapOf(
AnalyticsEvent.MissionPropertiesKeys.MISSION_TYPE to when (state.missionType) {
- is MissionType.Shake -> "shake"
- is MissionType.Click -> "click"
+ MissionType.SHAKE -> "shake"
+ MissionType.TAP -> "click"
},
),
),
@@ -401,7 +401,7 @@ private fun MissionRoutePreview() {
stateProvider = {
MissionContract.State(
isMissionTypeLoading = false,
- missionType = MissionType.Shake,
+ missionType = MissionType.SHAKE,
shakeCount = 0,
clickCount = 0,
showFinalAnimation = false,
diff --git a/feature/mission/src/main/java/com/yapp/mission/MissionViewModel.kt b/feature/mission/src/main/java/com/yapp/mission/MissionViewModel.kt
index c99b8f79..85dc3e6d 100644
--- a/feature/mission/src/main/java/com/yapp/mission/MissionViewModel.kt
+++ b/feature/mission/src/main/java/com/yapp/mission/MissionViewModel.kt
@@ -72,7 +72,7 @@ class MissionViewModel @Inject constructor(
}
private fun handleShake() = viewModelScope.launch {
- if (currentState.missionType !is MissionType.Shake) return@launch
+ if (currentState.missionType != MissionType.SHAKE) return@launch
val currentCount = currentState.shakeCount
if (currentCount < 9) {
@@ -92,7 +92,7 @@ class MissionViewModel @Inject constructor(
}
private fun handleClick() = viewModelScope.launch {
- if (currentState.missionType !is MissionType.Click) return@launch
+ if (currentState.missionType != MissionType.TAP) return@launch
val currentCount = currentState.clickCount
if (currentCount < 9) {
diff --git a/gradle.properties b/gradle.properties
index 20e2a015..e0d20494 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -20,4 +20,5 @@ kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
-android.nonTransitiveRClass=true
\ No newline at end of file
+android.nonTransitiveRClass=true
+android.experimental.androidTest.useUnifiedTestPlatform=false
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 1c8ef8ab..855c60e8 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -29,7 +29,7 @@ kotlinx-collections = "0.3.7"
androidx-app-compat = "1.7.0"
androidx-core = "1.15.0"
androidx-datastore = "1.1.1"
-androidx-room = "2.6.1"
+androidx-room = "2.7.2"
androidx-lifecycle = "2.8.7"
@@ -49,12 +49,7 @@ hilt-navigation-compose = "1.2.0"
## Third Party
okhttp = "4.12.0"
retrofit = "2.11.0"
-retrofit-kotlinx-serialization-json = "1.0.0"
coil = "2.4.0"
-sentry = "5.0.0"
-sentry-android = "8.0.0"
-sentry-compose = "8.0.0"
-gson = "2.11.0"
# Google Libraries Versions
google-service = "4.4.2"
@@ -82,7 +77,6 @@ process-pheonix = "3.0.0"
lottie = "6.1.0"
accompanist = "0.37.0"
materialAndroid = "1.7.5"
-flexible-bottomsheet = "0.1.5"
amplitude = "1.20.3"
[libraries]
@@ -107,6 +101,7 @@ androidx-datastore = { group = "androidx.datastore", name = "datastore-preferenc
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "androidx-room" }
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "androidx-room" }
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "androidx-room" }
+androidx-room-testing = { group = "androidx.room", name = "room-testing", version.ref = "androidx-room" }
androidx-room-paging = { group = "androidx.room", name = "room-paging", version.ref = "androidx-room" }
androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "annotation" }
@@ -137,7 +132,6 @@ hilt-android-testing = { group = "com.google.dagger", name = "hilt-android-testi
hilt-android-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hilt-navigation-compose" }
-
# Orbit
orbit-core = { group = "org.orbit-mvi", name = "orbit-core", version.ref = "orbit" }
orbit-compose = { group = "org.orbit-mvi", name = "orbit-compose", version.ref = "orbit" }
@@ -148,9 +142,6 @@ retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.r
retrofit-kotlin-serialization = { module = "com.squareup.retrofit2:converter-kotlinx-serialization", version.ref = "retrofit" }
okhttp-bom = { group = "com.squareup.okhttp3", name = "okhttp-bom", version.ref = "okhttp" }
okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
-flexible-bottomsheet = { group = "com.github.skydoves", name = "flexible-bottomsheet-material3", version.ref = "flexible-bottomsheet" }
-#sentry-android = { group = "io.sentry", name = "sentry-android", version.ref = "sentry-android" }
-#sentry-compose = { group = "io.sentry", name = "sentry-compose", version.ref = "sentry-compose" }
# Google Libraries
firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebase-bom" }
@@ -194,10 +185,10 @@ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
android-library = { id = "com.android.library", version.ref = "android-gradle-plugin" }
+room = { id = "androidx.room", version.ref = "androidx-room" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
android-test = { id = "com.android.test", version.ref = "android-gradle-plugin" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
google-service = { id = "com.google.gms.google-services", version.ref = "google-service" }
firebase-app-distribution = { id = "com.google.firebase.appdistribution", version.ref = "firebase-app-distribution" }
firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebase-crashlytics" }
-#sentry = { id = "io.sentry.android.gradle", version.ref = "sentry" }
diff --git a/project.dot.png b/project.dot.png
index 11f78f97..65f883c3 100644
Binary files a/project.dot.png and b/project.dot.png differ
diff --git a/settings.gradle.kts b/settings.gradle.kts
index e86bd657..984c9fc9 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -46,3 +46,4 @@ include(":feature:splash")
include(":feature:webview")
include(":core:analytics")
include(":core:remoteconfig")
+include(":core:database")