mirror of
https://github.com/n08i40k/polytechnic-android.git
synced 2025-12-06 09:47:48 +03:00
1.3.2
Фикс краша при получении расписания с включённым VPN. Допилено обновление расписания без перезагрузки всего экрана.
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package ru.n08i40k.polytechnic.next.data
|
||||
|
||||
import java.lang.Exception
|
||||
|
||||
sealed interface MyResult<out R> {
|
||||
data class Success<out T>(val data: T) : MyResult<T>
|
||||
data class Failure(val exception: Exception) : MyResult<Nothing>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Group>
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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<ScheduleGetResponse>()
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Profile> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val responseFuture = RequestFuture.newFuture<Profile>()
|
||||
UsersMeRequest(
|
||||
context,
|
||||
responseFuture,
|
||||
responseFuture
|
||||
).send()
|
||||
|
||||
try {
|
||||
MyResult.Success(responseFuture.get())
|
||||
} catch (exception: Exception) {
|
||||
MyResult.Failure(exception)
|
||||
tryFuture {
|
||||
UsersMeRequest(
|
||||
context,
|
||||
it,
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <ResultT, RequestT : RequestBase> tryFuture(
|
||||
buildRequest: (RequestFuture<ResultT>) -> RequestT
|
||||
): MyResult<ResultT> {
|
||||
val future = RequestFuture.newFuture<ResultT>()
|
||||
buildRequest(future).send()
|
||||
return tryGet(future)
|
||||
}
|
||||
|
||||
fun <T> tryGet(future: RequestFuture<T>): MyResult<T> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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<ScheduleGetCacheStatusResponse> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val mainPage = getMainPage()
|
||||
|
||||
if (mainPage is MyResult.Failure)
|
||||
return@withContext mainPage
|
||||
|
||||
val updateFuture = RequestFuture.newFuture<ScheduleGetCacheStatusResponse>()
|
||||
ScheduleUpdateRequest(
|
||||
ScheduleUpdateRequestData((mainPage as MyResult.Success<String>).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<ScheduleGetCacheStatusResponse>()
|
||||
|
||||
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) }
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ internal fun ProfileCard(profile: Profile = FakeProfileRepository.exampleProfile
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
context.profileViewModel!!.onUnauthorized()
|
||||
}) {
|
||||
Text(stringResource(R.string.sign_out))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<MainViewModel>(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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user