mirror of
https://github.com/n08i40k/polytechnic-android.git
synced 2025-12-06 17:57:46 +03:00
Compare commits
11 Commits
15894b290a
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3286df2b8a | ||
|
|
09871faaca | ||
|
|
d6fd1ae5ff | ||
|
|
e7eda47e13 | ||
|
|
6d9f6c6b4e | ||
|
|
b9cf49023a | ||
|
|
f8dee292dd | ||
|
|
c6fc268c19 | ||
|
|
3400015895 | ||
|
dbc1afcf28
|
|||
|
96f84b9f54
|
6
.idea/kotlinc.xml
generated
6
.idea/kotlinc.xml
generated
@@ -7,10 +7,10 @@
|
|||||||
<option name="jvmTarget" value="1.8" />
|
<option name="jvmTarget" value="1.8" />
|
||||||
</component>
|
</component>
|
||||||
<component name="KotlinCommonCompilerArguments">
|
<component name="KotlinCommonCompilerArguments">
|
||||||
<option name="apiVersion" value="2.0" />
|
<option name="apiVersion" value="2.1" />
|
||||||
<option name="languageVersion" value="2.0" />
|
<option name="languageVersion" value="2.1" />
|
||||||
</component>
|
</component>
|
||||||
<component name="KotlinJpsPluginSettings">
|
<component name="KotlinJpsPluginSettings">
|
||||||
<option name="version" value="2.0.10" />
|
<option name="version" value="2.1.10" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -7,7 +7,7 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
alias(libs.plugins.kotlin.compose)
|
alias(libs.plugins.kotlin.compose)
|
||||||
|
|
||||||
kotlin("plugin.serialization") version "2.1.10"
|
kotlin("plugin.serialization") version "2.1.20"
|
||||||
id("kotlin-parcelize")
|
id("kotlin-parcelize")
|
||||||
|
|
||||||
id("com.google.devtools.ksp")
|
id("com.google.devtools.ksp")
|
||||||
@@ -46,8 +46,8 @@ android {
|
|||||||
applicationId = "ru.n08i40k.polytechnic.next"
|
applicationId = "ru.n08i40k.polytechnic.next"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 27
|
versionCode = 29
|
||||||
versionName = "3.1.0"
|
versionName = "3.2.0"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
@@ -136,6 +136,7 @@ dependencies {
|
|||||||
implementation(libs.kotlinx.datetime)
|
implementation(libs.kotlinx.datetime)
|
||||||
|
|
||||||
// default
|
// default
|
||||||
|
implementation(libs.androidx.runtime.tracing)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
@@ -158,7 +159,7 @@ dependencies {
|
|||||||
|
|
||||||
protobuf {
|
protobuf {
|
||||||
protoc {
|
protoc {
|
||||||
artifact = "com.google.protobuf:protoc:4.29.3"
|
artifact = "com.google.protobuf:protoc:21.0-rc-1"
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ import kotlinx.coroutines.flow.first
|
|||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import ru.n08i40k.polytechnic.next.app.AppContainer
|
import ru.n08i40k.polytechnic.next.app.AppContainer
|
||||||
import ru.n08i40k.polytechnic.next.settings.settings
|
import ru.n08i40k.polytechnic.next.proto.settings
|
||||||
|
import ru.n08i40k.polytechnic.next.proto.settings_v0
|
||||||
import ru.n08i40k.polytechnic.next.utils.Observable
|
import ru.n08i40k.polytechnic.next.utils.Observable
|
||||||
import ru.n08i40k.polytechnic.next.worker.UpdateFCMTokenWorker
|
import ru.n08i40k.polytechnic.next.worker.UpdateFCMTokenWorker
|
||||||
import ru.n08i40k.polytechnic.next.worker.UpdateLinkWorker
|
import ru.n08i40k.polytechnic.next.worker.UpdateLinkWorker
|
||||||
@@ -97,8 +98,35 @@ class Application : Application() {
|
|||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
|
runBlocking { fixupSettings() }
|
||||||
|
|
||||||
VKID.init(this)
|
VKID.init(this)
|
||||||
|
|
||||||
setupFirebase()
|
setupFirebase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun fixupSettings() {
|
||||||
|
val accessToken = this.settings_v0.data.map { it.accessToken }.first()
|
||||||
|
|
||||||
|
if (accessToken.isEmpty())
|
||||||
|
return
|
||||||
|
|
||||||
|
val userId = this.settings_v0.data.map { it.userId }.first()
|
||||||
|
val group = this.settings_v0.data.map { it.group }.first()
|
||||||
|
val version = this.settings_v0.data.map { it.version }.first()
|
||||||
|
val fcmToken = this.settings_v0.data.map { it.fcmToken }.first()
|
||||||
|
|
||||||
|
this.settings.updateData {
|
||||||
|
it
|
||||||
|
.toBuilder()
|
||||||
|
.setUserId(userId)
|
||||||
|
.setAccessToken(accessToken)
|
||||||
|
.setGroup(group)
|
||||||
|
.setVersion(version)
|
||||||
|
.setFcmToken(fcmToken)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.settings_v0.updateData { it.toBuilder().clear().build() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import ru.n08i40k.polytechnic.next.app.NotificationChannels
|
import ru.n08i40k.polytechnic.next.app.NotificationChannels
|
||||||
import ru.n08i40k.polytechnic.next.settings.settings
|
import ru.n08i40k.polytechnic.next.proto.settings
|
||||||
import ru.n08i40k.polytechnic.next.ui.PolytechnicApp
|
import ru.n08i40k.polytechnic.next.ui.PolytechnicApp
|
||||||
import ru.n08i40k.polytechnic.next.ui.theme.AppTheme
|
import ru.n08i40k.polytechnic.next.ui.theme.AppTheme
|
||||||
import ru.n08i40k.polytechnic.next.utils.app
|
import ru.n08i40k.polytechnic.next.utils.app
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.os.Parcelable
|
|||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import ru.n08i40k.polytechnic.next.R
|
import ru.n08i40k.polytechnic.next.R
|
||||||
|
import ru.n08i40k.polytechnic.next.utils.dayMinutes
|
||||||
import ru.n08i40k.polytechnic.next.utils.limit
|
import ru.n08i40k.polytechnic.next.utils.limit
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
@@ -17,6 +18,8 @@ data class Lesson(
|
|||||||
val group: String? = null,
|
val group: String? = null,
|
||||||
val subGroups: List<SubGroup>
|
val subGroups: List<SubGroup>
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
val duration: Int get() = time.end.dayMinutes - time.start.dayMinutes
|
||||||
|
|
||||||
fun getShortName(context: Context): String {
|
fun getShortName(context: Context): String {
|
||||||
val name =
|
val name =
|
||||||
if (type == LessonType.BREAK)
|
if (type == LessonType.BREAK)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import ru.n08i40k.polytechnic.next.app.AppContainer
|
import ru.n08i40k.polytechnic.next.app.AppContainer
|
||||||
import ru.n08i40k.polytechnic.next.network.RequestBase
|
import ru.n08i40k.polytechnic.next.network.RequestBase
|
||||||
import ru.n08i40k.polytechnic.next.settings.settings
|
import ru.n08i40k.polytechnic.next.proto.settings
|
||||||
|
|
||||||
open class AuthorizedRequest(
|
open class AuthorizedRequest(
|
||||||
val appContainer: AppContainer,
|
val appContainer: AppContainer,
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package ru.n08i40k.polytechnic.next.proto
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.datastore.core.CorruptionException
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.core.Serializer
|
||||||
|
import androidx.datastore.dataStore
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException
|
||||||
|
import ru.n08i40k.polytechnic.next.Cache
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
object CacheSerializer : Serializer<Cache> {
|
||||||
|
override val defaultValue: Cache = Cache.getDefaultInstance()
|
||||||
|
|
||||||
|
override suspend fun readFrom(input: InputStream): Cache =
|
||||||
|
try {
|
||||||
|
Cache.parseFrom(input)
|
||||||
|
} catch (exception: InvalidProtocolBufferException) {
|
||||||
|
throw CorruptionException("Cannot read proto.", exception)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun writeTo(t: Cache, output: OutputStream) = t.writeTo(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
val Context.cache: DataStore<Cache> by dataStore(
|
||||||
|
fileName = "cache.pb",
|
||||||
|
serializer = CacheSerializer
|
||||||
|
)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ru.n08i40k.polytechnic.next.settings
|
package ru.n08i40k.polytechnic.next.proto
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.datastore.core.CorruptionException
|
import androidx.datastore.core.CorruptionException
|
||||||
@@ -23,7 +23,7 @@ object SettingsSerializer : Serializer<Settings> {
|
|||||||
override suspend fun writeTo(t: Settings, output: OutputStream) = t.writeTo(output)
|
override suspend fun writeTo(t: Settings, output: OutputStream) = t.writeTo(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
val Context.settings: DataStore<Settings> by dataStore(
|
val Context.settings_v0: DataStore<Settings> by dataStore(
|
||||||
fileName = "settings.pb",
|
fileName = "settings.pb",
|
||||||
serializer = SettingsSerializer
|
serializer = SettingsSerializer
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package ru.n08i40k.polytechnic.next.proto
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.datastore.core.CorruptionException
|
||||||
|
import androidx.datastore.core.DataStore
|
||||||
|
import androidx.datastore.core.Serializer
|
||||||
|
import androidx.datastore.dataStore
|
||||||
|
import com.google.protobuf.InvalidProtocolBufferException
|
||||||
|
import ru.n08i40k.polytechnic.next.SettingsV2
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
|
object SettingsV2Serializer : Serializer<SettingsV2> {
|
||||||
|
override val defaultValue: SettingsV2 = SettingsV2.getDefaultInstance()
|
||||||
|
|
||||||
|
override suspend fun readFrom(input: InputStream): SettingsV2 =
|
||||||
|
try {
|
||||||
|
SettingsV2.parseFrom(input)
|
||||||
|
} catch (exception: InvalidProtocolBufferException) {
|
||||||
|
throw CorruptionException("Cannot read proto.", exception)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun writeTo(t: SettingsV2, output: OutputStream) = t.writeTo(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
val Context.settings: DataStore<SettingsV2> by dataStore(
|
||||||
|
fileName = "settings-v2.pb",
|
||||||
|
serializer = SettingsV2Serializer
|
||||||
|
)
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package ru.n08i40k.polytechnic.next.repository.cache
|
package ru.n08i40k.polytechnic.next.repository.cache
|
||||||
|
|
||||||
import ru.n08i40k.polytechnic.next.CachedResponse
|
import ru.n08i40k.polytechnic.next.CacheDate
|
||||||
import ru.n08i40k.polytechnic.next.UpdateDates
|
import ru.n08i40k.polytechnic.next.CacheResponse
|
||||||
|
|
||||||
interface NetworkCacheRepository {
|
interface NetworkCacheRepository {
|
||||||
suspend fun put(url: String, data: String)
|
suspend fun put(url: String, data: String)
|
||||||
|
|
||||||
suspend fun get(url: String): CachedResponse?
|
suspend fun get(url: String): CacheResponse?
|
||||||
|
|
||||||
suspend fun clear()
|
suspend fun clear()
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ interface NetworkCacheRepository {
|
|||||||
|
|
||||||
suspend fun setHash(hash: String)
|
suspend fun setHash(hash: String)
|
||||||
|
|
||||||
suspend fun getUpdateDates(): UpdateDates
|
suspend fun getUpdateDates(): CacheDate
|
||||||
|
|
||||||
suspend fun setUpdateDates(cache: Long, schedule: Long)
|
suspend fun setUpdateDates(cache: Long, schedule: Long)
|
||||||
}
|
}
|
||||||
@@ -5,17 +5,18 @@ import kotlinx.coroutines.flow.first
|
|||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import ru.n08i40k.polytechnic.next.CachedResponse
|
import ru.n08i40k.polytechnic.next.CacheDate
|
||||||
import ru.n08i40k.polytechnic.next.UpdateDates
|
import ru.n08i40k.polytechnic.next.CacheResponse
|
||||||
import ru.n08i40k.polytechnic.next.app.AppContainer
|
import ru.n08i40k.polytechnic.next.app.AppContainer
|
||||||
|
import ru.n08i40k.polytechnic.next.proto.cache
|
||||||
import ru.n08i40k.polytechnic.next.repository.cache.NetworkCacheRepository
|
import ru.n08i40k.polytechnic.next.repository.cache.NetworkCacheRepository
|
||||||
import ru.n08i40k.polytechnic.next.settings.settings
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class LocalNetworkCacheRepository
|
class LocalNetworkCacheRepository
|
||||||
@Inject constructor(private val appContainer: AppContainer) : NetworkCacheRepository {
|
@Inject constructor(private val appContainer: AppContainer) : NetworkCacheRepository {
|
||||||
private val cacheMap: MutableMap<String, CachedResponse> = mutableMapOf()
|
private val cacheMap: MutableMap<String, CacheResponse> = mutableMapOf()
|
||||||
private var updateDates: UpdateDates = UpdateDates.newBuilder().build()
|
private var cacheDate: CacheDate = CacheDate.newBuilder().build()
|
||||||
|
|
||||||
private var hash: String? = null
|
private var hash: String? = null
|
||||||
|
|
||||||
private val context get() = appContainer.context
|
private val context get() = appContainer.context
|
||||||
@@ -26,14 +27,14 @@ class LocalNetworkCacheRepository
|
|||||||
runBlocking {
|
runBlocking {
|
||||||
cacheMap.putAll(
|
cacheMap.putAll(
|
||||||
context
|
context
|
||||||
.settings
|
.cache
|
||||||
.data
|
.data
|
||||||
.map { settings -> settings.cacheStorageMap }.first()
|
.map { it.storageMap }.first()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun get(url: String): CachedResponse? {
|
override suspend fun get(url: String): CacheResponse? {
|
||||||
// Если кешированного ответа нет, то возвращаем null
|
// Если кешированного ответа нет, то возвращаем null
|
||||||
// Если хеши не совпадают и локальный хеш присутствует, то возвращаем null
|
// Если хеши не совпадают и локальный хеш присутствует, то возвращаем null
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ class LocalNetworkCacheRepository
|
|||||||
if (hash == null)
|
if (hash == null)
|
||||||
throw IllegalStateException("Не установлен хеш!")
|
throw IllegalStateException("Не установлен хеш!")
|
||||||
|
|
||||||
cacheMap[url] = CachedResponse
|
cacheMap[url] = CacheResponse
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.setHash(this.hash)
|
.setHash(this.hash)
|
||||||
.setData(data)
|
.setData(data)
|
||||||
@@ -83,21 +84,21 @@ class LocalNetworkCacheRepository
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getUpdateDates(): UpdateDates {
|
override suspend fun getUpdateDates(): CacheDate {
|
||||||
return this.updateDates
|
return this.cacheDate
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun setUpdateDates(cache: Long, schedule: Long) {
|
override suspend fun setUpdateDates(cache: Long, schedule: Long) {
|
||||||
updateDates = UpdateDates
|
cacheDate = CacheDate
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.setCache(cache)
|
.setCache(cache)
|
||||||
.setSchedule(schedule).build()
|
.setSchedule(schedule).build()
|
||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
context.settings.updateData {
|
context.cache.updateData {
|
||||||
it
|
it
|
||||||
.toBuilder()
|
.toBuilder()
|
||||||
.setUpdateDates(updateDates)
|
.setDate(cacheDate)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,10 +107,10 @@ class LocalNetworkCacheRepository
|
|||||||
|
|
||||||
private suspend fun save() {
|
private suspend fun save() {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
context.settings.updateData {
|
context.cache.updateData {
|
||||||
it
|
it
|
||||||
.toBuilder()
|
.toBuilder()
|
||||||
.putAllCacheStorage(cacheMap)
|
.putAllStorage(cacheMap)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package ru.n08i40k.polytechnic.next.repository.cache.impl
|
package ru.n08i40k.polytechnic.next.repository.cache.impl
|
||||||
|
|
||||||
import ru.n08i40k.polytechnic.next.CachedResponse
|
import ru.n08i40k.polytechnic.next.CacheDate
|
||||||
import ru.n08i40k.polytechnic.next.UpdateDates
|
import ru.n08i40k.polytechnic.next.CacheResponse
|
||||||
import ru.n08i40k.polytechnic.next.repository.cache.NetworkCacheRepository
|
import ru.n08i40k.polytechnic.next.repository.cache.NetworkCacheRepository
|
||||||
|
|
||||||
class MockNetworkCacheRepository : NetworkCacheRepository {
|
class MockNetworkCacheRepository : NetworkCacheRepository {
|
||||||
override suspend fun get(url: String): CachedResponse? {
|
override suspend fun get(url: String): CacheResponse? {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ class MockNetworkCacheRepository : NetworkCacheRepository {
|
|||||||
|
|
||||||
override suspend fun setHash(hash: String) {}
|
override suspend fun setHash(hash: String) {}
|
||||||
|
|
||||||
override suspend fun getUpdateDates(): UpdateDates {
|
override suspend fun getUpdateDates(): CacheDate {
|
||||||
return UpdateDates.newBuilder().build()
|
return CacheDate.newBuilder().build()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun setUpdateDates(cache: Long, schedule: Long) {}
|
override suspend fun setUpdateDates(cache: Long, schedule: Long) {}
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import ru.n08i40k.polytechnic.next.model.Profile
|
|||||||
import ru.n08i40k.polytechnic.next.network.request.fcm.FcmSetToken
|
import ru.n08i40k.polytechnic.next.network.request.fcm.FcmSetToken
|
||||||
import ru.n08i40k.polytechnic.next.network.request.profile.ProfileMe
|
import ru.n08i40k.polytechnic.next.network.request.profile.ProfileMe
|
||||||
import ru.n08i40k.polytechnic.next.network.tryFuture
|
import ru.n08i40k.polytechnic.next.network.tryFuture
|
||||||
|
import ru.n08i40k.polytechnic.next.proto.cache
|
||||||
|
import ru.n08i40k.polytechnic.next.proto.settings
|
||||||
import ru.n08i40k.polytechnic.next.repository.profile.ProfileRepository
|
import ru.n08i40k.polytechnic.next.repository.profile.ProfileRepository
|
||||||
import ru.n08i40k.polytechnic.next.settings.settings
|
|
||||||
import ru.n08i40k.polytechnic.next.utils.MyResult
|
import ru.n08i40k.polytechnic.next.utils.MyResult
|
||||||
import ru.n08i40k.polytechnic.next.utils.app
|
import ru.n08i40k.polytechnic.next.utils.app
|
||||||
|
|
||||||
@@ -40,7 +41,14 @@ class RemoteProfileRepository(private val container: AppContainer) : ProfileRepo
|
|||||||
override suspend fun signOut() {
|
override suspend fun signOut() {
|
||||||
val context = container.context
|
val context = container.context
|
||||||
|
|
||||||
container.context.settings.updateData {
|
context.settings.updateData {
|
||||||
|
it
|
||||||
|
.toBuilder()
|
||||||
|
.clear()
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
context.cache.updateData {
|
||||||
it
|
it
|
||||||
.toBuilder()
|
.toBuilder()
|
||||||
.clear()
|
.clear()
|
||||||
@@ -48,12 +56,5 @@ class RemoteProfileRepository(private val container: AppContainer) : ProfileRepo
|
|||||||
}
|
}
|
||||||
|
|
||||||
context.app.events.signOut.next(Unit)
|
context.app.events.signOut.next(Unit)
|
||||||
|
|
||||||
// context.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)
|
|
||||||
// val pm = context.packageManager
|
|
||||||
// val intent = pm.getLaunchIntentForPackage(context.packageName)
|
|
||||||
// val mainIntent = Intent.makeRestartActivityTask(intent?.component)
|
|
||||||
// context.startActivity(mainIntent)
|
|
||||||
// Runtime.getRuntime().exit(0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import ru.n08i40k.polytechnic.next.Application
|
import ru.n08i40k.polytechnic.next.Application
|
||||||
import ru.n08i40k.polytechnic.next.R
|
import ru.n08i40k.polytechnic.next.R
|
||||||
import ru.n08i40k.polytechnic.next.settings.settings
|
import ru.n08i40k.polytechnic.next.proto.settings
|
||||||
import ru.n08i40k.polytechnic.next.ui.screen.MainScreen
|
import ru.n08i40k.polytechnic.next.ui.screen.MainScreen
|
||||||
import ru.n08i40k.polytechnic.next.ui.screen.auth.AuthScreen
|
import ru.n08i40k.polytechnic.next.ui.screen.auth.AuthScreen
|
||||||
import ru.n08i40k.polytechnic.next.utils.app
|
import ru.n08i40k.polytechnic.next.utils.app
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import ru.n08i40k.polytechnic.next.UpdateDates
|
import ru.n08i40k.polytechnic.next.CacheDate
|
||||||
import ru.n08i40k.polytechnic.next.app.AppContainer
|
import ru.n08i40k.polytechnic.next.app.AppContainer
|
||||||
import ru.n08i40k.polytechnic.next.model.GroupOrTeacher
|
import ru.n08i40k.polytechnic.next.model.GroupOrTeacher
|
||||||
import ru.n08i40k.polytechnic.next.utils.MyResult
|
import ru.n08i40k.polytechnic.next.utils.MyResult
|
||||||
@@ -25,7 +25,7 @@ sealed interface GroupUiState {
|
|||||||
|
|
||||||
data class HasData(
|
data class HasData(
|
||||||
val group: GroupOrTeacher,
|
val group: GroupOrTeacher,
|
||||||
val updateDates: UpdateDates,
|
val cacheDate: CacheDate,
|
||||||
val lastUpdateAt: Long,
|
val lastUpdateAt: Long,
|
||||||
override val isLoading: Boolean
|
override val isLoading: Boolean
|
||||||
) : GroupUiState
|
) : GroupUiState
|
||||||
@@ -33,7 +33,7 @@ sealed interface GroupUiState {
|
|||||||
|
|
||||||
private data class GroupViewModelState(
|
private data class GroupViewModelState(
|
||||||
val group: GroupOrTeacher? = null,
|
val group: GroupOrTeacher? = null,
|
||||||
val updateDates: UpdateDates? = null,
|
val updateDates: CacheDate? = null,
|
||||||
val lastUpdateAt: Long = 0,
|
val lastUpdateAt: Long = 0,
|
||||||
val isLoading: Boolean = false
|
val isLoading: Boolean = false
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import ru.n08i40k.polytechnic.next.UpdateDates
|
import ru.n08i40k.polytechnic.next.CacheDate
|
||||||
import ru.n08i40k.polytechnic.next.app.AppContainer
|
import ru.n08i40k.polytechnic.next.app.AppContainer
|
||||||
import ru.n08i40k.polytechnic.next.model.GroupOrTeacher
|
import ru.n08i40k.polytechnic.next.model.GroupOrTeacher
|
||||||
import ru.n08i40k.polytechnic.next.utils.MyResult
|
import ru.n08i40k.polytechnic.next.utils.MyResult
|
||||||
@@ -25,7 +25,7 @@ sealed interface SearchUiState {
|
|||||||
|
|
||||||
data class HasData(
|
data class HasData(
|
||||||
val teacher: GroupOrTeacher,
|
val teacher: GroupOrTeacher,
|
||||||
val updateDates: UpdateDates,
|
val cacheDate: CacheDate,
|
||||||
val lastUpdateAt: Long,
|
val lastUpdateAt: Long,
|
||||||
override val isLoading: Boolean
|
override val isLoading: Boolean
|
||||||
) : SearchUiState
|
) : SearchUiState
|
||||||
@@ -33,7 +33,7 @@ sealed interface SearchUiState {
|
|||||||
|
|
||||||
private data class SearchViewModelState(
|
private data class SearchViewModelState(
|
||||||
val teacher: GroupOrTeacher? = null,
|
val teacher: GroupOrTeacher? = null,
|
||||||
val updateDates: UpdateDates? = null,
|
val updateDates: CacheDate? = null,
|
||||||
val lastUpdateAt: Long = 0,
|
val lastUpdateAt: Long = 0,
|
||||||
val isLoading: Boolean = false
|
val isLoading: Boolean = false
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import ru.n08i40k.polytechnic.next.UpdateDates
|
import ru.n08i40k.polytechnic.next.CacheDate
|
||||||
import ru.n08i40k.polytechnic.next.app.AppContainer
|
import ru.n08i40k.polytechnic.next.app.AppContainer
|
||||||
import ru.n08i40k.polytechnic.next.model.GroupOrTeacher
|
import ru.n08i40k.polytechnic.next.model.GroupOrTeacher
|
||||||
import ru.n08i40k.polytechnic.next.utils.MyResult
|
import ru.n08i40k.polytechnic.next.utils.MyResult
|
||||||
@@ -24,7 +24,7 @@ sealed interface TeacherUiState {
|
|||||||
|
|
||||||
data class HasData(
|
data class HasData(
|
||||||
val teacher: GroupOrTeacher,
|
val teacher: GroupOrTeacher,
|
||||||
val updateDates: UpdateDates,
|
val cacheDate: CacheDate,
|
||||||
val lastUpdateAt: Long,
|
val lastUpdateAt: Long,
|
||||||
override val isLoading: Boolean
|
override val isLoading: Boolean
|
||||||
) : TeacherUiState
|
) : TeacherUiState
|
||||||
@@ -32,7 +32,7 @@ sealed interface TeacherUiState {
|
|||||||
|
|
||||||
private data class TeacherViewModelState(
|
private data class TeacherViewModelState(
|
||||||
val teacher: GroupOrTeacher? = null,
|
val teacher: GroupOrTeacher? = null,
|
||||||
val updateDates: UpdateDates? = null,
|
val updateDates: CacheDate? = null,
|
||||||
val lastUpdateAt: Long = 0,
|
val lastUpdateAt: Long = 0,
|
||||||
val isLoading: Boolean = false
|
val isLoading: Boolean = false
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import kotlinx.coroutines.runBlocking
|
|||||||
import ru.n08i40k.polytechnic.next.Application
|
import ru.n08i40k.polytechnic.next.Application
|
||||||
import ru.n08i40k.polytechnic.next.R
|
import ru.n08i40k.polytechnic.next.R
|
||||||
import ru.n08i40k.polytechnic.next.model.UserRole
|
import ru.n08i40k.polytechnic.next.model.UserRole
|
||||||
import ru.n08i40k.polytechnic.next.settings.settings
|
import ru.n08i40k.polytechnic.next.proto.settings
|
||||||
import ru.n08i40k.polytechnic.next.ui.AppRoute
|
import ru.n08i40k.polytechnic.next.ui.AppRoute
|
||||||
import ru.n08i40k.polytechnic.next.ui.icons.AppIcons
|
import ru.n08i40k.polytechnic.next.ui.icons.AppIcons
|
||||||
import ru.n08i40k.polytechnic.next.ui.icons.appicons.Filled
|
import ru.n08i40k.polytechnic.next.ui.icons.appicons.Filled
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ import androidx.navigation.compose.rememberNavController
|
|||||||
import com.google.android.gms.tasks.OnCompleteListener
|
import com.google.android.gms.tasks.OnCompleteListener
|
||||||
import com.google.android.gms.tasks.Task
|
import com.google.android.gms.tasks.Task
|
||||||
import com.google.firebase.messaging.FirebaseMessaging
|
import com.google.firebase.messaging.FirebaseMessaging
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import ru.n08i40k.polytechnic.next.proto.cache
|
||||||
import ru.n08i40k.polytechnic.next.ui.AppRoute
|
import ru.n08i40k.polytechnic.next.ui.AppRoute
|
||||||
import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar
|
import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar
|
||||||
import ru.n08i40k.polytechnic.next.ui.helper.SnackbarBox
|
import ru.n08i40k.polytechnic.next.ui.helper.SnackbarBox
|
||||||
@@ -61,8 +63,7 @@ private fun FormWrapper(
|
|||||||
with(localDensity) {
|
with(localDensity) {
|
||||||
onWidthChange(it.size.width.toDp())
|
onWidthChange(it.size.width.toDp())
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
/*.animateContentSize()*/,
|
|
||||||
content = content
|
content = content
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -84,6 +85,8 @@ private fun AuthForm(parentNavController: NavController, pushSnackbar: PushSnack
|
|||||||
}
|
}
|
||||||
|
|
||||||
val finish: () -> Unit = {
|
val finish: () -> Unit = {
|
||||||
|
runBlocking { context.cache.updateData { it.toBuilder().clear().build() } }
|
||||||
|
|
||||||
parentNavController.navigate(AppRoute.MAIN.route) {
|
parentNavController.navigate(AppRoute.MAIN.route) {
|
||||||
popUpTo(AppRoute.AUTH.route) { inclusive = true }
|
popUpTo(AppRoute.AUTH.route) { inclusive = true }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ import kotlinx.coroutines.runBlocking
|
|||||||
import ru.n08i40k.polytechnic.next.R
|
import ru.n08i40k.polytechnic.next.R
|
||||||
import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignIn
|
import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignIn
|
||||||
import ru.n08i40k.polytechnic.next.network.unwrapException
|
import ru.n08i40k.polytechnic.next.network.unwrapException
|
||||||
import ru.n08i40k.polytechnic.next.settings.settings
|
import ru.n08i40k.polytechnic.next.proto.settings
|
||||||
import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar
|
import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar
|
||||||
import ru.n08i40k.polytechnic.next.ui.helper.data.rememberInputValue
|
import ru.n08i40k.polytechnic.next.ui.helper.data.rememberInputValue
|
||||||
import java.util.logging.Logger
|
import java.util.logging.Logger
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignInVK
|
import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignInVK
|
||||||
import ru.n08i40k.polytechnic.next.network.unwrapException
|
import ru.n08i40k.polytechnic.next.network.unwrapException
|
||||||
import ru.n08i40k.polytechnic.next.settings.settings
|
import ru.n08i40k.polytechnic.next.proto.settings
|
||||||
import ru.n08i40k.polytechnic.next.ui.widgets.OneTapComplete
|
import ru.n08i40k.polytechnic.next.ui.widgets.OneTapComplete
|
||||||
import java.util.logging.Logger
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ import ru.n08i40k.polytechnic.next.R
|
|||||||
import ru.n08i40k.polytechnic.next.model.UserRole
|
import ru.n08i40k.polytechnic.next.model.UserRole
|
||||||
import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignUp
|
import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignUp
|
||||||
import ru.n08i40k.polytechnic.next.network.unwrapException
|
import ru.n08i40k.polytechnic.next.network.unwrapException
|
||||||
import ru.n08i40k.polytechnic.next.settings.settings
|
import ru.n08i40k.polytechnic.next.proto.settings
|
||||||
import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar
|
import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar
|
||||||
import ru.n08i40k.polytechnic.next.ui.helper.data.rememberInputValue
|
import ru.n08i40k.polytechnic.next.ui.helper.data.rememberInputValue
|
||||||
import ru.n08i40k.polytechnic.next.ui.widgets.selector.GroupSelector
|
import ru.n08i40k.polytechnic.next.ui.widgets.selector.GroupSelector
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ import ru.n08i40k.polytechnic.next.R
|
|||||||
import ru.n08i40k.polytechnic.next.model.UserRole
|
import ru.n08i40k.polytechnic.next.model.UserRole
|
||||||
import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignUpVK
|
import ru.n08i40k.polytechnic.next.network.request.auth.AuthSignUpVK
|
||||||
import ru.n08i40k.polytechnic.next.network.unwrapException
|
import ru.n08i40k.polytechnic.next.network.unwrapException
|
||||||
import ru.n08i40k.polytechnic.next.settings.settings
|
import ru.n08i40k.polytechnic.next.proto.settings
|
||||||
import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar
|
import ru.n08i40k.polytechnic.next.ui.helper.PushSnackbar
|
||||||
import ru.n08i40k.polytechnic.next.ui.helper.data.rememberInputValue
|
import ru.n08i40k.polytechnic.next.ui.helper.data.rememberInputValue
|
||||||
import ru.n08i40k.polytechnic.next.ui.widgets.selector.GroupSelector
|
import ru.n08i40k.polytechnic.next.ui.widgets.selector.GroupSelector
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ package ru.n08i40k.polytechnic.next.ui.screen.schedule
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -24,6 +23,7 @@ import ru.n08i40k.polytechnic.next.ui.model.GroupUiState
|
|||||||
import ru.n08i40k.polytechnic.next.ui.model.GroupViewModel
|
import ru.n08i40k.polytechnic.next.ui.model.GroupViewModel
|
||||||
import ru.n08i40k.polytechnic.next.ui.widgets.LoadingContent
|
import ru.n08i40k.polytechnic.next.ui.widgets.LoadingContent
|
||||||
import ru.n08i40k.polytechnic.next.ui.widgets.schedule.SchedulePager
|
import ru.n08i40k.polytechnic.next.ui.widgets.schedule.SchedulePager
|
||||||
|
import ru.n08i40k.polytechnic.next.ui.widgets.schedule.UpdateInfo
|
||||||
import ru.n08i40k.polytechnic.next.utils.rememberUpdatedLifecycleOwner
|
import ru.n08i40k.polytechnic.next.utils.rememberUpdatedLifecycleOwner
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -62,11 +62,10 @@ fun GroupScheduleScreen(viewModel: GroupViewModel) {
|
|||||||
) {
|
) {
|
||||||
when (uiState) {
|
when (uiState) {
|
||||||
is GroupUiState.HasData -> {
|
is GroupUiState.HasData -> {
|
||||||
Column {
|
Column(Modifier.padding(20.dp), Arrangement.spacedBy(10.dp)) {
|
||||||
val data = uiState as GroupUiState.HasData
|
val data = uiState as GroupUiState.HasData
|
||||||
|
|
||||||
UpdateInfo(data.lastUpdateAt, data.updateDates)
|
UpdateInfo(data.lastUpdateAt, data.cacheDate)
|
||||||
Spacer(Modifier.height(10.dp))
|
|
||||||
SchedulePager(data.group)
|
SchedulePager(data.group)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ package ru.n08i40k.polytechnic.next.ui.screen.schedule
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -24,6 +23,7 @@ import ru.n08i40k.polytechnic.next.ui.model.TeacherUiState
|
|||||||
import ru.n08i40k.polytechnic.next.ui.model.TeacherViewModel
|
import ru.n08i40k.polytechnic.next.ui.model.TeacherViewModel
|
||||||
import ru.n08i40k.polytechnic.next.ui.widgets.LoadingContent
|
import ru.n08i40k.polytechnic.next.ui.widgets.LoadingContent
|
||||||
import ru.n08i40k.polytechnic.next.ui.widgets.schedule.SchedulePager
|
import ru.n08i40k.polytechnic.next.ui.widgets.schedule.SchedulePager
|
||||||
|
import ru.n08i40k.polytechnic.next.ui.widgets.schedule.UpdateInfo
|
||||||
import ru.n08i40k.polytechnic.next.utils.rememberUpdatedLifecycleOwner
|
import ru.n08i40k.polytechnic.next.utils.rememberUpdatedLifecycleOwner
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -62,11 +62,10 @@ fun TeacherScheduleScreen(viewModel: TeacherViewModel) {
|
|||||||
) {
|
) {
|
||||||
when (uiState) {
|
when (uiState) {
|
||||||
is TeacherUiState.HasData -> {
|
is TeacherUiState.HasData -> {
|
||||||
Column {
|
Column(Modifier.padding(20.dp), Arrangement.spacedBy(10.dp)) {
|
||||||
val data = uiState as TeacherUiState.HasData
|
val data = uiState as TeacherUiState.HasData
|
||||||
|
|
||||||
UpdateInfo(data.lastUpdateAt, data.updateDates)
|
UpdateInfo(data.lastUpdateAt, data.cacheDate)
|
||||||
Spacer(Modifier.height(10.dp))
|
|
||||||
SchedulePager(data.teacher)
|
SchedulePager(data.teacher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -28,6 +29,7 @@ import ru.n08i40k.polytechnic.next.ui.model.SearchUiState
|
|||||||
import ru.n08i40k.polytechnic.next.ui.model.SearchViewModel
|
import ru.n08i40k.polytechnic.next.ui.model.SearchViewModel
|
||||||
import ru.n08i40k.polytechnic.next.ui.widgets.LoadingContent
|
import ru.n08i40k.polytechnic.next.ui.widgets.LoadingContent
|
||||||
import ru.n08i40k.polytechnic.next.ui.widgets.schedule.SchedulePager
|
import ru.n08i40k.polytechnic.next.ui.widgets.schedule.SchedulePager
|
||||||
|
import ru.n08i40k.polytechnic.next.ui.widgets.schedule.UpdateInfo
|
||||||
import ru.n08i40k.polytechnic.next.ui.widgets.selector.TeacherNameSelector
|
import ru.n08i40k.polytechnic.next.ui.widgets.selector.TeacherNameSelector
|
||||||
import ru.n08i40k.polytechnic.next.utils.rememberUpdatedLifecycleOwner
|
import ru.n08i40k.polytechnic.next.utils.rememberUpdatedLifecycleOwner
|
||||||
|
|
||||||
@@ -82,11 +84,10 @@ fun TeacherSearchScreen(viewModel: SearchViewModel) {
|
|||||||
|
|
||||||
when (uiState) {
|
when (uiState) {
|
||||||
is SearchUiState.HasData -> {
|
is SearchUiState.HasData -> {
|
||||||
Column {
|
Column(Modifier.padding(20.dp), Arrangement.spacedBy(10.dp)) {
|
||||||
val data = uiState as SearchUiState.HasData
|
val data = uiState as SearchUiState.HasData
|
||||||
|
|
||||||
UpdateInfo(data.lastUpdateAt, data.updateDates)
|
UpdateInfo(data.lastUpdateAt, data.cacheDate)
|
||||||
Spacer(Modifier.height(10.dp))
|
|
||||||
SchedulePager(data.teacher)
|
SchedulePager(data.teacher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import androidx.compose.animation.expandVertically
|
|||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.shrinkVertically
|
import androidx.compose.animation.shrinkVertically
|
||||||
import androidx.compose.foundation.BorderStroke
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -32,17 +31,12 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ExpandableCard(
|
fun ExpandableCard(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
colors: CardColors = CardDefaults.cardColors(),
|
colors: CardColors = CardDefaults.cardColors(),
|
||||||
border: BorderStroke = BorderStroke(
|
|
||||||
Dp.Hairline,
|
|
||||||
MaterialTheme.colorScheme.inverseSurface
|
|
||||||
),
|
|
||||||
expanded: Boolean = false,
|
expanded: Boolean = false,
|
||||||
onExpandedChange: () -> Unit,
|
onExpandedChange: () -> Unit,
|
||||||
title: @Composable () -> Unit,
|
title: @Composable () -> Unit,
|
||||||
@@ -61,8 +55,7 @@ fun ExpandableCard(
|
|||||||
onExpandedChange()
|
onExpandedChange()
|
||||||
transitionState.targetState = expanded
|
transitionState.targetState = expanded
|
||||||
},
|
},
|
||||||
colors = colors,
|
colors = colors
|
||||||
border = border
|
|
||||||
) {
|
) {
|
||||||
Column {
|
Column {
|
||||||
ExpandableCardHeader(title, transition)
|
ExpandableCardHeader(title, transition)
|
||||||
@@ -76,15 +69,10 @@ fun ExpandableCard(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
title: @Composable () -> Unit,
|
title: @Composable () -> Unit,
|
||||||
colors: CardColors = CardDefaults.cardColors(),
|
colors: CardColors = CardDefaults.cardColors(),
|
||||||
border: BorderStroke = BorderStroke(
|
|
||||||
Dp.Hairline,
|
|
||||||
MaterialTheme.colorScheme.inverseSurface
|
|
||||||
),
|
|
||||||
) {
|
) {
|
||||||
Card(
|
Card(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
colors = colors,
|
colors = colors
|
||||||
border = border
|
|
||||||
) {
|
) {
|
||||||
ExpandableCardHeader(title, null)
|
ExpandableCardHeader(title, null)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
package ru.n08i40k.polytechnic.next.ui.widgets.schedule
|
package ru.n08i40k.polytechnic.next.ui.widgets.schedule
|
||||||
|
|
||||||
import androidx.compose.foundation.BorderStroke
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.border
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.only
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.safeContent
|
||||||
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.HorizontalDivider
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -34,8 +34,9 @@ import kotlinx.coroutines.flow.flow
|
|||||||
import kotlinx.datetime.LocalDateTime
|
import kotlinx.datetime.LocalDateTime
|
||||||
import ru.n08i40k.polytechnic.next.R
|
import ru.n08i40k.polytechnic.next.R
|
||||||
import ru.n08i40k.polytechnic.next.model.Day
|
import ru.n08i40k.polytechnic.next.model.Day
|
||||||
import ru.n08i40k.polytechnic.next.model.LessonType
|
import ru.n08i40k.polytechnic.next.model.Lesson
|
||||||
import ru.n08i40k.polytechnic.next.repository.schedule.impl.MockScheduleRepository
|
import ru.n08i40k.polytechnic.next.repository.schedule.impl.MockScheduleRepository
|
||||||
|
import ru.n08i40k.polytechnic.next.ui.theme.AppTheme
|
||||||
import ru.n08i40k.polytechnic.next.utils.dateTime
|
import ru.n08i40k.polytechnic.next.utils.dateTime
|
||||||
import ru.n08i40k.polytechnic.next.utils.now
|
import ru.n08i40k.polytechnic.next.utils.now
|
||||||
|
|
||||||
@@ -76,81 +77,42 @@ private fun getCurrentLessonIdx(day: Day?): Flow<Int> {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@PreviewLightDark
|
||||||
|
@Composable
|
||||||
|
private fun DayCardPreview() {
|
||||||
|
AppTheme {
|
||||||
|
Surface(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.windowInsetsPadding(WindowInsets.safeContent.only(WindowInsetsSides.Top))
|
||||||
|
) {
|
||||||
|
DayCard(Modifier, MockScheduleRepository.exampleTeacher.days[0]) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DayCard(
|
fun DayCard(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
day: Day = MockScheduleRepository.exampleTeacher.days[0]
|
day: Day,
|
||||||
|
onLessonClick: (Lesson) -> Unit,
|
||||||
) {
|
) {
|
||||||
val offset = remember(day) { getDayOffset(day) }
|
val offset = remember(day) { getDayOffset(day) }
|
||||||
|
|
||||||
val defaultCardColors = CardDefaults.cardColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.secondaryContainer,
|
|
||||||
contentColor = MaterialTheme.colorScheme.onSecondaryContainer,
|
|
||||||
)
|
|
||||||
val customCardColors = CardDefaults.cardColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
|
|
||||||
contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
|
|
||||||
)
|
|
||||||
val noneCardColors = CardDefaults.cardColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
|
||||||
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
|
|
||||||
)
|
|
||||||
val examCardColors = CardDefaults.cardColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.errorContainer,
|
|
||||||
contentColor = MaterialTheme.colorScheme.onErrorContainer,
|
|
||||||
)
|
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
modifier,
|
modifier,
|
||||||
colors = CardDefaults.cardColors(
|
colors = CardDefaults.cardColors(
|
||||||
containerColor = when (offset) {
|
containerColor = MaterialTheme.colorScheme.surfaceContainerLowest
|
||||||
DayOffset.TODAY -> MaterialTheme.colorScheme.primaryContainer
|
|
||||||
else -> MaterialTheme.colorScheme.secondaryContainer
|
|
||||||
}
|
|
||||||
),
|
|
||||||
border = BorderStroke(1.dp, MaterialTheme.colorScheme.inverseSurface)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
day.name,
|
|
||||||
Modifier.fillMaxWidth(),
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
style = MaterialTheme.typography.titleLarge
|
|
||||||
)
|
)
|
||||||
|
) {
|
||||||
if (day.street != null) {
|
|
||||||
Text(
|
|
||||||
day.street,
|
|
||||||
Modifier.fillMaxWidth(),
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset != DayOffset.OTHER) {
|
|
||||||
Text(
|
|
||||||
stringResource(
|
|
||||||
when (offset) {
|
|
||||||
DayOffset.YESTERDAY -> R.string.yesterday
|
|
||||||
DayOffset.TODAY -> R.string.today
|
|
||||||
DayOffset.TOMORROW -> R.string.tomorrow
|
|
||||||
DayOffset.OTHER -> throw RuntimeException()
|
|
||||||
}
|
|
||||||
),
|
|
||||||
Modifier.fillMaxWidth(),
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val currentLessonIndex by getCurrentLessonIdx(if (offset == DayOffset.TODAY) day else null)
|
val currentLessonIndex by getCurrentLessonIdx(if (offset == DayOffset.TODAY) day else null)
|
||||||
.collectAsStateWithLifecycle(0)
|
.collectAsStateWithLifecycle(0)
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.verticalScroll(rememberScrollState()),
|
.verticalScroll(rememberScrollState())
|
||||||
|
.padding(10.dp),
|
||||||
Arrangement.spacedBy(0.5.dp)
|
Arrangement.spacedBy(0.5.dp)
|
||||||
) {
|
) {
|
||||||
if (day.lessons.isEmpty()) {
|
if (day.lessons.isEmpty()) {
|
||||||
@@ -159,39 +121,10 @@ fun DayCard(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (lessonIndex in day.lessons.indices) {
|
for (lessonIndex in day.lessons.indices) {
|
||||||
HorizontalDivider(
|
|
||||||
thickness = 1.dp,
|
|
||||||
color = MaterialTheme.colorScheme.inversePrimary
|
|
||||||
)
|
|
||||||
|
|
||||||
val lesson = day.lessons[lessonIndex]
|
val lesson = day.lessons[lessonIndex]
|
||||||
|
|
||||||
val cardColors = when (lesson.type) {
|
Box(Modifier.clickable { onLessonClick(lesson) }) {
|
||||||
LessonType.DEFAULT -> defaultCardColors
|
LessonRow(modifier, lesson, lessonIndex == currentLessonIndex)
|
||||||
LessonType.ADDITIONAL -> noneCardColors
|
|
||||||
LessonType.BREAK -> noneCardColors
|
|
||||||
LessonType.CONSULTATION -> customCardColors
|
|
||||||
LessonType.INDEPENDENT_WORK -> customCardColors
|
|
||||||
LessonType.EXAM -> examCardColors
|
|
||||||
LessonType.EXAM_WITH_GRADE -> examCardColors
|
|
||||||
LessonType.EXAM_DEFAULT -> examCardColors
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Вернуть ExtraInfo
|
|
||||||
var extraInfo by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
Box(
|
|
||||||
Modifier
|
|
||||||
.clickable { extraInfo = true }
|
|
||||||
.background(cardColors.containerColor)
|
|
||||||
) {
|
|
||||||
val modifier =
|
|
||||||
if (lessonIndex == currentLessonIndex)
|
|
||||||
Modifier.border(BorderStroke(1.dp, MaterialTheme.colorScheme.error))
|
|
||||||
else
|
|
||||||
Modifier
|
|
||||||
|
|
||||||
LessonRow(modifier, lesson, cardColors)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
package ru.n08i40k.polytechnic.next.ui.widgets.schedule
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
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.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.layout.onGloballyPositioned
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.max
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import ru.n08i40k.polytechnic.next.R
|
||||||
|
import ru.n08i40k.polytechnic.next.model.Lesson
|
||||||
|
import ru.n08i40k.polytechnic.next.model.LessonType
|
||||||
|
import ru.n08i40k.polytechnic.next.repository.schedule.impl.MockScheduleRepository
|
||||||
|
import ru.n08i40k.polytechnic.next.utils.dayMinutes
|
||||||
|
import ru.n08i40k.polytechnic.next.utils.fmtAsClock
|
||||||
|
|
||||||
|
class LessonPreviewParameterProvider : PreviewParameterProvider<Lesson> {
|
||||||
|
override val values: Sequence<Lesson>
|
||||||
|
get() {
|
||||||
|
val lessons = MockScheduleRepository.exampleGroup.days[0].lessons
|
||||||
|
|
||||||
|
return sequenceOf(
|
||||||
|
lessons[0],
|
||||||
|
lessons[2],
|
||||||
|
lessons[4],
|
||||||
|
lessons[6],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun ExtraInfoDialogPreview(
|
||||||
|
@PreviewParameter(LessonPreviewParameterProvider::class) lesson: Lesson
|
||||||
|
) {
|
||||||
|
ExtraInfoDialog(lesson) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ExtraInfoDialog(
|
||||||
|
lesson: Lesson,
|
||||||
|
onDismiss: () -> Unit
|
||||||
|
) {
|
||||||
|
Dialog(onDismiss) {
|
||||||
|
Card {
|
||||||
|
Column(Modifier.padding(10.dp)) {
|
||||||
|
var minWidth by remember { mutableStateOf(Dp.Unspecified) }
|
||||||
|
val density = LocalDensity.current
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun kvText(title: String, text: String) {
|
||||||
|
Row(
|
||||||
|
Modifier.alpha(if (minWidth == Dp.Unspecified) 0f else 1f),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
Modifier
|
||||||
|
.onGloballyPositioned {
|
||||||
|
with(density) {
|
||||||
|
val dp = it.size.width.toDp()
|
||||||
|
|
||||||
|
minWidth =
|
||||||
|
if (minWidth == Dp.Unspecified)
|
||||||
|
dp
|
||||||
|
else
|
||||||
|
max(minWidth, dp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.size(minWidth, Dp.Unspecified),
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontFamily = FontFamily.Monospace,
|
||||||
|
textAlign = TextAlign.Right,
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kvText(stringResource(R.string.extra_info_lesson_name), lesson.name ?: "")
|
||||||
|
when (lesson.type) {
|
||||||
|
LessonType.BREAK -> throw IllegalArgumentException()
|
||||||
|
LessonType.DEFAULT -> null
|
||||||
|
LessonType.ADDITIONAL -> null
|
||||||
|
LessonType.CONSULTATION -> R.string.lesson_type_consultation
|
||||||
|
LessonType.INDEPENDENT_WORK -> R.string.lesson_type_independent_work
|
||||||
|
LessonType.EXAM -> R.string.lesson_type_exam
|
||||||
|
LessonType.EXAM_WITH_GRADE -> R.string.lesson_type_exam_with_grade
|
||||||
|
LessonType.EXAM_DEFAULT -> R.string.lesson_type_exam_default
|
||||||
|
}?.let {
|
||||||
|
kvText(stringResource(R.string.extra_info_type), stringResource(it))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lesson.subGroups.size == 1) {
|
||||||
|
kvText(
|
||||||
|
stringResource(R.string.extra_info_teacher),
|
||||||
|
stringResource(
|
||||||
|
R.string.extra_info_teacher_second,
|
||||||
|
lesson.subGroups[0].teacher,
|
||||||
|
lesson.subGroups[0].cabinet
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
for (subGroup in lesson.subGroups) {
|
||||||
|
kvText(
|
||||||
|
stringResource(R.string.extra_info_teacher),
|
||||||
|
stringResource(
|
||||||
|
R.string.extra_info_teacher_second_subgroup,
|
||||||
|
subGroup.teacher,
|
||||||
|
subGroup.cabinet,
|
||||||
|
subGroup.number
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kvText(
|
||||||
|
stringResource(R.string.extra_info_duration),
|
||||||
|
stringResource(
|
||||||
|
R.string.extra_info_duration_second,
|
||||||
|
lesson.time.start.dayMinutes.fmtAsClock(),
|
||||||
|
lesson.time.end.dayMinutes.fmtAsClock(),
|
||||||
|
lesson.duration / 60,
|
||||||
|
lesson.duration % 60
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,34 +1,47 @@
|
|||||||
package ru.n08i40k.polytechnic.next.ui.widgets.schedule
|
package ru.n08i40k.polytechnic.next.ui.widgets.schedule
|
||||||
|
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.only
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.safeContent
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.layout.wrapContentWidth
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
import androidx.compose.foundation.text.BasicText
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.CardColors
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.CardDefaults
|
|
||||||
import androidx.compose.material3.LocalTextStyle
|
import androidx.compose.material3.LocalTextStyle
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.VerticalDivider
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.rememberTextMeasurer
|
import androidx.compose.ui.text.rememberTextMeasurer
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import ru.n08i40k.polytechnic.next.R
|
import ru.n08i40k.polytechnic.next.R
|
||||||
import ru.n08i40k.polytechnic.next.model.Lesson
|
import ru.n08i40k.polytechnic.next.model.Lesson
|
||||||
import ru.n08i40k.polytechnic.next.model.LessonType
|
import ru.n08i40k.polytechnic.next.model.LessonType
|
||||||
import ru.n08i40k.polytechnic.next.repository.schedule.impl.MockScheduleRepository
|
import ru.n08i40k.polytechnic.next.repository.schedule.impl.MockScheduleRepository
|
||||||
|
import ru.n08i40k.polytechnic.next.ui.theme.AppTheme
|
||||||
import ru.n08i40k.polytechnic.next.utils.dayMinutes
|
import ru.n08i40k.polytechnic.next.utils.dayMinutes
|
||||||
import ru.n08i40k.polytechnic.next.utils.fmtAsClock
|
import ru.n08i40k.polytechnic.next.utils.fmtAsClock
|
||||||
|
|
||||||
@@ -42,31 +55,51 @@ private fun fmtTime(start: Int, end: Int, format: TimeFormat): ArrayList<String>
|
|||||||
return when (format) {
|
return when (format) {
|
||||||
TimeFormat.CLOCK -> arrayListOf(start.fmtAsClock(), end.fmtAsClock())
|
TimeFormat.CLOCK -> arrayListOf(start.fmtAsClock(), end.fmtAsClock())
|
||||||
TimeFormat.DURATION -> arrayListOf(
|
TimeFormat.DURATION -> arrayListOf(
|
||||||
"${end - start} ${stringResource(R.string.minutes)}"
|
"${end - start} ${stringResource(R.string.minutes_full)}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true, showSystemUi = true)
|
@PreviewLightDark
|
||||||
|
@Composable
|
||||||
|
private fun LessonRowPreview() {
|
||||||
|
AppTheme {
|
||||||
|
Surface(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.windowInsetsPadding(WindowInsets.safeContent.only(WindowInsetsSides.Top))
|
||||||
|
) {
|
||||||
|
LessonRow(Modifier, MockScheduleRepository.exampleGroup.days[0].lessons[6], true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LessonRow(
|
fun LessonRow(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
lesson: Lesson = MockScheduleRepository.exampleGroup.days[0].lessons[0],
|
lesson: Lesson,
|
||||||
colors: CardColors = CardDefaults.cardColors()
|
current: Boolean
|
||||||
) {
|
) {
|
||||||
val verticalPadding = when (lesson.type) {
|
var time = fmtTime(
|
||||||
LessonType.BREAK -> 2.5.dp
|
lesson.time.start.dayMinutes,
|
||||||
else -> 5.dp
|
lesson.time.end.dayMinutes,
|
||||||
}
|
if (lesson.type == LessonType.BREAK) TimeFormat.DURATION else TimeFormat.CLOCK
|
||||||
|
)
|
||||||
|
|
||||||
val timeFormat = when (lesson.type) {
|
if (lesson.type == LessonType.BREAK) {
|
||||||
LessonType.BREAK -> TimeFormat.DURATION
|
Box(Modifier.fillMaxWidth(), Alignment.Center) {
|
||||||
else -> TimeFormat.CLOCK
|
HorizontalDivider(color = MaterialTheme.colorScheme.surfaceContainerHighest)
|
||||||
}
|
Text(
|
||||||
|
time[0],
|
||||||
|
Modifier
|
||||||
|
.background(MaterialTheme.colorScheme.surfaceContainerLowest)
|
||||||
|
.padding(5.dp, 0.dp),
|
||||||
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
color = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
val contentColor = when (lesson.type) {
|
return
|
||||||
LessonType.BREAK -> colors.disabledContentColor
|
|
||||||
else -> colors.contentColor
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// магические вычисления))
|
// магические вычисления))
|
||||||
@@ -78,120 +111,184 @@ fun LessonRow(
|
|||||||
|
|
||||||
Box(modifier) {
|
Box(modifier) {
|
||||||
Row(
|
Row(
|
||||||
Modifier.padding(10.dp, verticalPadding * rangeSize),
|
Modifier.padding(10.dp, 5.dp * rangeSize),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
Arrangement.spacedBy(15.dp),
|
||||||
|
Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
Text(
|
|
||||||
when (range) {
|
|
||||||
null -> " "
|
|
||||||
else -> {
|
|
||||||
if (range[0] == range[1])
|
|
||||||
" ${range[0]} "
|
|
||||||
else
|
|
||||||
"${range[0]}-${range[1]}"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
fontFamily = FontFamily.Monospace,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
color = contentColor
|
|
||||||
)
|
|
||||||
|
|
||||||
Spacer(Modifier.width(5.dp))
|
|
||||||
|
|
||||||
val textMeasurer = rememberTextMeasurer()
|
val textMeasurer = rememberTextMeasurer()
|
||||||
val timeWidth = textMeasurer.measure(
|
val timeWidth = textMeasurer.measure(
|
||||||
text = "00:00 ",
|
text = "00:00",
|
||||||
style = LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace)
|
style = LocalTextStyle.current.copy(fontFamily = FontFamily.Monospace)
|
||||||
)
|
)
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
Modifier.width(with(LocalDensity.current) { timeWidth.size.width.toDp() }),
|
Modifier
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
.width(with(LocalDensity.current) { timeWidth.size.width.toDp() + 1.dp }),
|
||||||
|
Arrangement.spacedBy(5.dp),
|
||||||
|
Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
var time = fmtTime(
|
Column {
|
||||||
lesson.time.start.dayMinutes,
|
Text(
|
||||||
lesson.time.end.dayMinutes,
|
time[0],
|
||||||
timeFormat
|
fontFamily = FontFamily.Monospace,
|
||||||
)
|
fontWeight = FontWeight.W600,
|
||||||
|
style = MaterialTheme.typography.titleSmall
|
||||||
Text(time[0], color = contentColor, fontFamily = FontFamily.Monospace, maxLines = 1)
|
)
|
||||||
if (lesson.type != LessonType.BREAK)
|
|
||||||
Text(
|
Text(
|
||||||
time[1],
|
time[1],
|
||||||
color = contentColor,
|
|
||||||
fontFamily = FontFamily.Monospace,
|
fontFamily = FontFamily.Monospace,
|
||||||
maxLines = 1
|
fontWeight = FontWeight.W600,
|
||||||
|
style = MaterialTheme.typography.titleSmall
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(Modifier.width(5.dp))
|
if (range != null) {
|
||||||
|
HorizontalDivider(
|
||||||
Row(Modifier.fillMaxWidth(), Arrangement.SpaceBetween, Alignment.CenterVertically) {
|
Modifier.width(32.dp),
|
||||||
Column(Modifier.weight(1f)) {
|
1.dp,
|
||||||
// FIXME: Очень странный метод отсеивания, может что-нибудь на замену сделать?
|
MaterialTheme.colorScheme.inverseSurface
|
||||||
if (lesson.type.value > LessonType.BREAK.value) {
|
)
|
||||||
Text(
|
|
||||||
when (lesson.type) {
|
|
||||||
LessonType.CONSULTATION -> stringResource(R.string.lesson_type_consultation)
|
|
||||||
LessonType.INDEPENDENT_WORK -> stringResource(R.string.lesson_type_independent_work)
|
|
||||||
LessonType.EXAM -> stringResource(R.string.lesson_type_exam)
|
|
||||||
LessonType.EXAM_WITH_GRADE -> stringResource(R.string.lesson_type_exam_with_grade)
|
|
||||||
LessonType.EXAM_DEFAULT -> stringResource(R.string.lesson_type_exam_default)
|
|
||||||
else -> throw RuntimeException("Unknown lesson type!")
|
|
||||||
},
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis,
|
|
||||||
color = contentColor
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
lesson.name ?: stringResource(R.string.lesson_type_break),
|
if (range[0] == range[1])
|
||||||
fontWeight = FontWeight.Medium,
|
" ${range[0]} "
|
||||||
maxLines = 1,
|
else
|
||||||
overflow = TextOverflow.Ellipsis,
|
"${range[0]}-${range[1]}",
|
||||||
color = contentColor
|
fontFamily = FontFamily.Monospace,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
style = MaterialTheme.typography.bodySmall
|
||||||
)
|
)
|
||||||
|
|
||||||
if (lesson.group != null) {
|
|
||||||
Text(
|
|
||||||
lesson.group,
|
|
||||||
color = contentColor,
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (subGroup in lesson.subGroups) {
|
|
||||||
Text(
|
|
||||||
subGroup.teacher,
|
|
||||||
color = contentColor,
|
|
||||||
maxLines = 1,
|
|
||||||
overflow = TextOverflow.Ellipsis
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(Modifier.wrapContentWidth()) {
|
|
||||||
if (lesson.subGroups.size != 1) {
|
|
||||||
BasicText("")
|
|
||||||
|
|
||||||
if (lesson.group != null)
|
|
||||||
BasicText("")
|
|
||||||
}
|
|
||||||
|
|
||||||
for (subGroup in lesson.subGroups) {
|
|
||||||
Text(
|
|
||||||
subGroup.cabinet,
|
|
||||||
color = contentColor,
|
|
||||||
maxLines = 1,
|
|
||||||
fontFamily = FontFamily.Monospace
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VerticalDivider(
|
||||||
|
Modifier.height(42.dp),
|
||||||
|
1.dp,
|
||||||
|
if (current)
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
else
|
||||||
|
MaterialTheme.colorScheme.inverseSurface
|
||||||
|
)
|
||||||
|
|
||||||
|
Column(Modifier.weight(1f)) {
|
||||||
|
Text(
|
||||||
|
lesson.name!!,
|
||||||
|
fontWeight = FontWeight.W600,
|
||||||
|
style = MaterialTheme.typography.titleMedium
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME: Очень странный метод отсеивания, может что-нибудь на замену сделать?
|
||||||
|
if (lesson.type.value > LessonType.BREAK.value) {
|
||||||
|
Text(
|
||||||
|
when (lesson.type) {
|
||||||
|
LessonType.CONSULTATION -> stringResource(R.string.lesson_type_consultation)
|
||||||
|
LessonType.INDEPENDENT_WORK -> stringResource(R.string.lesson_type_independent_work)
|
||||||
|
LessonType.EXAM -> stringResource(R.string.lesson_type_exam)
|
||||||
|
LessonType.EXAM_WITH_GRADE -> stringResource(R.string.lesson_type_exam_with_grade)
|
||||||
|
LessonType.EXAM_DEFAULT -> stringResource(R.string.lesson_type_exam_default)
|
||||||
|
else -> throw RuntimeException("Unknown lesson type!")
|
||||||
|
},
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
fontWeight = FontWeight.W600,
|
||||||
|
style = MaterialTheme.typography.titleMedium
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SmallTextWithIcon(
|
||||||
|
@DrawableRes iconId: Int,
|
||||||
|
contentDescription: String,
|
||||||
|
text: String
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(10.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painterResource(iconId),
|
||||||
|
contentDescription,
|
||||||
|
Modifier.size(12.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text,
|
||||||
|
fontWeight = FontWeight.W400,
|
||||||
|
style = MaterialTheme.typography.titleSmall
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lesson.group != null) {
|
||||||
|
Row {
|
||||||
|
Spacer(Modifier.size(5.dp))
|
||||||
|
|
||||||
|
SmallTextWithIcon(
|
||||||
|
R.drawable.ic_group,
|
||||||
|
"Group",
|
||||||
|
lesson.group
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(5.dp),
|
||||||
|
Arrangement.SpaceBetween,
|
||||||
|
Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
lesson
|
||||||
|
.subGroups
|
||||||
|
.sortedBy { it.number }
|
||||||
|
.forEachIndexed { subGroupIdx, subGroup ->
|
||||||
|
if (subGroupIdx > 0) {
|
||||||
|
VerticalDivider(
|
||||||
|
Modifier.height(25.dp), 1.dp,
|
||||||
|
MaterialTheme.colorScheme.inverseSurface
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: тупая проверка
|
||||||
|
if (subGroup.teacher == "Только у другой") {
|
||||||
|
Text(
|
||||||
|
stringResource(
|
||||||
|
if (subGroup.number == 1)
|
||||||
|
R.string.only_for_second
|
||||||
|
else
|
||||||
|
R.string.only_for_first
|
||||||
|
),
|
||||||
|
fontWeight = FontWeight.W400,
|
||||||
|
style = MaterialTheme.typography.titleSmall
|
||||||
|
)
|
||||||
|
|
||||||
|
return@forEachIndexed
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
val cabinet =
|
||||||
|
if (subGroup.cabinet.toIntOrNull() == null)
|
||||||
|
subGroup.cabinet
|
||||||
|
else
|
||||||
|
"№${subGroup.cabinet}"
|
||||||
|
|
||||||
|
SmallTextWithIcon(
|
||||||
|
R.drawable.ic_cabinet,
|
||||||
|
"Cabinet",
|
||||||
|
cabinet
|
||||||
|
)
|
||||||
|
|
||||||
|
SmallTextWithIcon(
|
||||||
|
R.drawable.ic_teacher,
|
||||||
|
"Teacher",
|
||||||
|
subGroup.teacher
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,26 +1,57 @@
|
|||||||
package ru.n08i40k.polytechnic.next.ui.widgets.schedule
|
package ru.n08i40k.polytechnic.next.ui.widgets.schedule
|
||||||
|
|
||||||
|
import androidx.compose.animation.Animatable
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.only
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.safeContent
|
||||||
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
import androidx.compose.foundation.pager.HorizontalPager
|
import androidx.compose.foundation.pager.HorizontalPager
|
||||||
import androidx.compose.foundation.pager.rememberPagerState
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.util.lerp
|
import androidx.compose.ui.util.lerp
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.datetime.LocalDateTime
|
import kotlinx.datetime.LocalDateTime
|
||||||
import ru.n08i40k.polytechnic.next.R
|
import ru.n08i40k.polytechnic.next.R
|
||||||
import ru.n08i40k.polytechnic.next.model.GroupOrTeacher
|
import ru.n08i40k.polytechnic.next.model.GroupOrTeacher
|
||||||
|
import ru.n08i40k.polytechnic.next.model.Lesson
|
||||||
|
import ru.n08i40k.polytechnic.next.model.LessonType
|
||||||
import ru.n08i40k.polytechnic.next.repository.schedule.impl.MockScheduleRepository
|
import ru.n08i40k.polytechnic.next.repository.schedule.impl.MockScheduleRepository
|
||||||
|
import ru.n08i40k.polytechnic.next.ui.theme.AppTheme
|
||||||
import ru.n08i40k.polytechnic.next.ui.widgets.NotificationCard
|
import ru.n08i40k.polytechnic.next.ui.widgets.NotificationCard
|
||||||
import ru.n08i40k.polytechnic.next.utils.dateTime
|
import ru.n08i40k.polytechnic.next.utils.dateTime
|
||||||
import ru.n08i40k.polytechnic.next.utils.now
|
import ru.n08i40k.polytechnic.next.utils.now
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
@@ -32,24 +63,120 @@ private fun isScheduleOutdated(schedule: GroupOrTeacher): Boolean {
|
|||||||
return nowDateTime > lastLesson.time.end.dateTime
|
return nowDateTime > lastLesson.time.end.dateTime
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview(showSystemUi = true)
|
@PreviewLightDark()
|
||||||
@Composable
|
@Composable
|
||||||
fun SchedulePager(schedule: GroupOrTeacher = MockScheduleRepository.exampleTeacher) {
|
private fun SchedulePagerPreview() {
|
||||||
|
AppTheme {
|
||||||
|
Surface(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.windowInsetsPadding(WindowInsets.safeContent.only(WindowInsetsSides.Top))
|
||||||
|
) {
|
||||||
|
SchedulePager(MockScheduleRepository.exampleTeacher)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val weekList = listOf(
|
||||||
|
R.string.week_bar_monday,
|
||||||
|
R.string.week_bar_tuesday,
|
||||||
|
R.string.week_bar_wednesday,
|
||||||
|
R.string.week_bar_thursday,
|
||||||
|
R.string.week_bar_friday,
|
||||||
|
R.string.week_bar_saturday,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SchedulePager(schedule: GroupOrTeacher) {
|
||||||
val pagerState = rememberPagerState(
|
val pagerState = rememberPagerState(
|
||||||
initialPage = (schedule.currentIdx ?: (schedule.days.size - 1)).coerceAtLeast(0),
|
initialPage = (schedule.currentIdx ?: (schedule.days.size - 1)).coerceAtLeast(0),
|
||||||
pageCount = { schedule.days.size }
|
pageCount = { schedule.days.size }
|
||||||
)
|
)
|
||||||
|
|
||||||
Column {
|
var dialogLesson by remember { mutableStateOf<WeakReference<Lesson>?>(null) }
|
||||||
|
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
Arrangement.spacedBy(20.dp)
|
||||||
|
) {
|
||||||
if (isScheduleOutdated(schedule))
|
if (isScheduleOutdated(schedule))
|
||||||
NotificationCard(Level.WARNING, stringResource(R.string.outdated_schedule))
|
NotificationCard(Level.WARNING, stringResource(R.string.outdated_schedule))
|
||||||
|
|
||||||
|
Card(
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surfaceContainerLowest
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Row(Modifier.fillMaxWidth(), Arrangement.spacedBy(5.dp)) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
for (i in 0..5) {
|
||||||
|
val primaryContainerColor = MaterialTheme.colorScheme.primaryContainer
|
||||||
|
|
||||||
|
val onPrimaryContainerColor = MaterialTheme.colorScheme.onPrimaryContainer
|
||||||
|
val onSurfaceColor = MaterialTheme.colorScheme.onSurface
|
||||||
|
|
||||||
|
val containerColor = remember {
|
||||||
|
Animatable(
|
||||||
|
if (pagerState.currentPage == i) primaryContainerColor
|
||||||
|
else Color.Transparent
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val contentColor = remember {
|
||||||
|
Animatable(
|
||||||
|
if (pagerState.currentPage == i) onPrimaryContainerColor
|
||||||
|
else onSurfaceColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(pagerState, pagerState.currentPage) {
|
||||||
|
containerColor.animateTo(
|
||||||
|
if (pagerState.currentPage == i) primaryContainerColor
|
||||||
|
else Color.Transparent
|
||||||
|
)
|
||||||
|
|
||||||
|
contentColor.animateTo(
|
||||||
|
if (pagerState.currentPage == i) onPrimaryContainerColor
|
||||||
|
else onSurfaceColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.clip(RoundedCornerShape(20))
|
||||||
|
.background(containerColor.value)
|
||||||
|
.clickable { coroutineScope.launch { pagerState.animateScrollToPage(i) } },
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.padding(12.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
stringResource(weekList[i]),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = contentColor.value
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
schedule.days[i].date.dateTime.date.dayOfMonth.toString(),
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
fontWeight = FontWeight.W600,
|
||||||
|
color = contentColor.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
pagerState,
|
pagerState,
|
||||||
Modifier
|
Modifier
|
||||||
.height(600.dp)
|
.height(600.dp),
|
||||||
.padding(top = 5.dp),
|
|
||||||
PaddingValues(horizontal = 7.dp),
|
|
||||||
verticalAlignment = Alignment.Top
|
verticalAlignment = Alignment.Top
|
||||||
) { page ->
|
) { page ->
|
||||||
DayCard(
|
DayCard(
|
||||||
@@ -59,7 +186,7 @@ fun SchedulePager(schedule: GroupOrTeacher = MockScheduleRepository.exampleTeach
|
|||||||
).absoluteValue
|
).absoluteValue
|
||||||
|
|
||||||
lerp(
|
lerp(
|
||||||
start = 1f, stop = 0.95f, fraction = 1f - offset.coerceIn(0f, 1f)
|
start = 1f, stop = 0.95f, fraction = offset.coerceIn(0f, 1f)
|
||||||
).also { scale ->
|
).also { scale ->
|
||||||
scaleX = scale
|
scaleX = scale
|
||||||
scaleY = scale
|
scaleY = scale
|
||||||
@@ -69,7 +196,14 @@ fun SchedulePager(schedule: GroupOrTeacher = MockScheduleRepository.exampleTeach
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
schedule.days[page]
|
schedule.days[page]
|
||||||
)
|
) { dialogLesson = WeakReference(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dialogLesson?.get()?.let { lesson ->
|
||||||
|
if (lesson.type == LessonType.BREAK)
|
||||||
|
return@let
|
||||||
|
|
||||||
|
ExtraInfoDialog(lesson) { dialogLesson = null }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ru.n08i40k.polytechnic.next.ui.screen.schedule
|
package ru.n08i40k.polytechnic.next.ui.widgets.schedule
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -6,6 +6,8 @@ import androidx.compose.foundation.layout.Column
|
|||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -19,31 +21,31 @@ import androidx.compose.ui.text.font.FontFamily
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import ru.n08i40k.polytechnic.next.CacheDate
|
||||||
import ru.n08i40k.polytechnic.next.R
|
import ru.n08i40k.polytechnic.next.R
|
||||||
import ru.n08i40k.polytechnic.next.UpdateDates
|
import ru.n08i40k.polytechnic.next.ui.screen.schedule.PaskhalkoDialog
|
||||||
import ru.n08i40k.polytechnic.next.ui.widgets.ExpandableCard
|
import ru.n08i40k.polytechnic.next.ui.widgets.ExpandableCard
|
||||||
import ru.n08i40k.polytechnic.next.ui.widgets.ExpandableCardTitle
|
import ru.n08i40k.polytechnic.next.ui.widgets.ExpandableCardTitle
|
||||||
import ru.n08i40k.polytechnic.next.utils.*
|
import ru.n08i40k.polytechnic.next.utils.*
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
|
|
||||||
val expanded = mutableStateOf(false)
|
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun UpdateInfo(
|
fun UpdateInfo(
|
||||||
lastUpdateAt: Long = 0,
|
lastUpdateAt: Long = 0,
|
||||||
updateDates: UpdateDates = UpdateDates.newBuilder().build()
|
cacheDate: CacheDate = CacheDate.newBuilder().build()
|
||||||
) {
|
) {
|
||||||
var expanded by remember { expanded }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val format = "HH:mm:ss dd.MM.yyyy"
|
val format = "HH:mm:ss dd.MM.yyyy"
|
||||||
|
|
||||||
val currentDate = Date(lastUpdateAt).toString(format)
|
val currentDate = Date(lastUpdateAt).toString(format)
|
||||||
val cacheUpdateDate = Date(updateDates.cache).toString(format)
|
val cacheUpdateDate = Date(cacheDate.cache).toString(format)
|
||||||
val scheduleUpdateDate = Date(updateDates.schedule).toString(format)
|
val scheduleUpdateDate = Date(cacheDate.schedule).toString(format)
|
||||||
|
|
||||||
ExpandableCard(
|
ExpandableCard(
|
||||||
expanded = expanded,
|
expanded = expanded,
|
||||||
|
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerLowest),
|
||||||
onExpandedChange = { expanded = !expanded },
|
onExpandedChange = { expanded = !expanded },
|
||||||
title = { ExpandableCardTitle(stringResource(R.string.update_info_header)) }
|
title = { ExpandableCardTitle(stringResource(R.string.update_info_header)) }
|
||||||
) {
|
) {
|
||||||
@@ -13,7 +13,7 @@ import kotlinx.coroutines.flow.first
|
|||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import ru.n08i40k.polytechnic.next.app.appContainer
|
import ru.n08i40k.polytechnic.next.app.appContainer
|
||||||
import ru.n08i40k.polytechnic.next.settings.settings
|
import ru.n08i40k.polytechnic.next.proto.settings
|
||||||
import ru.n08i40k.polytechnic.next.utils.MyResult
|
import ru.n08i40k.polytechnic.next.utils.MyResult
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.logging.Logger
|
import java.util.logging.Logger
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import androidx.work.WorkerParameters
|
|||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import ru.n08i40k.polytechnic.next.app.appContainer
|
import ru.n08i40k.polytechnic.next.app.appContainer
|
||||||
import ru.n08i40k.polytechnic.next.settings.settings
|
import ru.n08i40k.polytechnic.next.proto.settings
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.logging.Logger
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
|||||||
19
app/src/main/proto/cache.proto
Normal file
19
app/src/main/proto/cache.proto
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option java_package = "ru.n08i40k.polytechnic.next";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message CacheResponse {
|
||||||
|
string hash = 1;
|
||||||
|
string data = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CacheDate {
|
||||||
|
int64 cache = 1;
|
||||||
|
int64 schedule = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Cache {
|
||||||
|
map<string, CacheResponse> storage = 4;
|
||||||
|
CacheDate date = 5;
|
||||||
|
}
|
||||||
15
app/src/main/proto/settings-v2.proto
Normal file
15
app/src/main/proto/settings-v2.proto
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
option java_package = "ru.n08i40k.polytechnic.next";
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message SettingsV2 {
|
||||||
|
string user_id = 1;
|
||||||
|
string access_token = 2;
|
||||||
|
string group = 3;
|
||||||
|
|
||||||
|
string version = 5;
|
||||||
|
string suppressed_version = 6;
|
||||||
|
|
||||||
|
string fcm_token = 7;
|
||||||
|
}
|
||||||
9
app/src/main/res/drawable/ic_cabinet.xml
Normal file
9
app/src/main/res/drawable/ic_cabinet.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="M120,840v-80h80v-640h400v40h160v600h80v80L680,840v-600h-80v600L120,840ZM280,200v560,-560ZM440,520q17,0 28.5,-11.5T480,480q0,-17 -11.5,-28.5T440,440q-17,0 -28.5,11.5T400,480q0,17 11.5,28.5T440,520ZM280,760h240v-560L280,200v560Z"
|
||||||
|
android:fillColor="#e8eaed"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_group.xml
Normal file
9
app/src/main/res/drawable/ic_group.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="20dp"
|
||||||
|
android:height="20dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="M96,768v-92q0,-25.78 12.5,-47.39T143,594q54,-32 114.5,-49T384,528q66,0 126.5,17T625,594q22,13 34.5,34.61T672,676v92L96,768ZM744,768v-92q0,-42 -19.5,-78T672,539q39,8 75.5,21.5T817,594q22,13 34.5,34.67Q864,650.35 864,676v92L744,768ZM384,480q-60,0 -102,-42t-42,-102q0,-60 42,-102t102,-42q60,0 102,42t42,102q0,60 -42,102t-102,42ZM720,336q0,60 -42,102t-102,42q-8,0 -15,-0.5t-15,-2.5q25,-29 39.5,-64.5T600,336q0,-41 -14.5,-76.5T546,195q8,-2 15,-2.5t15,-0.5q60,0 102,42t42,102ZM168,696h432v-20q0,-6.47 -3.03,-11.76 -3.02,-5.3 -7.97,-8.24 -47,-27 -99,-41.5T384,600q-54,0 -106,14t-99,42q-4.95,2.83 -7.98,7.91 -3.02,5.09 -3.02,12L168,696ZM384.21,408Q414,408 435,386.79t21,-51Q456,306 434.79,285t-51,-21Q354,264 333,285.21t-21,51Q312,366 333.21,387t51,21ZM384,696ZM384,336Z"
|
||||||
|
android:fillColor="#e8eaed"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_teacher.xml
Normal file
9
app/src/main/res/drawable/ic_teacher.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="M480,840 L200,688v-240L40,360l440,-240 440,240v320h-80v-276l-80,44v240L480,840ZM480,508 L754,360 480,212 206,360 480,508ZM480,749 L680,641v-151L480,600 280,490v151l200,108ZM480,508ZM480,598ZM480,598Z"
|
||||||
|
android:fillColor="#e8eaed"/>
|
||||||
|
</vector>
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
<string name="auth_error_disallowed_role">Пожалуйста, используйте другую роль.</string>
|
<string name="auth_error_disallowed_role">Пожалуйста, используйте другую роль.</string>
|
||||||
<string name="sign_in_manual">По имени пользователя</string>
|
<string name="sign_in_manual">По имени пользователя</string>
|
||||||
<string name="updater_support_end">Поддержка версий ниже %1$s прекращена!</string>
|
<string name="updater_support_end">Поддержка версий ниже %1$s прекращена!</string>
|
||||||
<string name="last_server_schedule_update">Последнее обновление расписания</string>
|
<string name="last_server_schedule_update">Расписание</string>
|
||||||
<string name="teacher_name">ФИО преподавателя</string>
|
<string name="teacher_name">ФИО преподавателя</string>
|
||||||
<string name="updater_body">Желаете ли вы обновиться до последней версии?</string>
|
<string name="updater_body">Желаете ли вы обновиться до последней версии?</string>
|
||||||
<string name="updater_new_version">Вышла новая версия приложения!</string>
|
<string name="updater_new_version">Вышла новая версия приложения!</string>
|
||||||
@@ -76,8 +76,8 @@
|
|||||||
<string name="updater_update">ОБНОВИТЬ</string>
|
<string name="updater_update">ОБНОВИТЬ</string>
|
||||||
<string name="updater_suppress">ЗАГЛУШИТЬ</string>
|
<string name="updater_suppress">ЗАГЛУШИТЬ</string>
|
||||||
<string name="update_info_header">Дополнительная информация</string>
|
<string name="update_info_header">Дополнительная информация</string>
|
||||||
<string name="last_local_update">Последнее локальное обновление</string>
|
<string name="last_local_update">Локально</string>
|
||||||
<string name="last_server_cache_update">Последнее обновление кеша</string>
|
<string name="last_server_cache_update">Кеш</string>
|
||||||
<string name="download_update">Скачать обновление</string>
|
<string name="download_update">Скачать обновление</string>
|
||||||
<string name="telegram_channel">Телеграм канал</string>
|
<string name="telegram_channel">Телеграм канал</string>
|
||||||
<string name="schedule_update_title">Расписание обновлено!</string>
|
<string name="schedule_update_title">Расписание обновлено!</string>
|
||||||
@@ -100,4 +100,20 @@
|
|||||||
<string name="day_view_end_description">Ура, можно идти домой! Наверное :(</string>
|
<string name="day_view_end_description">Ура, можно идти домой! Наверное :(</string>
|
||||||
<string name="day_view_channel_name">Текущая пара</string>
|
<string name="day_view_channel_name">Текущая пара</string>
|
||||||
<string name="day_view_channel_description">Отображает текущую пару или перемену в уведомлении</string>
|
<string name="day_view_channel_description">Отображает текущую пару или перемену в уведомлении</string>
|
||||||
|
<string name="extra_info_lesson_name">"Название: "</string>
|
||||||
|
<string name="extra_info_teacher">"Преподаватель: "</string>
|
||||||
|
<string name="extra_info_teacher_second">%1$s в %2$s каб.</string>
|
||||||
|
<string name="extra_info_teacher_second_subgroup">%1$s в %2$s каб. [подгруппа - %3$d]</string>
|
||||||
|
<string name="extra_info_duration">"Длительность: "</string>
|
||||||
|
<string name="extra_info_duration_second">С %1$s до %2$s (%3$d ч. %4$d мин.)</string>
|
||||||
|
<string name="extra_info_type">"Тип: "</string>
|
||||||
|
<string name="minutes_full">минут</string>
|
||||||
|
<string name="only_for_second">Только у второй</string>
|
||||||
|
<string name="only_for_first">Только у первой</string>
|
||||||
|
<string name="week_bar_monday">Пн</string>
|
||||||
|
<string name="week_bar_tuesday">Вт</string>
|
||||||
|
<string name="week_bar_wednesday">Ср</string>
|
||||||
|
<string name="week_bar_thursday">Чт</string>
|
||||||
|
<string name="week_bar_friday">Пт</string>
|
||||||
|
<string name="week_bar_saturday">Сб</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -100,4 +100,20 @@
|
|||||||
<string name="day_view_end_description">ya ne budu eto perevidit\'</string>
|
<string name="day_view_end_description">ya ne budu eto perevidit\'</string>
|
||||||
<string name="day_view_channel_name">Current lesson</string>
|
<string name="day_view_channel_name">Current lesson</string>
|
||||||
<string name="day_view_channel_description">View the current lesson and breaks in notification</string>
|
<string name="day_view_channel_description">View the current lesson and breaks in notification</string>
|
||||||
|
<string name="extra_info_lesson_name">"Name: "</string>
|
||||||
|
<string name="extra_info_teacher">"Teacher: "</string>
|
||||||
|
<string name="extra_info_teacher_second">%1$s in %2$s cab.</string>
|
||||||
|
<string name="extra_info_teacher_second_subgroup">%1$s in %2$s cab. [subgroup - %3$d]</string>
|
||||||
|
<string name="extra_info_duration">"Duration: "</string>
|
||||||
|
<string name="extra_info_duration_second">From %1$s to %2$s (%3$d h. %4$d min.)</string>
|
||||||
|
<string name="extra_info_type">"Type: "</string>
|
||||||
|
<string name="minutes_full">minutes</string>
|
||||||
|
<string name="only_for_second">Only for second</string>
|
||||||
|
<string name="only_for_first">Only for first</string>
|
||||||
|
<string name="week_bar_tuesday">Tue</string>
|
||||||
|
<string name="week_bar_wednesday">Wed</string>
|
||||||
|
<string name="week_bar_thursday">Thu</string>
|
||||||
|
<string name="week_bar_friday">Fri</string>
|
||||||
|
<string name="week_bar_saturday">Sat</string>
|
||||||
|
<string name="week_bar_monday">Mon</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -4,7 +4,7 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.android) apply false
|
alias(libs.plugins.kotlin.android) apply false
|
||||||
alias(libs.plugins.kotlin.compose) apply false
|
alias(libs.plugins.kotlin.compose) apply false
|
||||||
|
|
||||||
id("com.google.devtools.ksp") version "2.1.10-1.0.30" apply false
|
id("com.google.devtools.ksp") version "2.1.20-1.0.31" apply false
|
||||||
|
|
||||||
id("com.google.protobuf") version "0.9.4" apply false
|
id("com.google.protobuf") version "0.9.4" apply false
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,33 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.8.1"
|
agp = "8.8.2"
|
||||||
desugar_jdk_libs = "2.1.4"
|
desugar_jdk_libs = "2.1.5"
|
||||||
kotlin = "2.1.10"
|
kotlin = "2.1.10"
|
||||||
coreKtx = "1.15.0"
|
coreKtx = "1.15.0"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
junitVersion = "1.2.1"
|
junitVersion = "1.2.1"
|
||||||
espressoCore = "3.6.1"
|
espressoCore = "3.6.1"
|
||||||
lifecycleRuntimeKtx = "2.8.7"
|
lifecycleRuntimeKtx = "2.8.7"
|
||||||
activityCompose = "1.10.0"
|
activityCompose = "1.10.1"
|
||||||
composeBom = "2025.02.00"
|
composeBom = "2025.03.00"
|
||||||
accompanistSwiperefresh = "0.36.0"
|
accompanistSwiperefresh = "0.36.0"
|
||||||
firebaseBom = "33.9.0"
|
firebaseBom = "33.10.0"
|
||||||
hiltAndroid = "2.55"
|
hiltAndroid = "2.57.2"
|
||||||
hiltAndroidCompiler = "2.55"
|
hiltAndroidCompiler = "2.56"
|
||||||
hiltNavigationCompose = "1.2.0"
|
hiltNavigationCompose = "1.2.0"
|
||||||
kotlinxSerializationJson = "1.8.0"
|
kotlinxSerializationJson = "1.8.0"
|
||||||
protobufLite = "3.0.1"
|
protobufLite = "3.0.1"
|
||||||
|
runtimeTracing = "1.7.8"
|
||||||
volley = "1.2.1"
|
volley = "1.2.1"
|
||||||
datastore = "1.1.2"
|
datastore = "1.1.3"
|
||||||
navigationCompose = "2.8.9"
|
navigationCompose = "2.8.9"
|
||||||
googleFirebaseCrashlytics = "3.0.3"
|
googleFirebaseCrashlytics = "3.0.3"
|
||||||
workRuntime = "2.10.0"
|
workRuntime = "2.10.0"
|
||||||
vkid = "2.3.1"
|
#noinspection GradleDependency
|
||||||
|
vkid = "2.2.2"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
|
androidx-runtime-tracing = { module = "androidx.compose.runtime:runtime-tracing", version.ref = "runtimeTracing" }
|
||||||
desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" }
|
desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" }
|
||||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||||
@@ -59,9 +62,9 @@ volley = { group = "com.android.volley", name = "volley", version.ref = "volley"
|
|||||||
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
|
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
|
||||||
|
|
||||||
firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" }
|
firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" }
|
||||||
firebase-analytics = { module = "com.google.firebase:firebase-analytics", version = "22.2.0" }
|
firebase-analytics = { module = "com.google.firebase:firebase-analytics", version = "22.3.0" }
|
||||||
firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics", version = "19.4.0" }
|
firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics", version = "19.4.1" }
|
||||||
firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging", version = "24.1.0" }
|
firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging", version = "24.1.1" }
|
||||||
firebase-config = { group = "com.google.firebase", name = "firebase-config", version = "22.1.0" }
|
firebase-config = { group = "com.google.firebase", name = "firebase-config", version = "22.1.0" }
|
||||||
vk-vkid = {group = "com.vk.id", name = "vkid", version.ref = "vkid" }
|
vk-vkid = {group = "com.vk.id", name = "vkid", version.ref = "vkid" }
|
||||||
vk-onetap-compose = {group = "com.vk.id", name = "onetap-compose", version.ref = "vkid" }
|
vk-onetap-compose = {group = "com.vk.id", name = "onetap-compose", version.ref = "vkid" }
|
||||||
|
|||||||
Reference in New Issue
Block a user