Implement core:config module
This commit is contained in:
@@ -61,6 +61,7 @@ dependencies {
|
|||||||
implementation(project(":core:common"))
|
implementation(project(":core:common"))
|
||||||
implementation(project(":core:network"))
|
implementation(project(":core:network"))
|
||||||
implementation(project(":core:database"))
|
implementation(project(":core:database"))
|
||||||
|
implementation(project(":core:config"))
|
||||||
|
|
||||||
implementation(project(":feature:schedule"))
|
implementation(project(":feature:schedule"))
|
||||||
implementation(project(":feature:progress"))
|
implementation(project(":feature:progress"))
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".App"
|
android:name=".TSUDeskApp"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
@@ -13,14 +13,14 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.TSUDesk">
|
android:theme="@style/Theme.TSUDesk">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".SplashScreenActivity"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity android:name=".MainActivity" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,7 +1,24 @@
|
|||||||
package ru.fincode.tsudesk
|
package ru.fincode.tsudesk
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.util.Log
|
||||||
import dagger.hilt.android.HiltAndroidApp
|
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
|
@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 ru.fincode.tsudesk.feature.schedule.domain.usecase.GetScheduleUseCase
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
private const val LOG_TAG = "NETWORK_DEBUG"
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : ComponentActivity() {
|
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
|
package ru.fincode.tsudesk.core.common.model
|
||||||
|
|
||||||
sealed interface DataResult<out T> {
|
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,
|
// NewsEntity::class,
|
||||||
// GradeEntity::class,
|
// GradeEntity::class,
|
||||||
],
|
],
|
||||||
version = 1,
|
version = 2,
|
||||||
exportSchema = false
|
exportSchema = false
|
||||||
)
|
)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ android {
|
|||||||
minSdk = libs.versions.minSdk.get().toInt()
|
minSdk = libs.versions.minSdk.get().toInt()
|
||||||
consumerProguardFiles("consumer-rules.pro")
|
consumerProguardFiles("consumer-rules.pro")
|
||||||
}
|
}
|
||||||
|
buildFeatures {
|
||||||
|
buildConfig = true
|
||||||
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
@@ -32,7 +34,9 @@ android {
|
|||||||
jvmTarget = jvm.toString()
|
jvmTarget = jvm.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
kapt {
|
||||||
|
correctErrorTypes = true
|
||||||
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
kapt(libs.hilt.compiler)
|
kapt(libs.hilt.compiler)
|
||||||
implementation(libs.hilt.android)
|
implementation(libs.hilt.android)
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ android {
|
|||||||
jvmTarget = jvm.toString()
|
jvmTarget = jvm.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.core.ktx)
|
implementation(libs.core.ktx)
|
||||||
implementation(libs.androidx.appcompat)
|
implementation(libs.androidx.appcompat)
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ android {
|
|||||||
minSdk = libs.versions.minSdk.get().toInt()
|
minSdk = libs.versions.minSdk.get().toInt()
|
||||||
consumerProguardFiles("consumer-rules.pro")
|
consumerProguardFiles("consumer-rules.pro")
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ android {
|
|||||||
jvmTarget = jvm.toString()
|
jvmTarget = jvm.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.core.ktx)
|
implementation(libs.core.ktx)
|
||||||
implementation(libs.androidx.appcompat)
|
implementation(libs.androidx.appcompat)
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ android {
|
|||||||
jvmTarget = jvm.toString()
|
jvmTarget = jvm.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
kapt {
|
||||||
|
correctErrorTypes = true
|
||||||
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.androidx.appcompat)
|
implementation(libs.androidx.appcompat)
|
||||||
implementation(libs.material)
|
implementation(libs.material)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package ru.fincode.tsudesk.feature.schedule.data
|
package ru.fincode.tsudesk.feature.schedule.data
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import ru.fincode.tsudesk.core.common.model.DataResult
|
import ru.fincode.tsudesk.core.common.model.DataResult
|
||||||
@@ -23,18 +22,29 @@ class ScheduleRepositoryImpl @Inject constructor(
|
|||||||
) : ScheduleRepository {
|
) : ScheduleRepository {
|
||||||
|
|
||||||
override fun observeSchedule(type: ScheduleType): Flow<DataResult<ScheduleEntity>> = flow {
|
override fun observeSchedule(type: ScheduleType): Flow<DataResult<ScheduleEntity>> = flow {
|
||||||
val key = when (type) {
|
val scheduleKey = type.toCacheKey()
|
||||||
is ScheduleType.Group -> ScheduleCacheKey.group(type.number)
|
val cached = local.observeSchedule(scheduleKey).first()
|
||||||
is ScheduleType.Teacher -> ScheduleCacheKey.teacher(type.name)
|
cached?.let {
|
||||||
|
emit(DataResult.Data(it, refreshedFromNetwork = false))
|
||||||
}
|
}
|
||||||
|
|
||||||
val cached: ScheduleEntity? = local.observeSchedule(key).first()
|
val networkResult = loadSchedule(type)
|
||||||
if (cached != null) {
|
if (networkResult is NetworkResult.Error) {
|
||||||
emit(DataResult.Data(cached, refreshedFromNetwork = false))
|
emit(DataResult.Error(networkResult.error.toAppError()))
|
||||||
|
return@flow
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (cached?.timestamp != updated.timestamp) { ... }
|
val networkSchedule = (networkResult as NetworkResult.Success).data
|
||||||
val networkResult: NetworkResult<ScheduleEntity> = when (type) {
|
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 ->
|
is ScheduleType.Group ->
|
||||||
remote.loadScheduleByGroup(type.number).map(mapper::invoke)
|
remote.loadScheduleByGroup(type.number).map(mapper::invoke)
|
||||||
|
|
||||||
@@ -42,18 +52,4 @@ class ScheduleRepositoryImpl @Inject constructor(
|
|||||||
remote.loadScheduleByTeacher(type.name).map(mapper::invoke)
|
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
|
package ru.fincode.tsudesk.feature.schedule.domain.model
|
||||||
|
|
||||||
|
import ru.fincode.tsudesk.feature.schedule.data.local.ScheduleCacheKey
|
||||||
|
|
||||||
sealed interface ScheduleType {
|
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"
|
agp = "8.12.0"
|
||||||
kotlin = "1.9.24"
|
kotlin = "1.9.24"
|
||||||
jvmTarget = "17"
|
jvmTarget = "17"
|
||||||
|
serilization = "1.6.3"
|
||||||
|
|
||||||
coreKtx = "1.10.1"
|
coreKtx = "1.10.1"
|
||||||
appcompat = "1.6.1"
|
appcompat = "1.6.1"
|
||||||
@@ -24,12 +25,16 @@ lifecycle = "2.7.0"
|
|||||||
coroutines = "1.8.1"
|
coroutines = "1.8.1"
|
||||||
|
|
||||||
room = "2.6.1"
|
room = "2.6.1"
|
||||||
|
junit = "4.13.2"
|
||||||
|
junitVersion = "1.1.5"
|
||||||
|
espressoCore = "3.5.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
# Android
|
# Android
|
||||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
|
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" }
|
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
|
||||||
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
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
|
# UI: AndroidX, Jetpack Compose
|
||||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||||
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
|
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-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
||||||
room-ktx = { group = "androidx.room", name = "room-ktx", 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" }
|
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]
|
[plugins]
|
||||||
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
|
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
android-library = { id = "com.android.library", 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-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", 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:ui")
|
||||||
include(":core:network")
|
include(":core:network")
|
||||||
include(":core:database")
|
include(":core:database")
|
||||||
|
include(":core:config")
|
||||||
|
|
||||||
include(":feature:schedule")
|
include(":feature:schedule")
|
||||||
include(":feature:news")
|
include(":feature:news")
|
||||||
|
|||||||
Reference in New Issue
Block a user