From 4db3a7d1c27cdcd82d3285e189b2cbe86c1f8965 Mon Sep 17 00:00:00 2001 From: n08i40k Date: Wed, 2 Oct 2024 00:22:51 +0400 Subject: [PATCH] 1.3.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Фикс краша при получении расписания с включённым VPN. Допилено обновление расписания без перезагрузки всего экрана. --- .idea/appInsightsSettings.xml | 6 +- .idea/codeStyles/Project.xml | 124 ++++++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 + .idea/misc.xml | 2 +- app/build.gradle.kts | 4 +- .../n08i40k/polytechnic/next/data/MyResult.kt | 2 - .../cache/impl/LocalNetworkCacheRepository.kt | 8 +- .../next/data/schedule/ScheduleRepository.kt | 2 +- .../schedule/impl/FakeScheduleRepository.kt | 4 +- .../schedule/impl/RemoteScheduleRepository.kt | 25 ++-- .../data/users/impl/FakeProfileRepository.kt | 1 - .../users/impl/RemoteProfileRepository.kt | 19 +-- .../polytechnic/next/network/Request.kt | 25 ++++ .../next/network/data/CachedRequest.kt | 79 +++++------ .../polytechnic/next/ui/ExpandableCard.kt | 14 +- .../next/ui/main/profile/ProfileCard.kt | 2 +- .../next/ui/main/schedule/DayPager.kt | 8 +- .../next/ui/main/schedule/ScheduleScreen.kt | 28 ++-- .../next/ui/main/schedule/UpdateInfo.kt | 72 +++++----- .../next/ui/model/ScheduleViewModel.kt | 29 +++- 20 files changed, 312 insertions(+), 147 deletions(-) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml diff --git a/.idea/appInsightsSettings.xml b/.idea/appInsightsSettings.xml index bac18c6..f23fb71 100644 --- a/.idea/appInsightsSettings.xml +++ b/.idea/appInsightsSettings.xml @@ -20,9 +20,9 @@ diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..bce97c3 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,124 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 2904b84..4c85420 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -7,7 +7,7 @@ - + diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ef5c2a0..0b7bda7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -32,8 +32,8 @@ android { applicationId = "ru.n08i40k.polytechnic.next" minSdk = 26 targetSdk = 35 - versionCode = 7 - versionName = "1.3.1" + versionCode = 8 + versionName = "1.3.2" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/data/MyResult.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/data/MyResult.kt index 272e3b2..79c83c9 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/data/MyResult.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/data/MyResult.kt @@ -1,7 +1,5 @@ package ru.n08i40k.polytechnic.next.data -import java.lang.Exception - sealed interface MyResult { data class Success(val data: T) : MyResult data class Failure(val exception: Exception) : MyResult diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/data/cache/impl/LocalNetworkCacheRepository.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/data/cache/impl/LocalNetworkCacheRepository.kt index fe914ab..586282b 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/data/cache/impl/LocalNetworkCacheRepository.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/data/cache/impl/LocalNetworkCacheRepository.kt @@ -32,12 +32,12 @@ class LocalNetworkCacheRepository } override suspend fun get(url: String): CachedResponse? { - if (this.hash == null) - return null + // Если кешированого ответа нет, то возвращаем null + // Если хеши не совпадают и локальный хеш присутствует, то возвращаем null - val response = cacheMap[url] + val response = cacheMap[url] ?: return null - if (response?.hash != this.hash) + if (response.hash != this.hash && this.hash != null) return null return response diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/data/schedule/ScheduleRepository.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/data/schedule/ScheduleRepository.kt index d7198ac..62a952b 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/data/schedule/ScheduleRepository.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/data/schedule/ScheduleRepository.kt @@ -1,7 +1,7 @@ package ru.n08i40k.polytechnic.next.data.schedule -import ru.n08i40k.polytechnic.next.model.Group import ru.n08i40k.polytechnic.next.data.MyResult +import ru.n08i40k.polytechnic.next.model.Group interface ScheduleRepository { suspend fun getGroup(): MyResult diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/data/schedule/impl/FakeScheduleRepository.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/data/schedule/impl/FakeScheduleRepository.kt index 8e83e35..2f678fc 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/data/schedule/impl/FakeScheduleRepository.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/data/schedule/impl/FakeScheduleRepository.kt @@ -4,13 +4,13 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.withContext +import ru.n08i40k.polytechnic.next.data.MyResult +import ru.n08i40k.polytechnic.next.data.schedule.ScheduleRepository import ru.n08i40k.polytechnic.next.model.Day import ru.n08i40k.polytechnic.next.model.Group -import ru.n08i40k.polytechnic.next.data.schedule.ScheduleRepository import ru.n08i40k.polytechnic.next.model.Lesson import ru.n08i40k.polytechnic.next.model.LessonTime import ru.n08i40k.polytechnic.next.model.LessonType -import ru.n08i40k.polytechnic.next.data.MyResult class FakeScheduleRepository : ScheduleRepository { @Suppress("SpellCheckingInspection") diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/data/schedule/impl/RemoteScheduleRepository.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/data/schedule/impl/RemoteScheduleRepository.kt index 198161c..9d42c3b 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/data/schedule/impl/RemoteScheduleRepository.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/data/schedule/impl/RemoteScheduleRepository.kt @@ -1,7 +1,6 @@ package ru.n08i40k.polytechnic.next.data.schedule.impl import android.content.Context -import com.android.volley.toolbox.RequestFuture import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map @@ -12,7 +11,7 @@ import ru.n08i40k.polytechnic.next.data.schedule.ScheduleRepository import ru.n08i40k.polytechnic.next.model.Group import ru.n08i40k.polytechnic.next.network.data.schedule.ScheduleGetRequest import ru.n08i40k.polytechnic.next.network.data.schedule.ScheduleGetRequestData -import ru.n08i40k.polytechnic.next.network.data.schedule.ScheduleGetResponse +import ru.n08i40k.polytechnic.next.network.tryFuture import ru.n08i40k.polytechnic.next.settings.settingsDataStore class RemoteScheduleRepository(private val context: Context) : ScheduleRepository { @@ -27,18 +26,18 @@ class RemoteScheduleRepository(private val context: Context) : ScheduleRepositor if (groupName.isEmpty()) return@withContext MyResult.Failure(IllegalArgumentException("No group name provided!")) - val future = RequestFuture.newFuture() - ScheduleGetRequest( - ScheduleGetRequestData(groupName), - context, - future, - future - ).send() + val response = tryFuture { + ScheduleGetRequest( + ScheduleGetRequestData(groupName), + context, + it, + it + ) + } - try { - MyResult.Success(future.get().group) - } catch (exception: Exception) { - MyResult.Failure(exception) + when (response) { + is MyResult.Failure -> response + is MyResult.Success -> MyResult.Success(response.data.group) } } } diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/data/users/impl/FakeProfileRepository.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/data/users/impl/FakeProfileRepository.kt index fbfe27a..4deeef6 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/data/users/impl/FakeProfileRepository.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/data/users/impl/FakeProfileRepository.kt @@ -7,7 +7,6 @@ import ru.n08i40k.polytechnic.next.data.MyResult import ru.n08i40k.polytechnic.next.data.users.ProfileRepository import ru.n08i40k.polytechnic.next.model.Profile import ru.n08i40k.polytechnic.next.model.UserRole -import java.lang.Exception class FakeProfileRepository : ProfileRepository { private var counter = 0 diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/data/users/impl/RemoteProfileRepository.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/data/users/impl/RemoteProfileRepository.kt index 3f5eab4..53916ec 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/data/users/impl/RemoteProfileRepository.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/data/users/impl/RemoteProfileRepository.kt @@ -1,28 +1,23 @@ package ru.n08i40k.polytechnic.next.data.users.impl import android.content.Context -import com.android.volley.toolbox.RequestFuture import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ru.n08i40k.polytechnic.next.data.MyResult import ru.n08i40k.polytechnic.next.data.users.ProfileRepository import ru.n08i40k.polytechnic.next.model.Profile import ru.n08i40k.polytechnic.next.network.data.profile.UsersMeRequest +import ru.n08i40k.polytechnic.next.network.tryFuture class RemoteProfileRepository(private val context: Context) : ProfileRepository { override suspend fun getProfile(): MyResult { return withContext(Dispatchers.IO) { - val responseFuture = RequestFuture.newFuture() - UsersMeRequest( - context, - responseFuture, - responseFuture - ).send() - - try { - MyResult.Success(responseFuture.get()) - } catch (exception: Exception) { - MyResult.Failure(exception) + tryFuture { + UsersMeRequest( + context, + it, + it + ) } } } diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/network/Request.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/network/Request.kt index 9d3d96c..f296e89 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/network/Request.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/network/Request.kt @@ -5,10 +5,15 @@ import android.content.Context import com.android.volley.Request import com.android.volley.RequestQueue import com.android.volley.Response +import com.android.volley.VolleyError import com.android.volley.toolbox.HurlStack +import com.android.volley.toolbox.RequestFuture import com.android.volley.toolbox.StringRequest import com.android.volley.toolbox.Volley +import ru.n08i40k.polytechnic.next.data.MyResult import java.security.cert.X509Certificate +import java.util.concurrent.ExecutionException +import java.util.concurrent.TimeoutException import java.util.logging.Logger import javax.net.ssl.SSLContext import javax.net.ssl.SSLSocketFactory @@ -82,3 +87,23 @@ open class RequestBase( return headers } } + +fun tryFuture( + buildRequest: (RequestFuture) -> RequestT +): MyResult { + val future = RequestFuture.newFuture() + buildRequest(future).send() + return tryGet(future) +} + +fun tryGet(future: RequestFuture): MyResult { + return try { + MyResult.Success(future.get()) + } catch (exception: VolleyError) { + MyResult.Failure(exception) + } catch (exception: ExecutionException) { + MyResult.Failure(exception.cause as VolleyError) + } catch (exception: TimeoutException) { + MyResult.Failure(exception) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/network/data/CachedRequest.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/network/data/CachedRequest.kt index a383eff..0012fc6 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/network/data/CachedRequest.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/network/data/CachedRequest.kt @@ -2,7 +2,6 @@ package ru.n08i40k.polytechnic.next.network.data import android.content.Context import com.android.volley.Response -import com.android.volley.VolleyError import com.android.volley.toolbox.RequestFuture import com.android.volley.toolbox.StringRequest import kotlinx.coroutines.Dispatchers @@ -16,6 +15,8 @@ import ru.n08i40k.polytechnic.next.network.data.schedule.ScheduleGetCacheStatusR import ru.n08i40k.polytechnic.next.network.data.schedule.ScheduleGetCacheStatusResponse import ru.n08i40k.polytechnic.next.network.data.schedule.ScheduleUpdateRequest import ru.n08i40k.polytechnic.next.network.data.schedule.ScheduleUpdateRequestData +import ru.n08i40k.polytechnic.next.network.tryFuture +import ru.n08i40k.polytechnic.next.network.tryGet import java.util.logging.Logger import kotlin.io.encoding.Base64 import kotlin.io.encoding.ExperimentalEncodingApi @@ -47,61 +48,58 @@ open class CachedRequest( ) NetworkConnection.getInstance(context).addToRequestQueue(request) - try { - val encodedMainPage = - Base64.Default.encode(mainPageFuture.get().encodeToByteArray()) - MyResult.Success(encodedMainPage) - } catch (exception: Exception) { - MyResult.Failure(exception) + when (val response = tryGet(mainPageFuture)) { + is MyResult.Failure -> response + is MyResult.Success -> { + val encodedMainPage = Base64.Default.encode(response.data.encodeToByteArray()) + MyResult.Success(encodedMainPage) + } } } } private suspend fun updateMainPage(): MyResult { return withContext(Dispatchers.IO) { - val mainPage = getMainPage() - - if (mainPage is MyResult.Failure) - return@withContext mainPage - - val updateFuture = RequestFuture.newFuture() - ScheduleUpdateRequest( - ScheduleUpdateRequestData((mainPage as MyResult.Success).data), - context, - updateFuture, - updateFuture - ).send() - - try { - MyResult.Success(updateFuture.get()) - } catch (exception: Exception) { - MyResult.Failure(exception) + when (val mainPage = getMainPage()) { + is MyResult.Failure -> mainPage + is MyResult.Success -> { + tryFuture { + ScheduleUpdateRequest( + ScheduleUpdateRequestData(mainPage.data), + context, + it, + it + ) + } + } } } } override fun send() { val logger = Logger.getLogger("CachedRequest") - val repository = appContainer.networkCacheRepository - val future = RequestFuture.newFuture() - logger.info("Getting cache status...") - ScheduleGetCacheStatusRequest(context, future, future).send() - try { - val response = future.get() + val cacheStatusResult = tryFuture { + ScheduleGetCacheStatusRequest(context, it, it) + } + + if (cacheStatusResult is MyResult.Success) { + val cacheStatus = cacheStatusResult.data logger.info("Cache status received successfully!") - if (!response.cacheUpdateRequired) { - logger.info("Cache update was not required!") - runBlocking { - repository.setUpdateDates(response.lastCacheUpdate, response.lastScheduleUpdate) - repository.setHash(response.cacheHash) - } - } else { + runBlocking { + repository.setUpdateDates( + cacheStatus.lastCacheUpdate, + cacheStatus.lastScheduleUpdate + ) + repository.setHash(cacheStatus.cacheHash) + } + + if (cacheStatus.cacheUpdateRequired) { logger.info("Cache update was required!") val updateResult = runBlocking { updateMainPage() } @@ -119,16 +117,11 @@ open class CachedRequest( is MyResult.Failure -> { logger.warning("Failed to update cache!") - super.getErrorListener() - ?.onErrorResponse(updateResult.exception.cause as VolleyError) - return } } } - } catch (exception: Exception) { + } else { logger.warning("Failed to get cache status!") - super.getErrorListener()?.onErrorResponse(exception.cause as VolleyError) - return } val cachedResponse = runBlocking { repository.get(url) } diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/ExpandableCard.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/ExpandableCard.kt index 3e97948..5e30cd4 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/ExpandableCard.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/ExpandableCard.kt @@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material3.Card +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -27,6 +28,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate +import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -46,10 +48,13 @@ fun ExpandableCard( val transition = rememberTransition(transitionState) - Card(modifier = modifier.clickable { - onExpandedChange() - transitionState.targetState = expanded - }) { + Card( + modifier = modifier.clickable { + onExpandedChange() + transitionState.targetState = expanded + }, + shape = RectangleShape + ) { Column { ExpandableCardHeader(title, transition) ExpandableCardContent(visible = expanded, content = content) @@ -83,6 +88,7 @@ private fun ExpandableCardContent( enter = enterTransition, exit = exitTransition ) { + HorizontalDivider() content() } } diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/profile/ProfileCard.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/profile/ProfileCard.kt index 021aca8..2b7a557 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/profile/ProfileCard.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/profile/ProfileCard.kt @@ -149,7 +149,7 @@ internal fun ProfileCard(profile: Profile = FakeProfileRepository.exampleProfile .build() } } - + context.profileViewModel!!.onUnauthorized() }) { Text(stringResource(R.string.sign_out)) diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/DayPager.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/DayPager.kt index 836d4f8..d1a41a3 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/DayPager.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/DayPager.kt @@ -1,6 +1,7 @@ package ru.n08i40k.polytechnic.next.ui.main.schedule import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.height import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.runtime.Composable @@ -28,7 +29,8 @@ fun DayPager(group: Group = FakeScheduleRepository.exampleGroup) { HorizontalPager( state = pagerState, contentPadding = PaddingValues(horizontal = 20.dp), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.Top, + modifier = Modifier.height(600.dp) ) { page -> DayCard( modifier = Modifier.graphicsLayer { @@ -37,8 +39,8 @@ fun DayPager(group: Group = FakeScheduleRepository.exampleGroup) { lerp( start = 0.95f, stop = 1f, fraction = 1f - offset.coerceIn(0f, 1f) ).also { scale -> - scaleX = scale - scaleY = scale + scaleX = 1F - scale + 0.95F + scaleY = 1F - scale + 0.95F } alpha = lerp( start = 0.5f, stop = 1f, fraction = 1f - offset.coerceIn(0f, 1f) diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/ScheduleScreen.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/ScheduleScreen.kt index 580bc15..2fdaffe 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/ScheduleScreen.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/ScheduleScreen.kt @@ -1,12 +1,8 @@ package ru.n08i40k.polytechnic.next.ui.main.schedule -import androidx.activity.ComponentActivity import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -16,10 +12,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import ru.n08i40k.polytechnic.next.MainViewModel import ru.n08i40k.polytechnic.next.R import ru.n08i40k.polytechnic.next.data.MockAppContainer import ru.n08i40k.polytechnic.next.ui.LoadingContent @@ -35,25 +28,20 @@ fun ScheduleScreen( val uiState by scheduleViewModel.uiState.collectAsStateWithLifecycle() LoadingContent( - empty = uiState.isLoading, + empty = when (uiState) { + is ScheduleUiState.NoSchedule -> uiState.isLoading + is ScheduleUiState.HasSchedule -> false + }, loading = uiState.isLoading, onRefresh = { onRefreshSchedule() }, verticalArrangement = Arrangement.Top ) { when (uiState) { is ScheduleUiState.HasSchedule -> { - Box { - val networkCacheRepository = - hiltViewModel(LocalContext.current as ComponentActivity) - .appContainer - .networkCacheRepository - - UpdateInfo(networkCacheRepository) - - Column { - Spacer(modifier = Modifier.height(200.dp)) - DayPager((uiState as ScheduleUiState.HasSchedule).group) - } + Column { + val hasSchedule = uiState as ScheduleUiState.HasSchedule + UpdateInfo(hasSchedule.lastUpdateAt, hasSchedule.updateDates) + DayPager(hasSchedule.group) } } diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/UpdateInfo.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/UpdateInfo.kt index 3986a11..96a1d8e 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/UpdateInfo.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/main/schedule/UpdateInfo.kt @@ -1,36 +1,26 @@ package ru.n08i40k.polytechnic.next.ui.main.schedule -import androidx.activity.ComponentActivity import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.Saver -import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel -import kotlinx.coroutines.runBlocking -import ru.n08i40k.polytechnic.next.MainViewModel import ru.n08i40k.polytechnic.next.R -import ru.n08i40k.polytechnic.next.data.cache.NetworkCacheRepository -import ru.n08i40k.polytechnic.next.data.cache.impl.FakeNetworkCacheRepository +import ru.n08i40k.polytechnic.next.UpdateDates import ru.n08i40k.polytechnic.next.ui.ExpandableCard -import ru.n08i40k.polytechnic.next.ui.model.ScheduleViewModel import java.text.SimpleDateFormat -import java.util.Calendar import java.util.Date import java.util.Locale @@ -40,24 +30,21 @@ fun Date.toString(format: String, locale: Locale = Locale.getDefault()): String return formatter.format(this) } -fun getCurrentDateTime(): Date { - return Calendar.getInstance().time -} - val expanded = mutableStateOf(false) @Preview(showBackground = true) @Composable -fun UpdateInfo(networkCacheRepository: NetworkCacheRepository = FakeNetworkCacheRepository()) { +fun UpdateInfo( + lastUpdateAt: Long = 0, + updateDates: UpdateDates = UpdateDates.newBuilder().build() +) { var expanded by remember { expanded } - val format = "hh:mm:ss dd.MM.yyyy" + val format = "HH:mm:ss dd.MM.yyyy" - val updateDates = remember { runBlocking { networkCacheRepository.getUpdateDates() } } - - val currentDate = remember { getCurrentDateTime().toString(format) } - val cacheUpdateDate = remember { Date(updateDates.cache).toString(format) } - val scheduleUpdateDate = remember { Date(updateDates.schedule).toString(format) } + val currentDate = Date(lastUpdateAt).toString(format) + val cacheUpdateDate = Date(updateDates.cache).toString(format) + val scheduleUpdateDate = Date(updateDates.schedule).toString(format) ExpandableCard( expanded = expanded, @@ -69,19 +56,40 @@ fun UpdateInfo(networkCacheRepository: NetworkCacheRepository = FakeNetworkCache .fillMaxWidth() .padding(10.dp) ) { - Row(horizontalArrangement = Arrangement.Center) { - Text(text = stringResource(R.string.last_local_update) + " - ") - Text(text = currentDate, fontWeight = FontWeight.Bold) + Row( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth() + ) { + Text(text = stringResource(R.string.last_local_update)) + Text( + text = currentDate, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace + ) } - Row(horizontalArrangement = Arrangement.Center) { - Text(text = stringResource(R.string.last_server_cache_update) + " - ") - Text(text = cacheUpdateDate, fontWeight = FontWeight.Bold) + Row( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth() + ) { + Text(text = stringResource(R.string.last_server_cache_update)) + Text( + text = cacheUpdateDate, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace + ) } - Row(horizontalArrangement = Arrangement.Center) { - Text(text = stringResource(R.string.last_server_schedule_update) + " - ") - Text(text = scheduleUpdateDate, fontWeight = FontWeight.Bold) + Row( + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth() + ) { + Text(text = stringResource(R.string.last_server_schedule_update)) + Text( + text = scheduleUpdateDate, + fontWeight = FontWeight.Bold, + fontFamily = FontFamily.Monospace + ) } } diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/model/ScheduleViewModel.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/model/ScheduleViewModel.kt index 07fdc5e..7e45e7c 100644 --- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/model/ScheduleViewModel.kt +++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/model/ScheduleViewModel.kt @@ -9,9 +9,12 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import ru.n08i40k.polytechnic.next.UpdateDates import ru.n08i40k.polytechnic.next.data.AppContainer import ru.n08i40k.polytechnic.next.data.MyResult import ru.n08i40k.polytechnic.next.model.Group +import java.util.Date +import java.util.logging.Logger import javax.inject.Inject sealed interface ScheduleUiState { @@ -23,18 +26,22 @@ sealed interface ScheduleUiState { data class HasSchedule( val group: Group, + val updateDates: UpdateDates, + val lastUpdateAt: Long, override val isLoading: Boolean ) : ScheduleUiState } private data class ScheduleViewModelState( val group: Group? = null, + val updateDates: UpdateDates? = null, + val lastUpdateAt: Long = 0, val isLoading: Boolean = false ) { fun toUiState(): ScheduleUiState = if (group == null) { ScheduleUiState.NoSchedule(isLoading) } else { - ScheduleUiState.HasSchedule(group, isLoading) + ScheduleUiState.HasSchedule(group, updateDates!!, lastUpdateAt, isLoading) } } @@ -43,6 +50,7 @@ class ScheduleViewModel @Inject constructor( appContainer: AppContainer ) : ViewModel() { private val scheduleRepository = appContainer.scheduleRepository + private val networkCacheRepository = appContainer.networkCacheRepository private val viewModelState = MutableStateFlow(ScheduleViewModelState(isLoading = true)) val uiState = viewModelState @@ -61,8 +69,23 @@ class ScheduleViewModel @Inject constructor( viewModelState.update { when (result) { - is MyResult.Success -> it.copy(group = result.data, isLoading = false) - is MyResult.Failure -> it.copy(group = null, isLoading = false) + is MyResult.Success -> { + val updateDates = networkCacheRepository.getUpdateDates() + + Logger.getLogger("ScheduleViewModel").info("Updating...") + + it.copy( + group = result.data, + updateDates = updateDates, + lastUpdateAt = Date().time, + isLoading = false + ) + } + + is MyResult.Failure -> it.copy( + group = null, + isLoading = false + ) } } }