diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0cbf6f4..ab17602 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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")) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5ebf391..ae495cc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,7 +5,7 @@ - + \ No newline at end of file diff --git a/app/src/main/java/ru/fincode/tsudesk/App.kt b/app/src/main/java/ru/fincode/tsudesk/App.kt index fcd2b04..bff88ab 100644 --- a/app/src/main/java/ru/fincode/tsudesk/App.kt +++ b/app/src/main/java/ru/fincode/tsudesk/App.kt @@ -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() + } +} diff --git a/app/src/main/java/ru/fincode/tsudesk/MainActivity.kt b/app/src/main/java/ru/fincode/tsudesk/MainActivity.kt index 0e07fca..58b0339 100644 --- a/app/src/main/java/ru/fincode/tsudesk/MainActivity.kt +++ b/app/src/main/java/ru/fincode/tsudesk/MainActivity.kt @@ -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() { diff --git a/app/src/main/java/ru/fincode/tsudesk/SplashScreenActivity.kt b/app/src/main/java/ru/fincode/tsudesk/SplashScreenActivity.kt new file mode 100644 index 0000000..bc356db --- /dev/null +++ b/app/src/main/java/ru/fincode/tsudesk/SplashScreenActivity.kt @@ -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() + } + } +} diff --git a/core/common/src/main/java/ru/fincode/tsudesk/core/common/model/DataResult.kt b/core/common/src/main/java/ru/fincode/tsudesk/core/common/model/DataResult.kt index 75d97c1..ce7da49 100644 --- a/core/common/src/main/java/ru/fincode/tsudesk/core/common/model/DataResult.kt +++ b/core/common/src/main/java/ru/fincode/tsudesk/core/common/model/DataResult.kt @@ -1,6 +1,12 @@ package ru.fincode.tsudesk.core.common.model sealed interface DataResult { - data class Data(val data: T, val refreshedFromNetwork: Boolean) : DataResult - data class Error(val error: AppError, val cause: Throwable? = null) : DataResult -} \ No newline at end of file + + data class Data( + val data: T, val refreshedFromNetwork: Boolean + ) : DataResult + + data class Error( + val error: AppError, val data: T? = null, val cause: Throwable? = null + ) : DataResult +} diff --git a/core/config/build.gradle.kts b/core/config/build.gradle.kts new file mode 100644 index 0000000..9618eb3 --- /dev/null +++ b/core/config/build.gradle.kts @@ -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")) +} \ No newline at end of file diff --git a/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/ConfigRepositoryImpl.kt b/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/ConfigRepositoryImpl.kt new file mode 100644 index 0000000..39cd3a9 --- /dev/null +++ b/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/ConfigRepositoryImpl.kt @@ -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 { + 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> = 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 + ) + ) + } + } + } +} diff --git a/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/datasource/ConfigRemoteDataSource.kt b/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/datasource/ConfigRemoteDataSource.kt new file mode 100644 index 0000000..81ad315 --- /dev/null +++ b/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/datasource/ConfigRemoteDataSource.kt @@ -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 +} \ No newline at end of file diff --git a/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/datasource/ConfigRemoteDataSourceImpl.kt b/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/datasource/ConfigRemoteDataSourceImpl.kt new file mode 100644 index 0000000..327f670 --- /dev/null +++ b/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/datasource/ConfigRemoteDataSourceImpl.kt @@ -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 = + apiCall { api.getConfig() } +} diff --git a/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/model/RemoteConfigDto.kt b/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/model/RemoteConfigDto.kt new file mode 100644 index 0000000..30768e7 --- /dev/null +++ b/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/model/RemoteConfigDto.kt @@ -0,0 +1,7 @@ +package ru.fincode.tsudesk.core.config.data.model + +data class RemoteConfig( + val newsEnabled: Boolean, + val scheduleEnabled: Boolean, + val gradesEnabled: Boolean +) diff --git a/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/remote/ConfigApiContract.kt b/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/remote/ConfigApiContract.kt new file mode 100644 index 0000000..a7e12cb --- /dev/null +++ b/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/remote/ConfigApiContract.kt @@ -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" + } +} \ No newline at end of file diff --git a/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/remote/api/ConfigApi.kt b/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/remote/api/ConfigApi.kt new file mode 100644 index 0000000..331dfd8 --- /dev/null +++ b/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/remote/api/ConfigApi.kt @@ -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 +} \ No newline at end of file diff --git a/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/remote/dto/RemoteConfigDto.kt b/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/remote/dto/RemoteConfigDto.kt new file mode 100644 index 0000000..4c5192f --- /dev/null +++ b/core/config/src/main/java/ru/fincode/tsudesk/core/config/data/remote/dto/RemoteConfigDto.kt @@ -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 +) \ No newline at end of file diff --git a/core/config/src/main/java/ru/fincode/tsudesk/core/config/di/ConfigNetworkModule.kt b/core/config/src/main/java/ru/fincode/tsudesk/core/config/di/ConfigNetworkModule.kt new file mode 100644 index 0000000..f6a223f --- /dev/null +++ b/core/config/src/main/java/ru/fincode/tsudesk/core/config/di/ConfigNetworkModule.kt @@ -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) +} diff --git a/core/config/src/main/java/ru/fincode/tsudesk/core/config/di/ConfigRepositoryModule.kt b/core/config/src/main/java/ru/fincode/tsudesk/core/config/di/ConfigRepositoryModule.kt new file mode 100644 index 0000000..ac2f156 --- /dev/null +++ b/core/config/src/main/java/ru/fincode/tsudesk/core/config/di/ConfigRepositoryModule.kt @@ -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 +} diff --git a/core/config/src/main/java/ru/fincode/tsudesk/core/config/di/ConfigRetrofit.kt b/core/config/src/main/java/ru/fincode/tsudesk/core/config/di/ConfigRetrofit.kt new file mode 100644 index 0000000..1b5ca0f --- /dev/null +++ b/core/config/src/main/java/ru/fincode/tsudesk/core/config/di/ConfigRetrofit.kt @@ -0,0 +1,7 @@ +package ru.fincode.tsudesk.core.config.di + +import javax.inject.Qualifier + +@Qualifier +@Retention(AnnotationRetention.BINARY) +annotation class ConfigRetrofit diff --git a/core/config/src/main/java/ru/fincode/tsudesk/core/config/domain/ConfigRepository.kt b/core/config/src/main/java/ru/fincode/tsudesk/core/config/domain/ConfigRepository.kt new file mode 100644 index 0000000..07a18e1 --- /dev/null +++ b/core/config/src/main/java/ru/fincode/tsudesk/core/config/domain/ConfigRepository.kt @@ -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 + fun getConfig(): Flow> +} diff --git a/core/config/src/main/java/ru/fincode/tsudesk/core/config/domain/model/AppConfig.kt b/core/config/src/main/java/ru/fincode/tsudesk/core/config/domain/model/AppConfig.kt new file mode 100644 index 0000000..e79629f --- /dev/null +++ b/core/config/src/main/java/ru/fincode/tsudesk/core/config/domain/model/AppConfig.kt @@ -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 +) diff --git a/core/config/src/main/java/ru/fincode/tsudesk/core/config/domain/usecase/GetConfigUseCase.kt b/core/config/src/main/java/ru/fincode/tsudesk/core/config/domain/usecase/GetConfigUseCase.kt new file mode 100644 index 0000000..170777c --- /dev/null +++ b/core/config/src/main/java/ru/fincode/tsudesk/core/config/domain/usecase/GetConfigUseCase.kt @@ -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 = repository.fetchConfig() +} \ No newline at end of file diff --git a/core/database/src/main/java/ru/fincode/tsudesk/core/database/AppDatabase.kt b/core/database/src/main/java/ru/fincode/tsudesk/core/database/AppDatabase.kt index 75f3f7d..dd2306c 100644 --- a/core/database/src/main/java/ru/fincode/tsudesk/core/database/AppDatabase.kt +++ b/core/database/src/main/java/ru/fincode/tsudesk/core/database/AppDatabase.kt @@ -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() { diff --git a/core/network/build.gradle.kts b/core/network/build.gradle.kts index de595ad..3559670 100644 --- a/core/network/build.gradle.kts +++ b/core/network/build.gradle.kts @@ -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) diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index c36b5c9..ab480b6 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -31,7 +31,6 @@ android { jvmTarget = jvm.toString() } } - dependencies { implementation(libs.core.ktx) implementation(libs.androidx.appcompat) diff --git a/feature/news/build.gradle.kts b/feature/news/build.gradle.kts index 162762f..20dfbeb 100644 --- a/feature/news/build.gradle.kts +++ b/feature/news/build.gradle.kts @@ -11,7 +11,6 @@ android { minSdk = libs.versions.minSdk.get().toInt() consumerProguardFiles("consumer-rules.pro") } - buildTypes { release { isMinifyEnabled = false diff --git a/feature/progress/build.gradle.kts b/feature/progress/build.gradle.kts index 4ef843d..f80ec86 100644 --- a/feature/progress/build.gradle.kts +++ b/feature/progress/build.gradle.kts @@ -30,7 +30,6 @@ android { jvmTarget = jvm.toString() } } - dependencies { implementation(libs.core.ktx) implementation(libs.androidx.appcompat) diff --git a/feature/schedule/build.gradle.kts b/feature/schedule/build.gradle.kts index 693694c..4a21b08 100644 --- a/feature/schedule/build.gradle.kts +++ b/feature/schedule/build.gradle.kts @@ -33,7 +33,9 @@ android { jvmTarget = jvm.toString() } } - +kapt { + correctErrorTypes = true +} dependencies { implementation(libs.androidx.appcompat) implementation(libs.material) diff --git a/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/ScheduleRepositoryImpl.kt b/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/ScheduleRepositoryImpl.kt index caa4496..16c5938 100644 --- a/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/ScheduleRepositoryImpl.kt +++ b/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/data/ScheduleRepositoryImpl.kt @@ -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> = 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 = 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 = + 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)) - } } diff --git a/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/domain/model/ScheduleType.kt b/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/domain/model/ScheduleType.kt index 805d53f..3f8fd9c 100644 --- a/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/domain/model/ScheduleType.kt +++ b/feature/schedule/src/main/java/ru/fincode/tsudesk/feature/schedule/domain/model/ScheduleType.kt @@ -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 -} \ No newline at end of file + + 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) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ce166c1..08ecb9c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" } diff --git a/settings.gradle.kts b/settings.gradle.kts index c7b07ce..61a06ef 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -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")