Implement core:config module
This commit is contained in:
@@ -61,6 +61,7 @@ dependencies {
|
||||
implementation(project(":core:common"))
|
||||
implementation(project(":core:network"))
|
||||
implementation(project(":core:database"))
|
||||
implementation(project(":core:config"))
|
||||
|
||||
implementation(project(":feature:schedule"))
|
||||
implementation(project(":feature:progress"))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:name=".App"
|
||||
android:name=".TSUDeskApp"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
@@ -13,14 +13,14 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.TSUDesk">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:name=".SplashScreenActivity"
|
||||
android:exported="true">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
</activity>
|
||||
<activity android:name=".MainActivity" />
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -1,7 +1,24 @@
|
||||
package ru.fincode.tsudesk
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.fincode.tsudesk.core.config.domain.usecase.GetConfigUseCase
|
||||
import ru.fincode.tsudesk.core.common.model.DataResult
|
||||
import javax.inject.Inject
|
||||
|
||||
const val LOG_TAG = "NETWORK_DEBUG"
|
||||
|
||||
@HiltAndroidApp
|
||||
class App : Application()
|
||||
class TSUDeskApp : Application() {
|
||||
|
||||
private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleType
|
||||
import ru.fincode.tsudesk.feature.schedule.domain.usecase.GetScheduleUseCase
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val LOG_TAG = "NETWORK_DEBUG"
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
44
app/src/main/java/ru/fincode/tsudesk/SplashScreenActivity.kt
Normal file
44
app/src/main/java/ru/fincode/tsudesk/SplashScreenActivity.kt
Normal file
@@ -0,0 +1,44 @@
|
||||
package ru.fincode.tsudesk
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import ru.fincode.tsudesk.core.common.model.DataResult
|
||||
import ru.fincode.tsudesk.core.config.domain.usecase.GetConfigUseCase
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SplashScreenActivity : ComponentActivity() {
|
||||
|
||||
@Inject
|
||||
lateinit var fetchConfigUseCase: GetConfigUseCase
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val result = withContext(Dispatchers.IO) { fetchConfigUseCase() }
|
||||
|
||||
when (result) {
|
||||
is DataResult.Data -> {
|
||||
Log.d(LOG_TAG, "SUCCESS: config=${result.data}")
|
||||
}
|
||||
|
||||
is DataResult.Error -> {
|
||||
Log.e(
|
||||
LOG_TAG,
|
||||
"ERROR: ${result.error}, fallback=${result.data}",
|
||||
result.cause
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
startActivity(Intent(this@SplashScreenActivity, MainActivity::class.java))
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,12 @@
|
||||
package ru.fincode.tsudesk.core.common.model
|
||||
|
||||
sealed interface DataResult<out T> {
|
||||
data class Data<T>(val data: T, val refreshedFromNetwork: Boolean) : DataResult<T>
|
||||
data class Error(val error: AppError, val cause: Throwable? = null) : DataResult<Nothing>
|
||||
|
||||
data class Data<T>(
|
||||
val data: T, val refreshedFromNetwork: Boolean
|
||||
) : DataResult<T>
|
||||
|
||||
data class Error<T>(
|
||||
val error: AppError, val data: T? = null, val cause: Throwable? = null
|
||||
) : DataResult<T>
|
||||
}
|
||||
38
core/config/build.gradle.kts
Normal file
38
core/config/build.gradle.kts
Normal file
@@ -0,0 +1,38 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.kapt)
|
||||
alias(libs.plugins.hilt)
|
||||
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "ru.fincode.tsudesk.core.config"
|
||||
compileSdk = libs.versions.compileSdk.get().toInt()
|
||||
|
||||
defaultConfig {
|
||||
minSdk = libs.versions.minSdk.get().toInt()
|
||||
}
|
||||
val jvm = JavaVersion.toVersion(libs.versions.jvmTarget.get())
|
||||
compileOptions {
|
||||
sourceCompatibility = jvm
|
||||
targetCompatibility = jvm
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = jvm.toString()
|
||||
}
|
||||
}
|
||||
kapt {
|
||||
correctErrorTypes = true
|
||||
}
|
||||
dependencies {
|
||||
kapt(libs.hilt.compiler)
|
||||
implementation(libs.hilt.android)
|
||||
|
||||
// Kotlin Serialization
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
|
||||
implementation(project(":core:network"))
|
||||
implementation(project(":core:common"))
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package ru.fincode.tsudesk.core.config.data
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import ru.fincode.tsudesk.core.common.model.DataResult
|
||||
import ru.fincode.tsudesk.core.config.data.datasource.ConfigRemoteDataSource
|
||||
import ru.fincode.tsudesk.core.config.domain.ConfigRepository
|
||||
import ru.fincode.tsudesk.core.config.domain.model.AppConfig
|
||||
import ru.fincode.tsudesk.core.network.model.NetworkResult
|
||||
import javax.inject.Inject
|
||||
|
||||
class ConfigRepositoryImpl @Inject constructor(
|
||||
private val remoteDataSource: ConfigRemoteDataSource
|
||||
) : ConfigRepository {
|
||||
|
||||
private val defaultConfig = AppConfig()
|
||||
|
||||
override suspend fun fetchConfig(): DataResult<AppConfig> {
|
||||
return when (val res = remoteDataSource.load()) {
|
||||
is NetworkResult.Success -> {
|
||||
val dto = res.data
|
||||
DataResult.Data(
|
||||
data = AppConfig(
|
||||
newsEnabled = dto.newsEnabled ?: defaultConfig.newsEnabled,
|
||||
scheduleEnabled = dto.scheduleEnabled ?: defaultConfig.scheduleEnabled,
|
||||
gradesEnabled = dto.gradesEnabled ?: defaultConfig.gradesEnabled
|
||||
),
|
||||
refreshedFromNetwork = true
|
||||
)
|
||||
}
|
||||
|
||||
is NetworkResult.Error -> {
|
||||
DataResult.Error(
|
||||
error = res.error.toAppError(), data = defaultConfig
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun getConfig(): Flow<DataResult<AppConfig>> = flow {
|
||||
when (val result = remoteDataSource.load()) {
|
||||
is NetworkResult.Success -> {
|
||||
val dto = result.data
|
||||
val appConfig = AppConfig(
|
||||
newsEnabled = dto.newsEnabled ?: defaultConfig.newsEnabled,
|
||||
scheduleEnabled = dto.scheduleEnabled ?: defaultConfig.scheduleEnabled,
|
||||
gradesEnabled = dto.gradesEnabled ?: defaultConfig.gradesEnabled
|
||||
)
|
||||
emit(DataResult.Data(data = appConfig, refreshedFromNetwork = true))
|
||||
}
|
||||
|
||||
is NetworkResult.Error -> {
|
||||
emit(
|
||||
DataResult.Error(
|
||||
error = result.error.toAppError(),
|
||||
data = defaultConfig
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package ru.fincode.tsudesk.core.config.data.datasource
|
||||
|
||||
import ru.fincode.tsudesk.core.config.data.remote.dto.RemoteConfigDto
|
||||
import ru.fincode.tsudesk.core.network.model.NetworkResult
|
||||
|
||||
interface ConfigRemoteDataSource {
|
||||
suspend fun load(): NetworkResult<RemoteConfigDto>
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package ru.fincode.tsudesk.core.config.data.datasource
|
||||
|
||||
import ru.fincode.tsudesk.core.config.data.remote.api.ConfigApi
|
||||
import ru.fincode.tsudesk.core.config.data.remote.dto.RemoteConfigDto
|
||||
import ru.fincode.tsudesk.core.network.apiCall
|
||||
import ru.fincode.tsudesk.core.network.model.NetworkResult
|
||||
import javax.inject.Inject
|
||||
|
||||
class ConfigRemoteDataSourceImpl @Inject constructor(
|
||||
private val api: ConfigApi
|
||||
) : ConfigRemoteDataSource {
|
||||
|
||||
override suspend fun load(): NetworkResult<RemoteConfigDto> =
|
||||
apiCall { api.getConfig() }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.fincode.tsudesk.core.config.data.model
|
||||
|
||||
data class RemoteConfig(
|
||||
val newsEnabled: Boolean,
|
||||
val scheduleEnabled: Boolean,
|
||||
val gradesEnabled: Boolean
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
package ru.fincode.tsudesk.core.config.data.remote
|
||||
|
||||
object ConfigApiContract {
|
||||
|
||||
const val CONFIG_BASE_URL = "https://scherbatykh.ru/app/tsudesk/"
|
||||
|
||||
object Path {
|
||||
const val GET_CONFIG_METHOD = "config.json"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package ru.fincode.tsudesk.core.config.data.remote.api
|
||||
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.GET
|
||||
import ru.fincode.tsudesk.core.config.data.remote.ConfigApiContract.Path.GET_CONFIG_METHOD
|
||||
import ru.fincode.tsudesk.core.config.data.remote.dto.RemoteConfigDto
|
||||
|
||||
interface ConfigApi {
|
||||
|
||||
@GET(GET_CONFIG_METHOD)
|
||||
suspend fun getConfig(): Response<RemoteConfigDto>
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package ru.fincode.tsudesk.core.config.data.remote.dto
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class RemoteConfigDto(
|
||||
val newsEnabled: Boolean? = null,
|
||||
val scheduleEnabled: Boolean? = null,
|
||||
val gradesEnabled: Boolean? = null
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
package ru.fincode.tsudesk.core.config.di
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Retrofit
|
||||
import ru.fincode.tsudesk.core.config.data.remote.ConfigApiContract.CONFIG_BASE_URL
|
||||
import ru.fincode.tsudesk.core.network.RetrofitFactory
|
||||
import ru.fincode.tsudesk.core.config.data.remote.api.ConfigApi
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object ConfigNetworkModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
@ConfigRetrofit
|
||||
fun provideConfigRetrofit(
|
||||
factory: RetrofitFactory,
|
||||
client: OkHttpClient
|
||||
): Retrofit =
|
||||
factory.create(
|
||||
baseUrl = CONFIG_BASE_URL,
|
||||
client = client
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideConfigApi(
|
||||
@ConfigRetrofit retrofit: Retrofit
|
||||
): ConfigApi =
|
||||
retrofit.create(ConfigApi::class.java)
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package ru.fincode.tsudesk.core.config.di
|
||||
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import ru.fincode.tsudesk.core.config.data.ConfigRepositoryImpl
|
||||
import ru.fincode.tsudesk.core.config.data.datasource.ConfigRemoteDataSource
|
||||
import ru.fincode.tsudesk.core.config.data.datasource.ConfigRemoteDataSourceImpl
|
||||
import ru.fincode.tsudesk.core.config.domain.ConfigRepository
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
abstract class ConfigRepositoryModule {
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindRemoteDataSource(
|
||||
impl: ConfigRemoteDataSourceImpl
|
||||
): ConfigRemoteDataSource
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindRepository(
|
||||
impl: ConfigRepositoryImpl
|
||||
): ConfigRepository
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.fincode.tsudesk.core.config.di
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Qualifier
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
annotation class ConfigRetrofit
|
||||
@@ -0,0 +1,10 @@
|
||||
package ru.fincode.tsudesk.core.config.domain
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import ru.fincode.tsudesk.core.common.model.DataResult
|
||||
import ru.fincode.tsudesk.core.config.domain.model.AppConfig
|
||||
|
||||
interface ConfigRepository {
|
||||
suspend fun fetchConfig(): DataResult<AppConfig>
|
||||
fun getConfig(): Flow<DataResult<AppConfig>>
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.fincode.tsudesk.core.config.domain.model
|
||||
|
||||
data class AppConfig(
|
||||
val newsEnabled: Boolean = false,
|
||||
val scheduleEnabled: Boolean = false,
|
||||
val gradesEnabled: Boolean = false
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
package ru.fincode.tsudesk.core.config.domain.usecase
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import ru.fincode.tsudesk.core.common.model.DataResult
|
||||
import ru.fincode.tsudesk.core.config.domain.ConfigRepository
|
||||
import ru.fincode.tsudesk.core.config.domain.model.AppConfig
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetConfigUseCase @Inject constructor(
|
||||
private val repository: ConfigRepository
|
||||
) {
|
||||
suspend operator fun invoke(): DataResult<AppConfig> = repository.fetchConfig()
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import ru.fincode.tsudesk.core.database.schedule.ScheduleDao
|
||||
// NewsEntity::class,
|
||||
// GradeEntity::class,
|
||||
],
|
||||
version = 1,
|
||||
version = 2,
|
||||
exportSchema = false
|
||||
)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
@@ -13,7 +13,9 @@ android {
|
||||
minSdk = libs.versions.minSdk.get().toInt()
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
buildConfig = true
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
@@ -32,7 +34,9 @@ android {
|
||||
jvmTarget = jvm.toString()
|
||||
}
|
||||
}
|
||||
|
||||
kapt {
|
||||
correctErrorTypes = true
|
||||
}
|
||||
dependencies {
|
||||
kapt(libs.hilt.compiler)
|
||||
implementation(libs.hilt.android)
|
||||
|
||||
@@ -31,7 +31,6 @@ android {
|
||||
jvmTarget = jvm.toString()
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.core.ktx)
|
||||
implementation(libs.androidx.appcompat)
|
||||
|
||||
@@ -11,7 +11,6 @@ android {
|
||||
minSdk = libs.versions.minSdk.get().toInt()
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
|
||||
@@ -30,7 +30,6 @@ android {
|
||||
jvmTarget = jvm.toString()
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.core.ktx)
|
||||
implementation(libs.androidx.appcompat)
|
||||
|
||||
@@ -33,7 +33,9 @@ android {
|
||||
jvmTarget = jvm.toString()
|
||||
}
|
||||
}
|
||||
|
||||
kapt {
|
||||
correctErrorTypes = true
|
||||
}
|
||||
dependencies {
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.material)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package ru.fincode.tsudesk.feature.schedule.data
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import ru.fincode.tsudesk.core.common.model.DataResult
|
||||
@@ -23,18 +22,29 @@ class ScheduleRepositoryImpl @Inject constructor(
|
||||
) : ScheduleRepository {
|
||||
|
||||
override fun observeSchedule(type: ScheduleType): Flow<DataResult<ScheduleEntity>> = flow {
|
||||
val key = when (type) {
|
||||
is ScheduleType.Group -> ScheduleCacheKey.group(type.number)
|
||||
is ScheduleType.Teacher -> ScheduleCacheKey.teacher(type.name)
|
||||
val scheduleKey = type.toCacheKey()
|
||||
val cached = local.observeSchedule(scheduleKey).first()
|
||||
cached?.let {
|
||||
emit(DataResult.Data(it, refreshedFromNetwork = false))
|
||||
}
|
||||
|
||||
val cached: ScheduleEntity? = local.observeSchedule(key).first()
|
||||
if (cached != null) {
|
||||
emit(DataResult.Data(cached, refreshedFromNetwork = false))
|
||||
val networkResult = loadSchedule(type)
|
||||
if (networkResult is NetworkResult.Error) {
|
||||
emit(DataResult.Error(networkResult.error.toAppError()))
|
||||
return@flow
|
||||
}
|
||||
|
||||
// if (cached?.timestamp != updated.timestamp) { ... }
|
||||
val networkResult: NetworkResult<ScheduleEntity> = when (type) {
|
||||
val networkSchedule = (networkResult as NetworkResult.Success).data
|
||||
local.saveSchedule(scheduleKey, networkSchedule)
|
||||
|
||||
val updated = local.loadSchedule(scheduleKey)
|
||||
if (updated != null && cached?.timestamp != updated.timestamp) {
|
||||
emit(DataResult.Data(updated, refreshedFromNetwork = true))
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadSchedule(type: ScheduleType): NetworkResult<ScheduleEntity> =
|
||||
when (type) {
|
||||
is ScheduleType.Group ->
|
||||
remote.loadScheduleByGroup(type.number).map(mapper::invoke)
|
||||
|
||||
@@ -42,18 +52,4 @@ class ScheduleRepositoryImpl @Inject constructor(
|
||||
remote.loadScheduleByTeacher(type.name).map(mapper::invoke)
|
||||
}
|
||||
|
||||
when (networkResult) {
|
||||
is NetworkResult.Success -> {
|
||||
local.saveSchedule(key, networkResult.data)
|
||||
}
|
||||
|
||||
is NetworkResult.Error -> {
|
||||
emit(DataResult.Error(networkResult.error.toAppError()))
|
||||
return@flow
|
||||
}
|
||||
}
|
||||
val updated: ScheduleEntity = local.observeSchedule(key).filterNotNull().first()
|
||||
val refreshedFromNetwork = cached?.timestamp != updated.timestamp
|
||||
emit(DataResult.Data(updated, refreshedFromNetwork))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
package ru.fincode.tsudesk.feature.schedule.domain.model
|
||||
|
||||
import ru.fincode.tsudesk.feature.schedule.data.local.ScheduleCacheKey
|
||||
|
||||
sealed interface ScheduleType {
|
||||
data class Group(val number: String) : ScheduleType
|
||||
data class Teacher(val name: String) : ScheduleType
|
||||
|
||||
fun toCacheKey(): String
|
||||
|
||||
data class Group(val number: String) : ScheduleType {
|
||||
override fun toCacheKey(): String =
|
||||
ScheduleCacheKey.group(number)
|
||||
}
|
||||
|
||||
data class Teacher(val name: String) : ScheduleType {
|
||||
override fun toCacheKey(): String =
|
||||
ScheduleCacheKey.teacher(name)
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ versionCode = "1"
|
||||
agp = "8.12.0"
|
||||
kotlin = "1.9.24"
|
||||
jvmTarget = "17"
|
||||
serilization = "1.6.3"
|
||||
|
||||
coreKtx = "1.10.1"
|
||||
appcompat = "1.6.1"
|
||||
@@ -24,12 +25,16 @@ lifecycle = "2.7.0"
|
||||
coroutines = "1.8.1"
|
||||
|
||||
room = "2.6.1"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.1.5"
|
||||
espressoCore = "3.5.1"
|
||||
|
||||
[libraries]
|
||||
# Android
|
||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
|
||||
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
|
||||
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serilization" }
|
||||
# UI: AndroidX, Jetpack Compose
|
||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
|
||||
@@ -51,11 +56,15 @@ core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx"
|
||||
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
||||
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
|
||||
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
||||
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" }
|
||||
|
||||
[plugins]
|
||||
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||
|
||||
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
|
||||
|
||||
@@ -27,6 +27,7 @@ include(":core:common")
|
||||
include(":core:ui")
|
||||
include(":core:network")
|
||||
include(":core:database")
|
||||
include(":core:config")
|
||||
|
||||
include(":feature:schedule")
|
||||
include(":feature:news")
|
||||
|
||||
Reference in New Issue
Block a user