Impl base schedule navigation; add internal logger
This commit is contained in:
@@ -3,8 +3,6 @@ package ru.fincode.tsudesk
|
||||
import android.app.Application
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
|
||||
const val LOG_DEBUG_TAG = "NETWORK_DEBUG"
|
||||
|
||||
@HiltAndroidApp
|
||||
class App : Application() {
|
||||
override fun onCreate() {
|
||||
|
||||
@@ -6,8 +6,8 @@ import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.navigation
|
||||
import ru.fincode.tsudesk.core.navigation.AppRoute
|
||||
import ru.fincode.tsudesk.feature.schedule.ui.navigation.scheduleGraph
|
||||
import ru.fincode.tsudesk.feature.splash.ui.navigation.splashGraph
|
||||
import ru.fincode.tsudesk.feature.schedule.presentation.navigation.scheduleGraph
|
||||
import ru.fincode.tsudesk.feature.splash.presentation.navigation.splashGraph
|
||||
|
||||
@Composable
|
||||
fun AppNavHost(
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package ru.fincode.tsudesk.core.common.log
|
||||
|
||||
object Constants {
|
||||
const val LOG_DEBUG_TAG = "LOG_DEBUG_TAG"
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package ru.fincode.tsudesk.core.common.log
|
||||
|
||||
import android.util.Log
|
||||
|
||||
private fun Any.moduleTag(): String {
|
||||
val module = this::class.java.`package`?.name?.substringAfterLast(".")?.uppercase() ?: "UNKNOWN"
|
||||
|
||||
return "${Constants.LOG_DEBUG_TAG}:$module"
|
||||
}
|
||||
|
||||
fun Any.logD(message: String) {
|
||||
Log.d(moduleTag(), message)
|
||||
}
|
||||
|
||||
fun Any.logD(message: String, throwable: Throwable) {
|
||||
Log.d(moduleTag(), message, throwable)
|
||||
}
|
||||
|
||||
fun Any.logE(message: String) {
|
||||
Log.e(moduleTag(), message)
|
||||
}
|
||||
|
||||
fun Any.logE(message: String, throwable: Throwable) {
|
||||
Log.e(moduleTag(), message, throwable)
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package ru.fincode.tsudesk.core.network
|
||||
|
||||
object Constants {
|
||||
|
||||
}
|
||||
@@ -3,20 +3,18 @@ package ru.fincode.tsudesk.core.network.interceptor
|
||||
import android.util.Log
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import ru.fincode.tsudesk.core.common.log.logD
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
private const val LOG_DEBUG_TAG = "LOG_DEBUG_TAG"
|
||||
|
||||
@Singleton
|
||||
class DebugInterceptor @Inject constructor() : Interceptor {
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
|
||||
Log.d(LOG_DEBUG_TAG, "URL: ${request.url}")
|
||||
Log.d(LOG_DEBUG_TAG, "Method: ${request.method}")
|
||||
|
||||
logD("URL: ${request.url}")
|
||||
logD("Method: ${request.method}")
|
||||
return chain.proceed(request)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,18 +3,18 @@ package ru.fincode.tsudesk.core.network.interceptor
|
||||
import android.util.Log
|
||||
import okhttp3.Call
|
||||
import okhttp3.EventListener
|
||||
import ru.fincode.tsudesk.core.common.log.logD
|
||||
import java.io.IOException
|
||||
import java.net.InetAddress
|
||||
|
||||
private const val LOG_DEBUG_TAG = "LOG_DEBUG_TAG"
|
||||
|
||||
class NetEventLogger : EventListener() {
|
||||
override fun dnsStart(call: Call, domainName: String) {
|
||||
Log.d(LOG_DEBUG_TAG, "dnsStart: $domainName")
|
||||
logD("dnsStart: $domainName")
|
||||
}
|
||||
|
||||
override fun dnsEnd(call: Call, domainName: String, inetAddressList: List<InetAddress>) {
|
||||
Log.d(LOG_DEBUG_TAG, "dnsEnd: $domainName -> $inetAddressList")
|
||||
logD("dnsEnd: $domainName -> $inetAddressList")
|
||||
}
|
||||
|
||||
override fun connectStart(
|
||||
@@ -22,30 +22,30 @@ class NetEventLogger : EventListener() {
|
||||
inetSocketAddress: java.net.InetSocketAddress,
|
||||
proxy: java.net.Proxy
|
||||
) {
|
||||
Log.d(LOG_DEBUG_TAG, "connectStart: $inetSocketAddress proxy=$proxy")
|
||||
logD("connectStart: $inetSocketAddress proxy=$proxy")
|
||||
}
|
||||
|
||||
override fun secureConnectStart(call: Call) {
|
||||
Log.d(LOG_DEBUG_TAG, "tlsStart")
|
||||
logD("tlsStart")
|
||||
}
|
||||
|
||||
override fun secureConnectEnd(call: Call, handshake: okhttp3.Handshake?) {
|
||||
Log.d(LOG_DEBUG_TAG, "tlsEnd: $handshake")
|
||||
logD("tlsEnd: $handshake")
|
||||
}
|
||||
|
||||
override fun requestHeadersStart(call: Call) {
|
||||
Log.d(LOG_DEBUG_TAG, "reqHeadersStart")
|
||||
logD("reqHeadersStart")
|
||||
}
|
||||
|
||||
override fun responseHeadersStart(call: Call) {
|
||||
Log.d(LOG_DEBUG_TAG, "respHeadersStart")
|
||||
logD("respHeadersStart")
|
||||
}
|
||||
|
||||
override fun callFailed(call: Call, ioe: IOException) {
|
||||
Log.e(LOG_DEBUG_TAG, "callFailed", ioe)
|
||||
logD("callFailed", ioe)
|
||||
}
|
||||
|
||||
override fun callEnd(call: Call) {
|
||||
Log.d(LOG_DEBUG_TAG, "callEnd")
|
||||
logD("callEnd")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import ru.fincode.tsudesk.core.network.model.NetworkResult
|
||||
import ru.fincode.tsudesk.core.network.model.map
|
||||
import ru.fincode.tsudesk.feature.schedule.data.datasource.ScheduleLocalDataSource
|
||||
import ru.fincode.tsudesk.feature.schedule.data.datasource.ScheduleRemoteDataSource
|
||||
import ru.fincode.tsudesk.feature.schedule.data.local.ScheduleCacheKey
|
||||
import ru.fincode.tsudesk.feature.schedule.data.remote.ScheduleNetworkMapper
|
||||
import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleEntity
|
||||
import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleType
|
||||
@@ -23,9 +22,11 @@ class ScheduleRepositoryImpl @Inject constructor(
|
||||
|
||||
override fun observeSchedule(type: ScheduleType): Flow<DataResult<ScheduleEntity>> = flow {
|
||||
val scheduleKey = type.toCacheKey()
|
||||
|
||||
val cached = local.observeSchedule(scheduleKey).first()
|
||||
cached?.let {
|
||||
emit(DataResult.Data(it, refreshedFromNetwork = false))
|
||||
if (cached != null) {
|
||||
emit(DataResult.Data(cached, refreshedFromNetwork = false))
|
||||
if ((System.currentTimeMillis() - cached.timestamp) < CACHE_TTL_MS) return@flow
|
||||
}
|
||||
|
||||
val networkResult = loadSchedule(type)
|
||||
@@ -45,11 +46,12 @@ class ScheduleRepositoryImpl @Inject constructor(
|
||||
|
||||
private suspend fun loadSchedule(type: ScheduleType): NetworkResult<ScheduleEntity> =
|
||||
when (type) {
|
||||
is ScheduleType.Group ->
|
||||
remote.loadScheduleByGroup(type.number).map(mapper::invoke)
|
||||
is ScheduleType.Group -> remote.loadScheduleByGroup(type.number).map(mapper::invoke)
|
||||
|
||||
is ScheduleType.Teacher ->
|
||||
remote.loadScheduleByTeacher(type.name).map(mapper::invoke)
|
||||
is ScheduleType.Teacher -> remote.loadScheduleByTeacher(type.name).map(mapper::invoke)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val CACHE_TTL_MS: Long = 1;//24L * 60L * 60L * 1000L // 1 day
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleType
|
||||
import ru.fincode.tsudesk.feature.schedule.domain.repository.ScheduleRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetScheduleUseCase @Inject constructor(
|
||||
class ObserveScheduleUseCase @Inject constructor(
|
||||
private val repository: ScheduleRepository
|
||||
) {
|
||||
operator fun invoke(type: ScheduleType): Flow<DataResult<ScheduleEntity>> =
|
||||
@@ -0,0 +1,11 @@
|
||||
package ru.fincode.tsudesk.feature.schedule.presentation.model
|
||||
|
||||
import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleEntity
|
||||
|
||||
data class ScheduleUiState(
|
||||
val isLoading: Boolean = false,
|
||||
val data: ScheduleEntity? = null,
|
||||
val errorMessage: String? = null,
|
||||
val title: String = "Schedule",
|
||||
val refreshedFromNetwork: Boolean = false
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.fincode.tsudesk.feature.schedule.presentation.navigation
|
||||
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.composable
|
||||
import ru.fincode.tsudesk.core.navigation.AppRoute
|
||||
import ru.fincode.tsudesk.feature.schedule.presentation.screen.ScheduleRoute
|
||||
|
||||
fun NavGraphBuilder.scheduleGraph(
|
||||
navController: NavHostController
|
||||
) {
|
||||
composable<AppRoute.Schedule> {
|
||||
ScheduleRoute()
|
||||
}
|
||||
|
||||
// composable<AppRoute.ScheduleDetails> { backStackEntry ->
|
||||
// val args = backStackEntry.toRoute<AppRoute.ScheduleDetails>()
|
||||
// ScheduleDetailsRoute(lessonId = args.lessonId)
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package ru.fincode.tsudesk.feature.schedule.presentation.screen
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import ru.fincode.tsudesk.feature.schedule.presentation.screen.ScheduleViewModel
|
||||
import ru.fincode.tsudesk.feature.schedule.presentation.ui.ScheduleScreen
|
||||
|
||||
|
||||
@Composable
|
||||
fun ScheduleRoute(
|
||||
viewModel: ScheduleViewModel = hiltViewModel()
|
||||
) {
|
||||
val state = viewModel.state.collectAsStateWithLifecycle().value
|
||||
ScheduleScreen(state = state)
|
||||
}
|
||||
@@ -2,21 +2,26 @@ package ru.fincode.tsudesk.feature.schedule.presentation.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import ru.fincode.tsudesk.feature.schedule.presentation.model.ScheduleUiState
|
||||
|
||||
@Composable
|
||||
fun ScheduleScreen() {
|
||||
fun ScheduleScreen(
|
||||
state: ScheduleUiState
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = "Schedule",
|
||||
color = Color.Black
|
||||
)
|
||||
if (state.isLoading) {
|
||||
CircularProgressIndicator()
|
||||
} else {
|
||||
Text(text = state.title, color = Color.Black)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package ru.fincode.tsudesk.feature.schedule.presentation.screen
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import ru.fincode.tsudesk.core.common.log.logD
|
||||
import ru.fincode.tsudesk.core.common.model.DataResult
|
||||
import ru.fincode.tsudesk.feature.schedule.domain.model.ScheduleType
|
||||
import ru.fincode.tsudesk.feature.schedule.domain.usecase.ObserveScheduleUseCase
|
||||
import ru.fincode.tsudesk.feature.schedule.presentation.model.ScheduleUiState
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ScheduleViewModel @Inject constructor(
|
||||
private val observeScheduleUseCase: ObserveScheduleUseCase
|
||||
) : ViewModel() {
|
||||
|
||||
private val scheduleType = ScheduleType.Group(number = "220631")
|
||||
|
||||
val state: StateFlow<ScheduleUiState> = observeScheduleUseCase(scheduleType).map { result ->
|
||||
when (result) {
|
||||
is DataResult.Data -> {
|
||||
logD("Data loaded from ${if (result.refreshedFromNetwork) "NETWORK" else "CACHE"}")
|
||||
ScheduleUiState(
|
||||
isLoading = false,
|
||||
data = result.data,
|
||||
refreshedFromNetwork = result.refreshedFromNetwork
|
||||
)
|
||||
}
|
||||
|
||||
is DataResult.Error -> {
|
||||
logD("Error loading schedule: ${result.error}")
|
||||
ScheduleUiState(isLoading = false, errorMessage = result.error.toString())
|
||||
}
|
||||
}
|
||||
}.onStart {
|
||||
logD("Start collecting schedule flow")
|
||||
emit(ScheduleUiState(isLoading = true))
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(5000),
|
||||
initialValue = ScheduleUiState(isLoading = true)
|
||||
)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package ru.fincode.tsudesk.feature.schedule.ui.navigation
|
||||
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.composable
|
||||
import ru.fincode.tsudesk.core.navigation.AppRoute
|
||||
import ru.fincode.tsudesk.feature.schedule.presentation.ui.ScheduleScreen
|
||||
|
||||
fun NavGraphBuilder.scheduleGraph(
|
||||
navController: NavHostController
|
||||
) {
|
||||
composable<AppRoute.Schedule> {
|
||||
ScheduleScreen()
|
||||
}
|
||||
|
||||
// регистрация экрана детальной информации о занятии
|
||||
// composable<AppRoute.ScheduleDetails> { backStackEntry ->
|
||||
// // val args = backStackEntry.toRoute<AppRoute.ScheduleDetails>()
|
||||
// // ScheduleDetailsRoute(lessonId = args.lessonId)
|
||||
// }
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package ru.fincode.tsudesk.feature.splash.ui
|
||||
package ru.fincode.tsudesk.feature.splash.presentation.model
|
||||
|
||||
sealed interface SplashEffect {
|
||||
data object OpenMain : SplashEffect
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.fincode.tsudesk.feature.splash.presentation.model
|
||||
|
||||
data class SplashUiState(
|
||||
val title: String = "SPLASH SCREEN",
|
||||
val isLoading: Boolean = true,
|
||||
val errorMessage: String? = null
|
||||
)
|
||||
@@ -1,10 +1,10 @@
|
||||
package ru.fincode.tsudesk.feature.splash.ui.navigation
|
||||
package ru.fincode.tsudesk.feature.splash.presentation.navigation
|
||||
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.composable
|
||||
import ru.fincode.tsudesk.core.navigation.AppRoute
|
||||
import ru.fincode.tsudesk.feature.splash.ui.SplashRoute
|
||||
import ru.fincode.tsudesk.feature.splash.presentation.screen.SplashRoute
|
||||
|
||||
fun NavGraphBuilder.splashGraph(
|
||||
navController: NavHostController
|
||||
@@ -1,9 +1,10 @@
|
||||
package ru.fincode.tsudesk.feature.splash.ui
|
||||
package ru.fincode.tsudesk.feature.splash.presentation.screen
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import ru.fincode.tsudesk.feature.splash.presentation.model.SplashEffect
|
||||
|
||||
@Composable
|
||||
fun SplashRoute(
|
||||
@@ -1,4 +1,4 @@
|
||||
package ru.fincode.tsudesk.feature.splash.ui
|
||||
package ru.fincode.tsudesk.feature.splash.presentation.screen
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@@ -7,6 +7,7 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import ru.fincode.tsudesk.feature.splash.presentation.model.SplashUiState
|
||||
|
||||
@Composable
|
||||
fun SplashScreen(
|
||||
@@ -1,4 +1,4 @@
|
||||
package ru.fincode.tsudesk.feature.splash.ui
|
||||
package ru.fincode.tsudesk.feature.splash.presentation.screen
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
@@ -10,6 +10,8 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.fincode.tsudesk.core.common.model.DataResult
|
||||
import ru.fincode.tsudesk.core.config.domain.usecase.GetConfigUseCase
|
||||
import ru.fincode.tsudesk.feature.splash.presentation.model.SplashUiState
|
||||
import ru.fincode.tsudesk.feature.splash.presentation.model.SplashEffect
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@@ -1,7 +0,0 @@
|
||||
package ru.fincode.tsudesk.feature.splash.ui
|
||||
|
||||
data class SplashUiState(
|
||||
val title: String = "TSUDesk",
|
||||
val isLoading: Boolean = true,
|
||||
val errorMessage: String? = null
|
||||
)
|
||||
Reference in New Issue
Block a user