mirror of
https://github.com/n08i40k/polytechnic-android.git
synced 2025-12-06 09:47:48 +03:00
1.5.0
Уведомления об обновлении расписания. Обновление кеша сервера в фоновом режиме (раз в 15 минут). Теперь полный порядок с запросами.
This commit is contained in:
18
.idea/appInsightsSettings.xml
generated
18
.idea/appInsightsSettings.xml
generated
@@ -15,15 +15,33 @@
|
|||||||
<option name="projectNumber" value="946974192625" />
|
<option name="projectNumber" value="946974192625" />
|
||||||
</ConnectionSetting>
|
</ConnectionSetting>
|
||||||
</option>
|
</option>
|
||||||
|
<option name="devices">
|
||||||
|
<list />
|
||||||
|
</option>
|
||||||
<option name="signal" value="SIGNAL_UNSPECIFIED" />
|
<option name="signal" value="SIGNAL_UNSPECIFIED" />
|
||||||
<option name="timeIntervalDays" value="THIRTY_DAYS" />
|
<option name="timeIntervalDays" value="THIRTY_DAYS" />
|
||||||
<option name="versions">
|
<option name="versions">
|
||||||
<list>
|
<list>
|
||||||
|
<VersionSetting>
|
||||||
|
<option name="buildVersion" value="9" />
|
||||||
|
<option name="displayName" value="1.4.0 (9)" />
|
||||||
|
<option name="displayVersion" value="1.4.0" />
|
||||||
|
</VersionSetting>
|
||||||
<VersionSetting>
|
<VersionSetting>
|
||||||
<option name="buildVersion" value="8" />
|
<option name="buildVersion" value="8" />
|
||||||
<option name="displayName" value="1.3.2 (8)" />
|
<option name="displayName" value="1.3.2 (8)" />
|
||||||
<option name="displayVersion" value="1.3.2" />
|
<option name="displayVersion" value="1.3.2" />
|
||||||
</VersionSetting>
|
</VersionSetting>
|
||||||
|
<VersionSetting>
|
||||||
|
<option name="buildVersion" value="7" />
|
||||||
|
<option name="displayName" value="1.3.1 (7)" />
|
||||||
|
<option name="displayVersion" value="1.3.1" />
|
||||||
|
</VersionSetting>
|
||||||
|
<VersionSetting>
|
||||||
|
<option name="buildVersion" value="6" />
|
||||||
|
<option name="displayName" value="1.3.0 (6)" />
|
||||||
|
<option name="displayVersion" value="1.3.0" />
|
||||||
|
</VersionSetting>
|
||||||
</list>
|
</list>
|
||||||
</option>
|
</option>
|
||||||
<option name="visibilityType" value="ALL" />
|
<option name="visibilityType" value="ALL" />
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ android {
|
|||||||
applicationId = "ru.n08i40k.polytechnic.next"
|
applicationId = "ru.n08i40k.polytechnic.next"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 9
|
versionCode = 10
|
||||||
versionName = "1.4.0"
|
versionName = "1.5.0"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
@@ -89,13 +89,22 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
// work manager
|
||||||
|
implementation(libs.androidx.work.runtime)
|
||||||
|
implementation(libs.androidx.work.runtime.ktx)
|
||||||
|
|
||||||
|
// hilt
|
||||||
implementation(libs.hilt.android)
|
implementation(libs.hilt.android)
|
||||||
ksp(libs.hilt.android.compiler)
|
ksp(libs.hilt.android.compiler)
|
||||||
implementation(libs.androidx.hilt.navigation.compose)
|
implementation(libs.androidx.hilt.navigation.compose)
|
||||||
|
|
||||||
|
// firebase
|
||||||
implementation(platform(libs.firebase.bom))
|
implementation(platform(libs.firebase.bom))
|
||||||
implementation(libs.firebase.analytics)
|
implementation(libs.firebase.analytics)
|
||||||
|
implementation(libs.firebase.messaging)
|
||||||
|
implementation(libs.firebase.crashlytics)
|
||||||
|
|
||||||
|
// datastore
|
||||||
implementation(libs.androidx.datastore)
|
implementation(libs.androidx.datastore)
|
||||||
implementation(libs.protobuf.lite)
|
implementation(libs.protobuf.lite)
|
||||||
|
|
||||||
@@ -112,7 +121,7 @@ dependencies {
|
|||||||
implementation(libs.androidx.material3)
|
implementation(libs.androidx.material3)
|
||||||
implementation(libs.volley)
|
implementation(libs.volley)
|
||||||
implementation(libs.androidx.navigation.compose)
|
implementation(libs.androidx.navigation.compose)
|
||||||
implementation(libs.firebase.crashlytics)
|
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".PolytechnicApplication"
|
android:name=".PolytechnicApplication"
|
||||||
@@ -14,6 +15,15 @@
|
|||||||
android:roundIcon="@drawable/ic_launcher_round"
|
android:roundIcon="@drawable/ic_launcher_round"
|
||||||
android:theme="@style/Theme.PolytechnicNext"
|
android:theme="@style/Theme.PolytechnicNext"
|
||||||
tools:targetApi="35">
|
tools:targetApi="35">
|
||||||
|
|
||||||
|
<service
|
||||||
|
android:name=".service.MyFirebaseMessagingService"
|
||||||
|
android:exported="false">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||||
|
</intent-filter>
|
||||||
|
</service>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.MainActivity"
|
android:name=".ui.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package ru.n08i40k.polytechnic.next
|
||||||
|
|
||||||
|
object NotificationChannels {
|
||||||
|
const val SCHEDULE_UPDATE = "schedule-update"
|
||||||
|
}
|
||||||
@@ -10,9 +10,9 @@ import ru.n08i40k.polytechnic.next.data.cache.NetworkCacheRepository
|
|||||||
import ru.n08i40k.polytechnic.next.data.cache.impl.FakeNetworkCacheRepository
|
import ru.n08i40k.polytechnic.next.data.cache.impl.FakeNetworkCacheRepository
|
||||||
import ru.n08i40k.polytechnic.next.data.cache.impl.LocalNetworkCacheRepository
|
import ru.n08i40k.polytechnic.next.data.cache.impl.LocalNetworkCacheRepository
|
||||||
import ru.n08i40k.polytechnic.next.data.schedule.ScheduleRepository
|
import ru.n08i40k.polytechnic.next.data.schedule.ScheduleRepository
|
||||||
import ru.n08i40k.polytechnic.next.data.schedule.impl.FakeScheduleReplacerRepository
|
import ru.n08i40k.polytechnic.next.data.scheduleReplacer.impl.FakeScheduleReplacerRepository
|
||||||
import ru.n08i40k.polytechnic.next.data.schedule.impl.FakeScheduleRepository
|
import ru.n08i40k.polytechnic.next.data.schedule.impl.FakeScheduleRepository
|
||||||
import ru.n08i40k.polytechnic.next.data.schedule.impl.RemoteScheduleReplacerRepository
|
import ru.n08i40k.polytechnic.next.data.scheduleReplacer.impl.RemoteScheduleReplacerRepository
|
||||||
import ru.n08i40k.polytechnic.next.data.schedule.impl.RemoteScheduleRepository
|
import ru.n08i40k.polytechnic.next.data.schedule.impl.RemoteScheduleRepository
|
||||||
import ru.n08i40k.polytechnic.next.data.scheduleReplacer.ScheduleReplacerRepository
|
import ru.n08i40k.polytechnic.next.data.scheduleReplacer.ScheduleReplacerRepository
|
||||||
import ru.n08i40k.polytechnic.next.data.users.ProfileRepository
|
import ru.n08i40k.polytechnic.next.data.users.ProfileRepository
|
||||||
|
|||||||
@@ -9,14 +9,11 @@ import kotlinx.coroutines.withContext
|
|||||||
import ru.n08i40k.polytechnic.next.data.MyResult
|
import ru.n08i40k.polytechnic.next.data.MyResult
|
||||||
import ru.n08i40k.polytechnic.next.data.schedule.ScheduleRepository
|
import ru.n08i40k.polytechnic.next.data.schedule.ScheduleRepository
|
||||||
import ru.n08i40k.polytechnic.next.model.Group
|
import ru.n08i40k.polytechnic.next.model.Group
|
||||||
import ru.n08i40k.polytechnic.next.network.data.schedule.ScheduleGetReq
|
import ru.n08i40k.polytechnic.next.network.request.schedule.ScheduleGet
|
||||||
import ru.n08i40k.polytechnic.next.network.data.schedule.ScheduleGetReqData
|
|
||||||
import ru.n08i40k.polytechnic.next.network.tryFuture
|
import ru.n08i40k.polytechnic.next.network.tryFuture
|
||||||
import ru.n08i40k.polytechnic.next.settings.settingsDataStore
|
import ru.n08i40k.polytechnic.next.settings.settingsDataStore
|
||||||
|
|
||||||
class RemoteScheduleRepository(private val context: Context) : ScheduleRepository {
|
class RemoteScheduleRepository(private val context: Context) : ScheduleRepository {
|
||||||
|
|
||||||
|
|
||||||
override suspend fun getGroup(): MyResult<Group> {
|
override suspend fun getGroup(): MyResult<Group> {
|
||||||
return withContext(Dispatchers.IO) {
|
return withContext(Dispatchers.IO) {
|
||||||
val groupName = runBlocking {
|
val groupName = runBlocking {
|
||||||
@@ -27,8 +24,8 @@ class RemoteScheduleRepository(private val context: Context) : ScheduleRepositor
|
|||||||
return@withContext MyResult.Failure(IllegalArgumentException("No group name provided!"))
|
return@withContext MyResult.Failure(IllegalArgumentException("No group name provided!"))
|
||||||
|
|
||||||
val response = tryFuture {
|
val response = tryFuture {
|
||||||
ScheduleGetReq(
|
ScheduleGet(
|
||||||
ScheduleGetReqData(groupName),
|
ScheduleGet.RequestDto(groupName),
|
||||||
context,
|
context,
|
||||||
it,
|
it,
|
||||||
it
|
it
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package ru.n08i40k.polytechnic.next.data.schedule.impl
|
package ru.n08i40k.polytechnic.next.data.scheduleReplacer.impl
|
||||||
|
|
||||||
import ru.n08i40k.polytechnic.next.data.MyResult
|
import ru.n08i40k.polytechnic.next.data.MyResult
|
||||||
import ru.n08i40k.polytechnic.next.data.scheduleReplacer.ScheduleReplacerRepository
|
import ru.n08i40k.polytechnic.next.data.scheduleReplacer.ScheduleReplacerRepository
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package ru.n08i40k.polytechnic.next.data.schedule.impl
|
package ru.n08i40k.polytechnic.next.data.scheduleReplacer.impl
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -6,15 +6,15 @@ import kotlinx.coroutines.withContext
|
|||||||
import ru.n08i40k.polytechnic.next.data.MyResult
|
import ru.n08i40k.polytechnic.next.data.MyResult
|
||||||
import ru.n08i40k.polytechnic.next.data.scheduleReplacer.ScheduleReplacerRepository
|
import ru.n08i40k.polytechnic.next.data.scheduleReplacer.ScheduleReplacerRepository
|
||||||
import ru.n08i40k.polytechnic.next.model.ScheduleReplacer
|
import ru.n08i40k.polytechnic.next.model.ScheduleReplacer
|
||||||
import ru.n08i40k.polytechnic.next.network.data.scheduleReplacer.ScheduleReplacerClearReq
|
import ru.n08i40k.polytechnic.next.network.request.scheduleReplacer.ScheduleReplacerClear
|
||||||
import ru.n08i40k.polytechnic.next.network.data.scheduleReplacer.ScheduleReplacerGetReq
|
import ru.n08i40k.polytechnic.next.network.request.scheduleReplacer.ScheduleReplacerGet
|
||||||
import ru.n08i40k.polytechnic.next.network.data.scheduleReplacer.ScheduleReplacerSetReq
|
import ru.n08i40k.polytechnic.next.network.request.scheduleReplacer.ScheduleReplacerSet
|
||||||
import ru.n08i40k.polytechnic.next.network.tryFuture
|
import ru.n08i40k.polytechnic.next.network.tryFuture
|
||||||
|
|
||||||
class RemoteScheduleReplacerRepository(private val context: Context) : ScheduleReplacerRepository {
|
class RemoteScheduleReplacerRepository(private val context: Context) : ScheduleReplacerRepository {
|
||||||
override suspend fun getAll(): MyResult<List<ScheduleReplacer>> =
|
override suspend fun getAll(): MyResult<List<ScheduleReplacer>> =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
tryFuture { ScheduleReplacerGetReq(context, it, it) }
|
tryFuture { ScheduleReplacerGet(context, it, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -24,12 +24,12 @@ class RemoteScheduleReplacerRepository(private val context: Context) : ScheduleR
|
|||||||
fileType: String
|
fileType: String
|
||||||
): MyResult<Nothing> =
|
): MyResult<Nothing> =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
tryFuture { ScheduleReplacerSetReq(context, fileName, fileData, fileType, it, it) }
|
tryFuture { ScheduleReplacerSet(context, fileName, fileData, fileType, it, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun clear(): MyResult<Int> {
|
override suspend fun clear(): MyResult<Int> {
|
||||||
val response = withContext(Dispatchers.IO) {
|
val response = withContext(Dispatchers.IO) {
|
||||||
tryFuture { ScheduleReplacerClearReq(context, it, it) }
|
tryFuture { ScheduleReplacerClear(context, it, it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return when (response) {
|
return when (response) {
|
||||||
|
|||||||
@@ -5,4 +5,6 @@ import ru.n08i40k.polytechnic.next.model.Profile
|
|||||||
|
|
||||||
interface ProfileRepository {
|
interface ProfileRepository {
|
||||||
suspend fun getProfile(): MyResult<Profile>
|
suspend fun getProfile(): MyResult<Profile>
|
||||||
|
|
||||||
|
suspend fun setFcmToken(token: String): MyResult<Unit>
|
||||||
}
|
}
|
||||||
@@ -26,4 +26,8 @@ class FakeProfileRepository : ProfileRepository {
|
|||||||
MyResult.Success(exampleProfile)
|
MyResult.Success(exampleProfile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun setFcmToken(token: String): MyResult<Unit> {
|
||||||
|
return MyResult.Success(Unit)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,18 +6,18 @@ import kotlinx.coroutines.withContext
|
|||||||
import ru.n08i40k.polytechnic.next.data.MyResult
|
import ru.n08i40k.polytechnic.next.data.MyResult
|
||||||
import ru.n08i40k.polytechnic.next.data.users.ProfileRepository
|
import ru.n08i40k.polytechnic.next.data.users.ProfileRepository
|
||||||
import ru.n08i40k.polytechnic.next.model.Profile
|
import ru.n08i40k.polytechnic.next.model.Profile
|
||||||
import ru.n08i40k.polytechnic.next.network.data.profile.UsersMeRequest
|
import ru.n08i40k.polytechnic.next.network.request.fcm.FcmSetToken
|
||||||
|
import ru.n08i40k.polytechnic.next.network.request.profile.ProfileMe
|
||||||
import ru.n08i40k.polytechnic.next.network.tryFuture
|
import ru.n08i40k.polytechnic.next.network.tryFuture
|
||||||
|
|
||||||
class RemoteProfileRepository(private val context: Context) : ProfileRepository {
|
class RemoteProfileRepository(private val context: Context) : ProfileRepository {
|
||||||
override suspend fun getProfile(): MyResult<Profile> =
|
override suspend fun getProfile(): MyResult<Profile> =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
tryFuture {
|
tryFuture { ProfileMe(context, it, it) }
|
||||||
UsersMeRequest(
|
}
|
||||||
context,
|
|
||||||
it,
|
override suspend fun setFcmToken(token: String): MyResult<Unit> =
|
||||||
it
|
withContext(Dispatchers.IO) {
|
||||||
)
|
tryFuture { FcmSetToken(context, token, it, it) }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ package ru.n08i40k.polytechnic.next.network
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.volley.Response
|
import com.android.volley.Response
|
||||||
import ru.n08i40k.polytechnic.next.network.data.AuthorizedRequest
|
import ru.n08i40k.polytechnic.next.network.request.AuthorizedRequest
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network
|
package ru.n08i40k.polytechnic.next.network
|
||||||
|
|
||||||
object NetworkValues {
|
object NetworkValues {
|
||||||
const val API_HOST = "https://polytechnic.n08i40k.ru:5050/api/v1/"
|
const val API_HOST = "https://192.168.0.103:5050/api/v1/"
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,9 @@ fun <ResultT, RequestT : RequestBase> tryFuture(
|
|||||||
buildRequest: (RequestFuture<ResultT>) -> RequestT
|
buildRequest: (RequestFuture<ResultT>) -> RequestT
|
||||||
): MyResult<ResultT> {
|
): MyResult<ResultT> {
|
||||||
val future = RequestFuture.newFuture<ResultT>()
|
val future = RequestFuture.newFuture<ResultT>()
|
||||||
|
|
||||||
buildRequest(future).send()
|
buildRequest(future).send()
|
||||||
|
|
||||||
return tryGet(future)
|
return tryGet(future)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.auth
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ChangePasswordRequestData(val oldPassword: String, val newPassword: String)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.auth
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class LoginRequestData(val username: String, val password: String)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.auth
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class LoginResponseData(val id: String, val accessToken: String)
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.auth
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import ru.n08i40k.polytechnic.next.model.UserRole
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class RegisterRequestData(
|
|
||||||
val username: String,
|
|
||||||
val password: String,
|
|
||||||
val group: String,
|
|
||||||
val role: UserRole
|
|
||||||
)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.auth
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class RegisterResponseData(val id: String, val accessToken: String)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.profile
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ChangeGroupRequestData(val group: String)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.profile
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ChangeUsernameRequestData(val username: String)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.schedule
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.android.volley.Response
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import ru.n08i40k.polytechnic.next.network.data.AuthorizedRequest
|
|
||||||
|
|
||||||
class ScheduleGetCacheStatusReq(
|
|
||||||
context: Context,
|
|
||||||
listener: Response.Listener<ScheduleGetCacheStatusResData>,
|
|
||||||
errorListener: Response.ErrorListener? = null
|
|
||||||
) : AuthorizedRequest(
|
|
||||||
context,
|
|
||||||
Method.GET,
|
|
||||||
"schedule/cache-status",
|
|
||||||
{ listener.onResponse(Json.decodeFromString(it)) },
|
|
||||||
errorListener
|
|
||||||
)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.schedule
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ScheduleGetCacheStatusResData(
|
|
||||||
val cacheUpdateRequired: Boolean,
|
|
||||||
val cacheHash: String,
|
|
||||||
val lastCacheUpdate: Long,
|
|
||||||
val lastScheduleUpdate: Long,
|
|
||||||
)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.schedule
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.android.volley.Response
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import ru.n08i40k.polytechnic.next.network.data.CachedRequest
|
|
||||||
|
|
||||||
class ScheduleGetGroupNamesReq(
|
|
||||||
context: Context,
|
|
||||||
listener: Response.Listener<ScheduleGetGroupNamesResData>,
|
|
||||||
errorListener: Response.ErrorListener? = null
|
|
||||||
) : CachedRequest(
|
|
||||||
context,
|
|
||||||
Method.GET,
|
|
||||||
"schedule/get-group-names",
|
|
||||||
{ listener.onResponse(Json.decodeFromString(it)) },
|
|
||||||
errorListener
|
|
||||||
)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.schedule
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ScheduleGetGroupNamesResData(
|
|
||||||
val names: ArrayList<String>,
|
|
||||||
)
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.schedule
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.android.volley.Response
|
|
||||||
import kotlinx.serialization.encodeToString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import ru.n08i40k.polytechnic.next.network.data.CachedRequest
|
|
||||||
|
|
||||||
class ScheduleGetReq(
|
|
||||||
private val data: ScheduleGetReqData,
|
|
||||||
context: Context,
|
|
||||||
listener: Response.Listener<ScheduleGetResData>,
|
|
||||||
errorListener: Response.ErrorListener? = null
|
|
||||||
) : CachedRequest(
|
|
||||||
context,
|
|
||||||
Method.POST,
|
|
||||||
"schedule/get-group",
|
|
||||||
{ listener.onResponse(Json.decodeFromString(it)) },
|
|
||||||
errorListener
|
|
||||||
) {
|
|
||||||
override fun getBody(): ByteArray {
|
|
||||||
return Json.encodeToString(data).toByteArray()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.schedule
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ScheduleGetReqData(val name: String)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.schedule
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import ru.n08i40k.polytechnic.next.model.Group
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ScheduleGetResData(
|
|
||||||
val updatedAt: String,
|
|
||||||
val group: Group,
|
|
||||||
val lastChangedDays: ArrayList<Int>,
|
|
||||||
)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.schedule
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ScheduleUpdateReqData(val mainPage: String)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.scheduleReplacer
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.android.volley.Response
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import ru.n08i40k.polytechnic.next.network.data.AuthorizedRequest
|
|
||||||
|
|
||||||
class ScheduleReplacerClearReq(
|
|
||||||
context: Context,
|
|
||||||
listener: Response.Listener<ScheduleReplacerClearResData>,
|
|
||||||
errorListener: Response.ErrorListener?
|
|
||||||
) : AuthorizedRequest(
|
|
||||||
context,
|
|
||||||
Method.POST,
|
|
||||||
"schedule-replacer/clear",
|
|
||||||
{ listener.onResponse(Json.decodeFromString(it)) },
|
|
||||||
errorListener
|
|
||||||
)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.scheduleReplacer
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ScheduleReplacerClearResData(
|
|
||||||
val count: Int
|
|
||||||
)
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.scheduleReplacer
|
|
||||||
|
|
||||||
import ru.n08i40k.polytechnic.next.model.ScheduleReplacer
|
|
||||||
|
|
||||||
typealias ScheduleReplacerGetResData = List<ScheduleReplacer>
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data
|
package ru.n08i40k.polytechnic.next.network.request
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.volley.AuthFailureError
|
import com.android.volley.AuthFailureError
|
||||||
@@ -40,7 +40,7 @@ open class AuthorizedRequest(
|
|||||||
context.settingsDataStore.data.map { settings -> settings.accessToken }.first()
|
context.settingsDataStore.data.map { settings -> settings.accessToken }.first()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (accessToken.isEmpty())
|
if (accessToken.isEmpty() && context.profileViewModel != null)
|
||||||
context.profileViewModel!!.onUnauthorized()
|
context.profileViewModel!!.onUnauthorized()
|
||||||
|
|
||||||
val headers = super.getHeaders()
|
val headers = super.getHeaders()
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data
|
package ru.n08i40k.polytechnic.next.network.request
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.volley.Response
|
import com.android.volley.Response
|
||||||
@@ -11,10 +11,8 @@ import ru.n08i40k.polytechnic.next.PolytechnicApplication
|
|||||||
import ru.n08i40k.polytechnic.next.data.AppContainer
|
import ru.n08i40k.polytechnic.next.data.AppContainer
|
||||||
import ru.n08i40k.polytechnic.next.data.MyResult
|
import ru.n08i40k.polytechnic.next.data.MyResult
|
||||||
import ru.n08i40k.polytechnic.next.network.NetworkConnection
|
import ru.n08i40k.polytechnic.next.network.NetworkConnection
|
||||||
import ru.n08i40k.polytechnic.next.network.data.schedule.ScheduleGetCacheStatusReq
|
import ru.n08i40k.polytechnic.next.network.request.schedule.ScheduleGetCacheStatus
|
||||||
import ru.n08i40k.polytechnic.next.network.data.schedule.ScheduleGetCacheStatusResData
|
import ru.n08i40k.polytechnic.next.network.request.schedule.ScheduleUpdate
|
||||||
import ru.n08i40k.polytechnic.next.network.data.schedule.ScheduleUpdateReq
|
|
||||||
import ru.n08i40k.polytechnic.next.network.data.schedule.ScheduleUpdateReqData
|
|
||||||
import ru.n08i40k.polytechnic.next.network.tryFuture
|
import ru.n08i40k.polytechnic.next.network.tryFuture
|
||||||
import ru.n08i40k.polytechnic.next.network.tryGet
|
import ru.n08i40k.polytechnic.next.network.tryGet
|
||||||
import java.util.logging.Logger
|
import java.util.logging.Logger
|
||||||
@@ -58,14 +56,14 @@ open class CachedRequest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun updateMainPage(): MyResult<ScheduleGetCacheStatusResData> {
|
private suspend fun updateMainPage(): MyResult<ScheduleGetCacheStatus.ResponseDto> {
|
||||||
return withContext(Dispatchers.IO) {
|
return withContext(Dispatchers.IO) {
|
||||||
when (val mainPage = getMainPage()) {
|
when (val mainPage = getMainPage()) {
|
||||||
is MyResult.Failure -> mainPage
|
is MyResult.Failure -> mainPage
|
||||||
is MyResult.Success -> {
|
is MyResult.Success -> {
|
||||||
tryFuture {
|
tryFuture {
|
||||||
ScheduleUpdateReq(
|
ScheduleUpdate(
|
||||||
ScheduleUpdateReqData(mainPage.data),
|
ScheduleUpdate.RequestDto(mainPage.data),
|
||||||
context,
|
context,
|
||||||
it,
|
it,
|
||||||
it
|
it
|
||||||
@@ -83,7 +81,7 @@ open class CachedRequest(
|
|||||||
logger.info("Getting cache status...")
|
logger.info("Getting cache status...")
|
||||||
|
|
||||||
val cacheStatusResult = tryFuture {
|
val cacheStatusResult = tryFuture {
|
||||||
ScheduleGetCacheStatusReq(context, it, it)
|
ScheduleGetCacheStatus(context, it, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cacheStatusResult is MyResult.Success) {
|
if (cacheStatusResult is MyResult.Success) {
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.auth
|
package ru.n08i40k.polytechnic.next.network.request.auth
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.volley.Response
|
import com.android.volley.Response
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import ru.n08i40k.polytechnic.next.network.data.AuthorizedRequest
|
import ru.n08i40k.polytechnic.next.network.request.AuthorizedRequest
|
||||||
|
|
||||||
class ChangePasswordRequest(
|
class AuthChangePassword(
|
||||||
private val data: ChangePasswordRequestData,
|
private val data: RequestDto,
|
||||||
context: Context,
|
context: Context,
|
||||||
listener: Response.Listener<Nothing>,
|
listener: Response.Listener<Nothing>,
|
||||||
errorListener: Response.ErrorListener?
|
errorListener: Response.ErrorListener?
|
||||||
@@ -15,10 +16,13 @@ class ChangePasswordRequest(
|
|||||||
context,
|
context,
|
||||||
Method.POST,
|
Method.POST,
|
||||||
"auth/change-password",
|
"auth/change-password",
|
||||||
Response.Listener<String> { listener.onResponse(null) },
|
{ listener.onResponse(null) },
|
||||||
errorListener,
|
errorListener,
|
||||||
canBeUnauthorized = true
|
canBeUnauthorized = true
|
||||||
) {
|
) {
|
||||||
|
@Serializable
|
||||||
|
data class RequestDto(val oldPassword: String, val newPassword: String)
|
||||||
|
|
||||||
override fun getBody(): ByteArray {
|
override fun getBody(): ByteArray {
|
||||||
return Json.encodeToString(data).toByteArray()
|
return Json.encodeToString(data).toByteArray()
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.auth
|
package ru.n08i40k.polytechnic.next.network.request.auth
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.volley.Response
|
import com.android.volley.Response
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import ru.n08i40k.polytechnic.next.network.RequestBase
|
import ru.n08i40k.polytechnic.next.network.RequestBase
|
||||||
|
|
||||||
class LoginRequest(
|
class AuthLogin(
|
||||||
private val data: LoginRequestData,
|
private val data: RequestDto,
|
||||||
context: Context,
|
context: Context,
|
||||||
listener: Response.Listener<LoginResponseData>,
|
listener: Response.Listener<ResponseDto>,
|
||||||
errorListener: Response.ErrorListener?
|
errorListener: Response.ErrorListener?
|
||||||
) : RequestBase(
|
) : RequestBase(
|
||||||
context,
|
context,
|
||||||
@@ -18,6 +19,12 @@ class LoginRequest(
|
|||||||
{ listener.onResponse(Json.decodeFromString(it)) },
|
{ listener.onResponse(Json.decodeFromString(it)) },
|
||||||
errorListener
|
errorListener
|
||||||
) {
|
) {
|
||||||
|
@Serializable
|
||||||
|
data class RequestDto(val username: String, val password: String)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ResponseDto(val id: String, val accessToken: String)
|
||||||
|
|
||||||
override fun getBody(): ByteArray {
|
override fun getBody(): ByteArray {
|
||||||
return Json.encodeToString(data).toByteArray()
|
return Json.encodeToString(data).toByteArray()
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,17 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.auth
|
package ru.n08i40k.polytechnic.next.network.request.auth
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.volley.Response
|
import com.android.volley.Response
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import ru.n08i40k.polytechnic.next.model.UserRole
|
||||||
import ru.n08i40k.polytechnic.next.network.RequestBase
|
import ru.n08i40k.polytechnic.next.network.RequestBase
|
||||||
|
|
||||||
class RegisterRequest(
|
class AuthRegister(
|
||||||
private val data: RegisterRequestData,
|
private val data: RequestDto,
|
||||||
context: Context,
|
context: Context,
|
||||||
listener: Response.Listener<RegisterResponseData>,
|
listener: Response.Listener<ResponseDto>,
|
||||||
errorListener: Response.ErrorListener?
|
errorListener: Response.ErrorListener?
|
||||||
) : RequestBase(
|
) : RequestBase(
|
||||||
context,
|
context,
|
||||||
@@ -18,6 +20,17 @@ class RegisterRequest(
|
|||||||
{ listener.onResponse(Json.decodeFromString(it)) },
|
{ listener.onResponse(Json.decodeFromString(it)) },
|
||||||
errorListener
|
errorListener
|
||||||
) {
|
) {
|
||||||
|
@Serializable
|
||||||
|
data class RequestDto(
|
||||||
|
val username: String,
|
||||||
|
val password: String,
|
||||||
|
val group: String,
|
||||||
|
val role: UserRole
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ResponseDto(val id: String, val accessToken: String)
|
||||||
|
|
||||||
override fun getBody(): ByteArray {
|
override fun getBody(): ByteArray {
|
||||||
return Json.encodeToString(data).toByteArray()
|
return Json.encodeToString(data).toByteArray()
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package ru.n08i40k.polytechnic.next.network.request.fcm
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.android.volley.Response
|
||||||
|
import ru.n08i40k.polytechnic.next.network.request.AuthorizedRequest
|
||||||
|
|
||||||
|
class FcmSetToken(
|
||||||
|
context: Context,
|
||||||
|
token: String,
|
||||||
|
listener: Response.Listener<Unit>,
|
||||||
|
errorListener: Response.ErrorListener?,
|
||||||
|
) : AuthorizedRequest(
|
||||||
|
context, Method.POST,
|
||||||
|
"fcm/set-token/$token",
|
||||||
|
{ listener.onResponse(Unit) },
|
||||||
|
errorListener,
|
||||||
|
true
|
||||||
|
)
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.profile
|
package ru.n08i40k.polytechnic.next.network.request.profile
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.volley.Response
|
import com.android.volley.Response
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import ru.n08i40k.polytechnic.next.network.data.AuthorizedRequest
|
import ru.n08i40k.polytechnic.next.network.request.AuthorizedRequest
|
||||||
|
|
||||||
class ChangeGroupRequest(
|
class ProfileChangeGroup(
|
||||||
private val data: ChangeGroupRequestData,
|
private val data: RequestDto,
|
||||||
context: Context,
|
context: Context,
|
||||||
listener: Response.Listener<Nothing>,
|
listener: Response.Listener<Nothing>,
|
||||||
errorListener: Response.ErrorListener?
|
errorListener: Response.ErrorListener?
|
||||||
@@ -15,9 +16,12 @@ class ChangeGroupRequest(
|
|||||||
context,
|
context,
|
||||||
Method.POST,
|
Method.POST,
|
||||||
"users/change-group",
|
"users/change-group",
|
||||||
Response.Listener<String> { listener.onResponse(null) },
|
{ listener.onResponse(null) },
|
||||||
errorListener
|
errorListener
|
||||||
) {
|
) {
|
||||||
|
@Serializable
|
||||||
|
data class RequestDto(val group: String)
|
||||||
|
|
||||||
override fun getBody(): ByteArray {
|
override fun getBody(): ByteArray {
|
||||||
return Json.encodeToString(data).toByteArray()
|
return Json.encodeToString(data).toByteArray()
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.profile
|
package ru.n08i40k.polytechnic.next.network.request.profile
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.volley.Response
|
import com.android.volley.Response
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import ru.n08i40k.polytechnic.next.network.data.AuthorizedRequest
|
import ru.n08i40k.polytechnic.next.network.request.AuthorizedRequest
|
||||||
|
|
||||||
class ChangeUsernameRequest(
|
class ProfileChangeUsername(
|
||||||
private val data: ChangeUsernameRequestData,
|
private val data: RequestDto,
|
||||||
context: Context,
|
context: Context,
|
||||||
listener: Response.Listener<Nothing>,
|
listener: Response.Listener<Nothing>,
|
||||||
errorListener: Response.ErrorListener?
|
errorListener: Response.ErrorListener?
|
||||||
@@ -15,9 +16,12 @@ class ChangeUsernameRequest(
|
|||||||
context,
|
context,
|
||||||
Method.POST,
|
Method.POST,
|
||||||
"users/change-username",
|
"users/change-username",
|
||||||
Response.Listener<String> { listener.onResponse(null) },
|
{ listener.onResponse(null) },
|
||||||
errorListener
|
errorListener
|
||||||
) {
|
) {
|
||||||
|
@Serializable
|
||||||
|
data class RequestDto(val username: String)
|
||||||
|
|
||||||
override fun getBody(): ByteArray {
|
override fun getBody(): ByteArray {
|
||||||
return Json.encodeToString(data).toByteArray()
|
return Json.encodeToString(data).toByteArray()
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.profile
|
package ru.n08i40k.polytechnic.next.network.request.profile
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.volley.Response
|
import com.android.volley.Response
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import ru.n08i40k.polytechnic.next.model.Profile
|
import ru.n08i40k.polytechnic.next.model.Profile
|
||||||
import ru.n08i40k.polytechnic.next.network.data.AuthorizedRequest
|
import ru.n08i40k.polytechnic.next.network.request.AuthorizedRequest
|
||||||
|
|
||||||
class UsersMeRequest(
|
class ProfileMe(
|
||||||
context: Context,
|
context: Context,
|
||||||
listener: Response.Listener<Profile>,
|
listener: Response.Listener<Profile>,
|
||||||
errorListener: Response.ErrorListener?
|
errorListener: Response.ErrorListener?
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package ru.n08i40k.polytechnic.next.network.request.schedule
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.android.volley.Response
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import ru.n08i40k.polytechnic.next.model.Group
|
||||||
|
import ru.n08i40k.polytechnic.next.network.request.CachedRequest
|
||||||
|
|
||||||
|
class ScheduleGet(
|
||||||
|
private val data: RequestDto,
|
||||||
|
context: Context,
|
||||||
|
listener: Response.Listener<ResponseDto>,
|
||||||
|
errorListener: Response.ErrorListener? = null
|
||||||
|
) : CachedRequest(
|
||||||
|
context,
|
||||||
|
Method.POST,
|
||||||
|
"schedule/get-group",
|
||||||
|
{ listener.onResponse(Json.decodeFromString(it)) },
|
||||||
|
errorListener
|
||||||
|
) {
|
||||||
|
@Serializable
|
||||||
|
data class RequestDto(val name: String)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ResponseDto(
|
||||||
|
val updatedAt: String,
|
||||||
|
val group: Group,
|
||||||
|
val lastChangedDays: ArrayList<Int>,
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun getBody(): ByteArray {
|
||||||
|
return Json.encodeToString(data).toByteArray()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package ru.n08i40k.polytechnic.next.network.request.schedule
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.android.volley.Response
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import ru.n08i40k.polytechnic.next.network.request.AuthorizedRequest
|
||||||
|
|
||||||
|
class ScheduleGetCacheStatus(
|
||||||
|
context: Context,
|
||||||
|
listener: Response.Listener<ResponseDto>,
|
||||||
|
errorListener: Response.ErrorListener? = null
|
||||||
|
) : AuthorizedRequest(
|
||||||
|
context,
|
||||||
|
Method.GET,
|
||||||
|
"schedule/cache-status",
|
||||||
|
{ listener.onResponse(Json.decodeFromString(it)) },
|
||||||
|
errorListener
|
||||||
|
) {
|
||||||
|
@Serializable
|
||||||
|
data class ResponseDto(
|
||||||
|
val cacheUpdateRequired: Boolean,
|
||||||
|
val cacheHash: String,
|
||||||
|
val lastCacheUpdate: Long,
|
||||||
|
val lastScheduleUpdate: Long,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package ru.n08i40k.polytechnic.next.network.request.schedule
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.android.volley.Response
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import ru.n08i40k.polytechnic.next.network.request.CachedRequest
|
||||||
|
|
||||||
|
class ScheduleGetGroupNames(
|
||||||
|
context: Context,
|
||||||
|
listener: Response.Listener<ResponseDto>,
|
||||||
|
errorListener: Response.ErrorListener? = null
|
||||||
|
) : CachedRequest(
|
||||||
|
context,
|
||||||
|
Method.GET,
|
||||||
|
"schedule/get-group-names",
|
||||||
|
{ listener.onResponse(Json.decodeFromString(it)) },
|
||||||
|
errorListener
|
||||||
|
) {
|
||||||
|
@Serializable
|
||||||
|
data class ResponseDto(
|
||||||
|
val names: ArrayList<String>,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.schedule
|
package ru.n08i40k.polytechnic.next.network.request.schedule
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.volley.Response
|
import com.android.volley.Response
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import ru.n08i40k.polytechnic.next.network.data.AuthorizedRequest
|
import ru.n08i40k.polytechnic.next.network.request.AuthorizedRequest
|
||||||
|
|
||||||
class ScheduleUpdateReq(
|
class ScheduleUpdate(
|
||||||
private val data: ScheduleUpdateReqData,
|
private val data: RequestDto,
|
||||||
context: Context,
|
context: Context,
|
||||||
listener: Response.Listener<ScheduleGetCacheStatusResData>,
|
listener: Response.Listener<ScheduleGetCacheStatus.ResponseDto>,
|
||||||
errorListener: Response.ErrorListener? = null
|
errorListener: Response.ErrorListener? = null
|
||||||
) : AuthorizedRequest(
|
) : AuthorizedRequest(
|
||||||
context,
|
context,
|
||||||
@@ -18,6 +19,9 @@ class ScheduleUpdateReq(
|
|||||||
{ listener.onResponse(Json.decodeFromString(it)) },
|
{ listener.onResponse(Json.decodeFromString(it)) },
|
||||||
errorListener
|
errorListener
|
||||||
) {
|
) {
|
||||||
|
@Serializable
|
||||||
|
data class RequestDto(val mainPage: String)
|
||||||
|
|
||||||
override fun getBody(): ByteArray {
|
override fun getBody(): ByteArray {
|
||||||
return Json.encodeToString(data).toByteArray()
|
return Json.encodeToString(data).toByteArray()
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package ru.n08i40k.polytechnic.next.network.request.scheduleReplacer
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.android.volley.Response
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import ru.n08i40k.polytechnic.next.network.request.AuthorizedRequest
|
||||||
|
|
||||||
|
class ScheduleReplacerClear(
|
||||||
|
context: Context,
|
||||||
|
listener: Response.Listener<ResponseDto>,
|
||||||
|
errorListener: Response.ErrorListener?
|
||||||
|
) : AuthorizedRequest(
|
||||||
|
context,
|
||||||
|
Method.POST,
|
||||||
|
"schedule-replacer/clear",
|
||||||
|
{ listener.onResponse(Json.decodeFromString(it)) },
|
||||||
|
errorListener
|
||||||
|
) {
|
||||||
|
@Serializable
|
||||||
|
data class ResponseDto(
|
||||||
|
val count: Int
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.scheduleReplacer
|
package ru.n08i40k.polytechnic.next.network.request.scheduleReplacer
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.volley.Response
|
import com.android.volley.Response
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import ru.n08i40k.polytechnic.next.network.data.AuthorizedRequest
|
import ru.n08i40k.polytechnic.next.model.ScheduleReplacer
|
||||||
|
import ru.n08i40k.polytechnic.next.network.request.AuthorizedRequest
|
||||||
|
|
||||||
class ScheduleReplacerGetReq(
|
class ScheduleReplacerGet(
|
||||||
context: Context,
|
context: Context,
|
||||||
listener: Response.Listener<ScheduleReplacerGetResData>,
|
listener: Response.Listener<List<ScheduleReplacer>>,
|
||||||
errorListener: Response.ErrorListener?
|
errorListener: Response.ErrorListener?
|
||||||
) : AuthorizedRequest(
|
) : AuthorizedRequest(
|
||||||
context,
|
context,
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
package ru.n08i40k.polytechnic.next.network.data.scheduleReplacer
|
package ru.n08i40k.polytechnic.next.network.request.scheduleReplacer
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.android.volley.Response
|
import com.android.volley.Response
|
||||||
import ru.n08i40k.polytechnic.next.network.AuthorizedMultipartRequest
|
import ru.n08i40k.polytechnic.next.network.AuthorizedMultipartRequest
|
||||||
|
|
||||||
class ScheduleReplacerSetReq(
|
class ScheduleReplacerSet(
|
||||||
context: Context,
|
context: Context,
|
||||||
private val fileName: String,
|
private val fileName: String,
|
||||||
private val fileData: ByteArray,
|
private val fileData: ByteArray,
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package ru.n08i40k.polytechnic.next.service
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import androidx.work.BackoffPolicy
|
||||||
|
import androidx.work.Constraints
|
||||||
|
import androidx.work.NetworkType
|
||||||
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.Worker
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import androidx.work.workDataOf
|
||||||
|
import com.google.firebase.messaging.FirebaseMessagingService
|
||||||
|
import com.google.firebase.messaging.RemoteMessage
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import ru.n08i40k.polytechnic.next.NotificationChannels
|
||||||
|
import ru.n08i40k.polytechnic.next.PolytechnicApplication
|
||||||
|
import ru.n08i40k.polytechnic.next.R
|
||||||
|
import ru.n08i40k.polytechnic.next.data.MyResult
|
||||||
|
import ru.n08i40k.polytechnic.next.settings.settingsDataStore
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
class MyFirebaseMessagingService : FirebaseMessagingService() {
|
||||||
|
override fun onNewToken(token: String) {
|
||||||
|
super.onNewToken(token)
|
||||||
|
|
||||||
|
val constraints = Constraints.Builder()
|
||||||
|
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val request = OneTimeWorkRequestBuilder<SetFcmTokenWorker>()
|
||||||
|
.setConstraints(constraints)
|
||||||
|
.setBackoffCriteria(BackoffPolicy.LINEAR, Duration.ofMinutes(1))
|
||||||
|
.setInputData(workDataOf("TOKEN" to token))
|
||||||
|
.build()
|
||||||
|
|
||||||
|
WorkManager
|
||||||
|
.getInstance(applicationContext)
|
||||||
|
.enqueue(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMessageReceived(message: RemoteMessage) {
|
||||||
|
val type = message.data["type"]
|
||||||
|
|
||||||
|
when (type) {
|
||||||
|
"schedule-update" -> {
|
||||||
|
val notification = NotificationCompat
|
||||||
|
.Builder(applicationContext, NotificationChannels.SCHEDULE_UPDATE)
|
||||||
|
.setSmallIcon(R.drawable.logo)
|
||||||
|
.setContentTitle(getString(R.string.schedule_update_title))
|
||||||
|
.setContentText(
|
||||||
|
getString(
|
||||||
|
if (message.data["replaced"] == "true")
|
||||||
|
R.string.schedule_update_replaced
|
||||||
|
else
|
||||||
|
R.string.schedule_update_default
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
with(NotificationManagerCompat.from(this)) {
|
||||||
|
if (ActivityCompat.checkSelfPermission(
|
||||||
|
this@MyFirebaseMessagingService,
|
||||||
|
Manifest.permission.POST_NOTIFICATIONS
|
||||||
|
) != PackageManager.PERMISSION_GRANTED
|
||||||
|
) {
|
||||||
|
return@with
|
||||||
|
}
|
||||||
|
|
||||||
|
notify(message.data["etag"].hashCode(), notification)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onMessageReceived(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
class SetFcmTokenWorker(context: Context, workerParams: WorkerParameters) :
|
||||||
|
Worker(context, workerParams) {
|
||||||
|
override fun doWork(): Result {
|
||||||
|
val fcmToken = inputData.getString("TOKEN") ?: return Result.failure()
|
||||||
|
|
||||||
|
val accessToken = runBlocking {
|
||||||
|
applicationContext.settingsDataStore.data.map { it.accessToken }.first()
|
||||||
|
}
|
||||||
|
if (accessToken.isEmpty())
|
||||||
|
return Result.retry()
|
||||||
|
|
||||||
|
val setResult = runBlocking {
|
||||||
|
(applicationContext as PolytechnicApplication)
|
||||||
|
.container
|
||||||
|
.profileRepository
|
||||||
|
.setFcmToken(fcmToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (setResult) {
|
||||||
|
is MyResult.Success -> Result.success()
|
||||||
|
is MyResult.Failure -> Result.retry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
package ru.n08i40k.polytechnic.next.ui
|
package ru.n08i40k.polytechnic.next.ui
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
@@ -11,20 +19,95 @@ import androidx.compose.foundation.layout.only
|
|||||||
import androidx.compose.foundation.layout.safeContent
|
import androidx.compose.foundation.layout.safeContent
|
||||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.work.PeriodicWorkRequest
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.Worker
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import ru.n08i40k.polytechnic.next.NotificationChannels.SCHEDULE_UPDATE
|
||||||
|
import ru.n08i40k.polytechnic.next.PolytechnicApplication
|
||||||
|
import ru.n08i40k.polytechnic.next.R
|
||||||
import ru.n08i40k.polytechnic.next.settings.settingsDataStore
|
import ru.n08i40k.polytechnic.next.settings.settingsDataStore
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
@SuppressLint("ObsoleteSdkInt")
|
||||||
|
private fun createNotificationChannel() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val name = getString(R.string.schedule_channel_name)
|
||||||
|
val description = getString(R.string.schedule_channel_description)
|
||||||
|
val importance = NotificationManager.IMPORTANCE_DEFAULT
|
||||||
|
val channel = NotificationChannel(SCHEDULE_UPDATE, name, importance)
|
||||||
|
channel.description = description
|
||||||
|
|
||||||
|
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
notificationManager.createNotificationChannel(channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val requestPermissionLauncher = registerForActivityResult(
|
||||||
|
ActivityResultContracts.RequestPermission(),
|
||||||
|
) {
|
||||||
|
if (it) {
|
||||||
|
createNotificationChannel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hasNotificationPermission(): Boolean {
|
||||||
|
return (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
|
||||||
|
|| ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
|
||||||
|
== PackageManager.PERMISSION_GRANTED)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun askNotificationPermission() {
|
||||||
|
if (!hasNotificationPermission())
|
||||||
|
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||||
|
}
|
||||||
|
|
||||||
|
class CacheUpdateWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||||
|
override fun doWork(): Result {
|
||||||
|
runBlocking {
|
||||||
|
(applicationContext as PolytechnicApplication)
|
||||||
|
.container
|
||||||
|
.scheduleRepository
|
||||||
|
.getGroup()
|
||||||
|
}
|
||||||
|
return Result.success()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun schedulePeriodicRequest() {
|
||||||
|
val workRequest = PeriodicWorkRequest.Builder(
|
||||||
|
CacheUpdateWorker::class.java,
|
||||||
|
15, TimeUnit.MINUTES
|
||||||
|
)
|
||||||
|
.addTag("schedule-update")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val workManager = WorkManager.getInstance(applicationContext)
|
||||||
|
|
||||||
|
workManager.cancelAllWorkByTag("schedule-update")
|
||||||
|
workManager.enqueue(workRequest)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
schedulePeriodicRequest()
|
||||||
|
askNotificationPermission()
|
||||||
|
|
||||||
|
if (hasNotificationPermission())
|
||||||
|
createNotificationChannel()
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
Box(Modifier.windowInsetsPadding(WindowInsets.safeContent.only(WindowInsetsSides.Top))) {
|
Box(Modifier.windowInsetsPadding(WindowInsets.safeContent.only(WindowInsetsSides.Top))) {
|
||||||
PolytechnicApp()
|
PolytechnicApp()
|
||||||
|
|||||||
@@ -59,11 +59,9 @@ import kotlinx.coroutines.runBlocking
|
|||||||
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.model.UserRole.Companion.AcceptableUserRoles
|
import ru.n08i40k.polytechnic.next.model.UserRole.Companion.AcceptableUserRoles
|
||||||
import ru.n08i40k.polytechnic.next.network.data.auth.LoginRequest
|
import ru.n08i40k.polytechnic.next.network.request.auth.AuthLogin
|
||||||
import ru.n08i40k.polytechnic.next.network.data.auth.LoginRequestData
|
import ru.n08i40k.polytechnic.next.network.request.auth.AuthRegister
|
||||||
import ru.n08i40k.polytechnic.next.network.data.auth.RegisterRequest
|
import ru.n08i40k.polytechnic.next.network.request.profile.ProfileMe
|
||||||
import ru.n08i40k.polytechnic.next.network.data.auth.RegisterRequestData
|
|
||||||
import ru.n08i40k.polytechnic.next.network.data.profile.UsersMeRequest
|
|
||||||
import ru.n08i40k.polytechnic.next.settings.settingsDataStore
|
import ru.n08i40k.polytechnic.next.settings.settingsDataStore
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@@ -368,7 +366,7 @@ fun tryLogin(
|
|||||||
|
|
||||||
isLoading = true
|
isLoading = true
|
||||||
|
|
||||||
LoginRequest(LoginRequestData(username, password), context, {
|
AuthLogin(AuthLogin.RequestDto(username, password), context, {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
context.settingsDataStore.updateData { currentSettings ->
|
context.settingsDataStore.updateData { currentSettings ->
|
||||||
currentSettings
|
currentSettings
|
||||||
@@ -379,7 +377,7 @@ fun tryLogin(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UsersMeRequest(context, {
|
ProfileMe(context, {
|
||||||
scope.launch { snackbarHostState.showSnackbar("Cool!") }
|
scope.launch { snackbarHostState.showSnackbar("Cool!") }
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
@@ -437,8 +435,8 @@ fun tryRegister(
|
|||||||
|
|
||||||
isLoading = true
|
isLoading = true
|
||||||
|
|
||||||
RegisterRequest(
|
AuthRegister(
|
||||||
RegisterRequestData(
|
AuthRegister.RequestDto(
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
group,
|
group,
|
||||||
|
|||||||
@@ -35,9 +35,8 @@ import com.android.volley.ClientError
|
|||||||
import ru.n08i40k.polytechnic.next.R
|
import ru.n08i40k.polytechnic.next.R
|
||||||
import ru.n08i40k.polytechnic.next.data.users.impl.FakeProfileRepository
|
import ru.n08i40k.polytechnic.next.data.users.impl.FakeProfileRepository
|
||||||
import ru.n08i40k.polytechnic.next.model.Profile
|
import ru.n08i40k.polytechnic.next.model.Profile
|
||||||
import ru.n08i40k.polytechnic.next.network.data.profile.ChangeGroupRequest
|
import ru.n08i40k.polytechnic.next.network.request.profile.ProfileChangeGroup
|
||||||
import ru.n08i40k.polytechnic.next.network.data.profile.ChangeGroupRequestData
|
import ru.n08i40k.polytechnic.next.network.request.schedule.ScheduleGetGroupNames
|
||||||
import ru.n08i40k.polytechnic.next.network.data.schedule.ScheduleGetGroupNamesReq
|
|
||||||
|
|
||||||
private enum class ChangeGroupError {
|
private enum class ChangeGroupError {
|
||||||
NOT_EXISTS
|
NOT_EXISTS
|
||||||
@@ -49,7 +48,7 @@ private fun tryChangeGroup(
|
|||||||
onError: (ChangeGroupError) -> Unit,
|
onError: (ChangeGroupError) -> Unit,
|
||||||
onSuccess: (String) -> Unit
|
onSuccess: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
ChangeGroupRequest(ChangeGroupRequestData(group), context, {
|
ProfileChangeGroup(ProfileChangeGroup.RequestDto(group), context, {
|
||||||
onSuccess(group)
|
onSuccess(group)
|
||||||
}, {
|
}, {
|
||||||
if (it is ClientError && it.networkResponse.statusCode == 404)
|
if (it is ClientError && it.networkResponse.statusCode == 404)
|
||||||
@@ -65,7 +64,7 @@ private fun getGroups(context: Context): ArrayList<String> {
|
|||||||
val groups = remember { arrayListOf(groupPlaceholder) }
|
val groups = remember { arrayListOf(groupPlaceholder) }
|
||||||
|
|
||||||
LaunchedEffect(groups) {
|
LaunchedEffect(groups) {
|
||||||
ScheduleGetGroupNamesReq(context, {
|
ScheduleGetGroupNames(context, {
|
||||||
groups.clear()
|
groups.clear()
|
||||||
groups.addAll(it.names)
|
groups.addAll(it.names)
|
||||||
}, {
|
}, {
|
||||||
|
|||||||
@@ -26,8 +26,7 @@ import com.android.volley.ClientError
|
|||||||
import ru.n08i40k.polytechnic.next.R
|
import ru.n08i40k.polytechnic.next.R
|
||||||
import ru.n08i40k.polytechnic.next.data.users.impl.FakeProfileRepository
|
import ru.n08i40k.polytechnic.next.data.users.impl.FakeProfileRepository
|
||||||
import ru.n08i40k.polytechnic.next.model.Profile
|
import ru.n08i40k.polytechnic.next.model.Profile
|
||||||
import ru.n08i40k.polytechnic.next.network.data.auth.ChangePasswordRequest
|
import ru.n08i40k.polytechnic.next.network.request.auth.AuthChangePassword
|
||||||
import ru.n08i40k.polytechnic.next.network.data.auth.ChangePasswordRequestData
|
|
||||||
|
|
||||||
private enum class ChangePasswordError {
|
private enum class ChangePasswordError {
|
||||||
INCORRECT_CURRENT_PASSWORD,
|
INCORRECT_CURRENT_PASSWORD,
|
||||||
@@ -41,7 +40,7 @@ private fun tryChangePassword(
|
|||||||
onError: (ChangePasswordError) -> Unit,
|
onError: (ChangePasswordError) -> Unit,
|
||||||
onSuccess: () -> Unit
|
onSuccess: () -> Unit
|
||||||
) {
|
) {
|
||||||
ChangePasswordRequest(ChangePasswordRequestData(oldPassword, newPassword), context, {
|
AuthChangePassword(AuthChangePassword.RequestDto(oldPassword, newPassword), context, {
|
||||||
onSuccess()
|
onSuccess()
|
||||||
}, {
|
}, {
|
||||||
if (it is ClientError && it.networkResponse.statusCode == 409)
|
if (it is ClientError && it.networkResponse.statusCode == 409)
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ import com.android.volley.ClientError
|
|||||||
import ru.n08i40k.polytechnic.next.R
|
import ru.n08i40k.polytechnic.next.R
|
||||||
import ru.n08i40k.polytechnic.next.data.users.impl.FakeProfileRepository
|
import ru.n08i40k.polytechnic.next.data.users.impl.FakeProfileRepository
|
||||||
import ru.n08i40k.polytechnic.next.model.Profile
|
import ru.n08i40k.polytechnic.next.model.Profile
|
||||||
import ru.n08i40k.polytechnic.next.network.data.profile.ChangeUsernameRequest
|
import ru.n08i40k.polytechnic.next.network.request.profile.ProfileChangeUsername
|
||||||
import ru.n08i40k.polytechnic.next.network.data.profile.ChangeUsernameRequestData
|
|
||||||
|
|
||||||
private enum class ChangeUsernameError {
|
private enum class ChangeUsernameError {
|
||||||
INCORRECT_LENGTH,
|
INCORRECT_LENGTH,
|
||||||
@@ -38,7 +37,7 @@ private fun tryChangeUsername(
|
|||||||
onError: (ChangeUsernameError) -> Unit,
|
onError: (ChangeUsernameError) -> Unit,
|
||||||
onSuccess: () -> Unit
|
onSuccess: () -> Unit
|
||||||
) {
|
) {
|
||||||
ChangeUsernameRequest(ChangeUsernameRequestData(username), context, {
|
ProfileChangeUsername(ProfileChangeUsername.RequestDto(username), context, {
|
||||||
onSuccess()
|
onSuccess()
|
||||||
}, {
|
}, {
|
||||||
if (it is ClientError && it.networkResponse.statusCode == 409)
|
if (it is ClientError && it.networkResponse.statusCode == 409)
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import ru.n08i40k.polytechnic.next.R
|
import ru.n08i40k.polytechnic.next.R
|
||||||
import ru.n08i40k.polytechnic.next.data.MockAppContainer
|
import ru.n08i40k.polytechnic.next.data.MockAppContainer
|
||||||
import ru.n08i40k.polytechnic.next.data.schedule.impl.FakeScheduleReplacerRepository
|
import ru.n08i40k.polytechnic.next.data.scheduleReplacer.impl.FakeScheduleReplacerRepository
|
||||||
import ru.n08i40k.polytechnic.next.model.ScheduleReplacer
|
import ru.n08i40k.polytechnic.next.model.ScheduleReplacer
|
||||||
import ru.n08i40k.polytechnic.next.ui.LoadingContent
|
import ru.n08i40k.polytechnic.next.ui.LoadingContent
|
||||||
import ru.n08i40k.polytechnic.next.ui.model.ScheduleReplacerUiState
|
import ru.n08i40k.polytechnic.next.ui.model.ScheduleReplacerUiState
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package ru.n08i40k.polytechnic.next.ui.main.schedule
|
package ru.n08i40k.polytechnic.next.ui.main.schedule
|
||||||
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
@@ -17,12 +15,10 @@ import androidx.compose.material3.CardDefaults
|
|||||||
import androidx.compose.material3.MaterialTheme
|
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.DisposableEffect
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
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.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@@ -30,6 +26,10 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
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 androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
import ru.n08i40k.polytechnic.next.R
|
import ru.n08i40k.polytechnic.next.R
|
||||||
import ru.n08i40k.polytechnic.next.data.schedule.impl.FakeScheduleRepository
|
import ru.n08i40k.polytechnic.next.data.schedule.impl.FakeScheduleRepository
|
||||||
import ru.n08i40k.polytechnic.next.model.Day
|
import ru.n08i40k.polytechnic.next.model.Day
|
||||||
@@ -44,26 +44,39 @@ private fun getCurrentMinutes(): Int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun getMinutes(): Int {
|
private fun getMinutes(): Flow<Int> {
|
||||||
var value by remember { mutableIntStateOf(getCurrentMinutes()) }
|
val value by remember {
|
||||||
|
derivedStateOf {
|
||||||
DisposableEffect(Unit) {
|
flow {
|
||||||
val handler = Handler(Looper.getMainLooper())
|
while (true) {
|
||||||
|
emit(getCurrentMinutes())
|
||||||
val runnable = {
|
delay(5_000)
|
||||||
value = getCurrentMinutes()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.postDelayed(runnable, 60_000)
|
|
||||||
|
|
||||||
onDispose {
|
|
||||||
handler.removeCallbacks(runnable)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun calculateCurrentLessonIdx(lessons: ArrayList<Lesson?>): Int {
|
||||||
|
val currentMinutes by getMinutes().collectAsStateWithLifecycle(0)
|
||||||
|
|
||||||
|
val filteredLessons = lessons
|
||||||
|
.filterNotNull()
|
||||||
|
.filter {
|
||||||
|
it.time != null
|
||||||
|
&& it.time.start >= currentMinutes
|
||||||
|
&& it.time.end <= currentMinutes
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filteredLessons.isEmpty())
|
||||||
|
return -1
|
||||||
|
|
||||||
|
return lessons.indexOf(filteredLessons[0])
|
||||||
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun DayCard(
|
fun DayCard(
|
||||||
@@ -71,9 +84,26 @@ fun DayCard(
|
|||||||
day: Day? = FakeScheduleRepository.exampleGroup.days[0],
|
day: Day? = FakeScheduleRepository.exampleGroup.days[0],
|
||||||
current: Boolean = true
|
current: Boolean = true
|
||||||
) {
|
) {
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
colors = CardDefaults.cardColors(containerColor = if (current) MaterialTheme.colorScheme.surfaceContainerHighest else MaterialTheme.colorScheme.surfaceContainerLowest)
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor =
|
||||||
|
if (current) MaterialTheme.colorScheme.surfaceContainerHighest
|
||||||
|
else MaterialTheme.colorScheme.surfaceContainerLowest
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
if (day == null) {
|
if (day == null) {
|
||||||
Text(
|
Text(
|
||||||
@@ -92,34 +122,16 @@ fun DayCard(
|
|||||||
text = day.name,
|
text = day.name,
|
||||||
)
|
)
|
||||||
|
|
||||||
val currentMinutes = getMinutes()
|
val currentLessonIdx = calculateCurrentLessonIdx(day.lessons)
|
||||||
|
|
||||||
val isCurrentLesson: (lesson: Lesson) -> Boolean = {
|
|
||||||
current
|
|
||||||
&& it.time != null
|
|
||||||
&& currentMinutes >= it.time.start
|
|
||||||
&& currentMinutes <= it.time.end
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(0.5.dp)
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(0.5.dp)
|
||||||
) {
|
) {
|
||||||
if (day.nonNullIndices.isEmpty()) {
|
if (day.nonNullIndices.isEmpty()) {
|
||||||
Text("Can't get schedule!")
|
Text("Can't get schedule!")
|
||||||
} else {
|
return@Column
|
||||||
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,
|
|
||||||
|
|
||||||
)
|
|
||||||
|
|
||||||
for (i in day.nonNullIndices.first()..day.nonNullIndices.last()) {
|
for (i in day.nonNullIndices.first()..day.nonNullIndices.last()) {
|
||||||
val lesson = day.lessons[i]!!
|
val lesson = day.lessons[i]!!
|
||||||
@@ -139,7 +151,8 @@ fun DayCard(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = if (isCurrentLesson(lesson)) lessonBoxModifier.border(
|
modifier =
|
||||||
|
if (i == currentLessonIdx) lessonBoxModifier.border(
|
||||||
border = BorderStroke(
|
border = BorderStroke(
|
||||||
3.5.dp,
|
3.5.dp,
|
||||||
Color(
|
Color(
|
||||||
@@ -149,7 +162,8 @@ fun DayCard(
|
|||||||
1F
|
1F
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
) else lessonBoxModifier
|
)
|
||||||
|
else lessonBoxModifier
|
||||||
) {
|
) {
|
||||||
LessonRow(
|
LessonRow(
|
||||||
day, lesson, cardColors
|
day, lesson, cardColors
|
||||||
@@ -169,10 +183,8 @@ fun DayCard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mutableExpanded.value) LessonExtraInfo(
|
if (mutableExpanded.value)
|
||||||
lesson, mutableExpanded
|
LessonExtraInfo(lesson, mutableExpanded)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,26 +6,63 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
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
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import ru.n08i40k.polytechnic.next.R
|
import ru.n08i40k.polytechnic.next.R
|
||||||
import ru.n08i40k.polytechnic.next.data.MockAppContainer
|
import ru.n08i40k.polytechnic.next.data.MockAppContainer
|
||||||
import ru.n08i40k.polytechnic.next.ui.LoadingContent
|
import ru.n08i40k.polytechnic.next.ui.LoadingContent
|
||||||
import ru.n08i40k.polytechnic.next.ui.model.ScheduleUiState
|
import ru.n08i40k.polytechnic.next.ui.model.ScheduleUiState
|
||||||
import ru.n08i40k.polytechnic.next.ui.model.ScheduleViewModel
|
import ru.n08i40k.polytechnic.next.ui.model.ScheduleViewModel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun rememberUpdatedLifecycleOwner(): LifecycleOwner {
|
||||||
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
|
return remember { lifecycleOwner }
|
||||||
|
}
|
||||||
|
|
||||||
@Preview(showBackground = true, showSystemUi = true)
|
@Preview(showBackground = true, showSystemUi = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun ScheduleScreen(
|
fun ScheduleScreen(
|
||||||
scheduleViewModel: ScheduleViewModel = ScheduleViewModel(MockAppContainer(LocalContext.current)),
|
scheduleViewModel: ScheduleViewModel = ScheduleViewModel(MockAppContainer(LocalContext.current)),
|
||||||
onRefreshSchedule: () -> Unit = {}
|
onRefresh: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
val uiState by scheduleViewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by scheduleViewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
LaunchedEffect(uiState) {
|
||||||
|
delay(120_000)
|
||||||
|
onRefresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
val lifecycleOwner = rememberUpdatedLifecycleOwner()
|
||||||
|
|
||||||
|
DisposableEffect(lifecycleOwner) {
|
||||||
|
val observer = LifecycleEventObserver { _, event ->
|
||||||
|
when (event) {
|
||||||
|
Lifecycle.Event.ON_RESUME -> {
|
||||||
|
onRefresh()
|
||||||
|
}
|
||||||
|
else -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lifecycleOwner.lifecycle.addObserver(observer)
|
||||||
|
|
||||||
|
onDispose {
|
||||||
|
lifecycleOwner.lifecycle.removeObserver(observer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LoadingContent(
|
LoadingContent(
|
||||||
empty = when (uiState) {
|
empty = when (uiState) {
|
||||||
@@ -33,13 +70,14 @@ fun ScheduleScreen(
|
|||||||
is ScheduleUiState.HasSchedule -> false
|
is ScheduleUiState.HasSchedule -> false
|
||||||
},
|
},
|
||||||
loading = uiState.isLoading,
|
loading = uiState.isLoading,
|
||||||
onRefresh = { onRefreshSchedule() },
|
onRefresh = onRefresh,
|
||||||
verticalArrangement = Arrangement.Top
|
verticalArrangement = Arrangement.Top
|
||||||
) {
|
) {
|
||||||
when (uiState) {
|
when (uiState) {
|
||||||
is ScheduleUiState.HasSchedule -> {
|
is ScheduleUiState.HasSchedule -> {
|
||||||
Column {
|
Column {
|
||||||
val hasSchedule = uiState as ScheduleUiState.HasSchedule
|
val hasSchedule = uiState as ScheduleUiState.HasSchedule
|
||||||
|
|
||||||
UpdateInfo(hasSchedule.lastUpdateAt, hasSchedule.updateDates)
|
UpdateInfo(hasSchedule.lastUpdateAt, hasSchedule.updateDates)
|
||||||
DayPager(hasSchedule.group)
|
DayPager(hasSchedule.group)
|
||||||
}
|
}
|
||||||
@@ -47,7 +85,7 @@ fun ScheduleScreen(
|
|||||||
|
|
||||||
is ScheduleUiState.NoSchedule -> {
|
is ScheduleUiState.NoSchedule -> {
|
||||||
if (!uiState.isLoading) {
|
if (!uiState.isLoading) {
|
||||||
TextButton(onClick = onRefreshSchedule, modifier = Modifier.fillMaxSize()) {
|
TextButton(onClick = onRefresh, modifier = Modifier.fillMaxSize()) {
|
||||||
Text(stringResource(R.string.reload), textAlign = TextAlign.Center)
|
Text(stringResource(R.string.reload), textAlign = TextAlign.Center)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,4 +40,9 @@
|
|||||||
<string name="bytes">байт</string>
|
<string name="bytes">байт</string>
|
||||||
<string name="clear_replacers">Удалить всё</string>
|
<string name="clear_replacers">Удалить всё</string>
|
||||||
<string name="set_replacer">Загрузить новое расписание</string>
|
<string name="set_replacer">Загрузить новое расписание</string>
|
||||||
|
<string name="schedule_channel_name">Обновления расписания</string>
|
||||||
|
<string name="schedule_channel_description">Информирует об обновлении расписания</string>
|
||||||
|
<string name="schedule_update_title">Расписание обновлено.</string>
|
||||||
|
<string name="schedule_update_replaced">Расписание было обновлено Администратором.</string>
|
||||||
|
<string name="schedule_update_default">Расписание было обновлено на сайте политехникума.</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -40,4 +40,9 @@
|
|||||||
<string name="bytes">bytes</string>
|
<string name="bytes">bytes</string>
|
||||||
<string name="clear_replacers">Clear</string>
|
<string name="clear_replacers">Clear</string>
|
||||||
<string name="set_replacer">Set new</string>
|
<string name="set_replacer">Set new</string>
|
||||||
|
<string name="schedule_channel_name">Schedule update</string>
|
||||||
|
<string name="schedule_channel_description">Inform when schedule has been updated</string>
|
||||||
|
<string name="schedule_update_title">Schedule has been updated.</string>
|
||||||
|
<string name="schedule_update_replaced">Schedule was updated by Administrator.</string>
|
||||||
|
<string name="schedule_update_default">Schedule was updated on polytechnic website.</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
[versions]
|
[versions]
|
||||||
accompanistSwiperefresh = "0.36.0"
|
accompanistSwiperefresh = "0.36.0"
|
||||||
agp = "8.6.1"
|
agp = "8.6.1"
|
||||||
firebaseBom = "33.3.0"
|
firebaseBom = "33.4.0"
|
||||||
hiltAndroid = "2.52"
|
hiltAndroid = "2.52"
|
||||||
hiltAndroidCompiler = "2.52"
|
hiltAndroidCompiler = "2.52"
|
||||||
hiltNavigationCompose = "1.2.0"
|
hiltNavigationCompose = "1.2.0"
|
||||||
@@ -13,18 +13,22 @@ espressoCore = "3.6.1"
|
|||||||
kotlinxSerializationJson = "1.7.3"
|
kotlinxSerializationJson = "1.7.3"
|
||||||
lifecycleRuntimeKtx = "2.8.6"
|
lifecycleRuntimeKtx = "2.8.6"
|
||||||
activityCompose = "1.9.2"
|
activityCompose = "1.9.2"
|
||||||
composeBom = "2024.09.02"
|
composeBom = "2024.09.03"
|
||||||
protobufLite = "3.0.1"
|
protobufLite = "3.0.1"
|
||||||
volley = "1.2.1"
|
volley = "1.2.1"
|
||||||
datastore = "1.1.1"
|
datastore = "1.1.1"
|
||||||
navigationCompose = "2.8.1"
|
navigationCompose = "2.8.2"
|
||||||
firebaseCrashlytics = "19.1.0"
|
firebaseCrashlytics = "19.2.0"
|
||||||
googleFirebaseCrashlytics = "3.0.2"
|
googleFirebaseCrashlytics = "3.0.2"
|
||||||
|
firebaseMessaging = "24.0.2"
|
||||||
|
workRuntime = "2.9.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
accompanist-swiperefresh = { module = "com.google.accompanist:accompanist-swiperefresh", version.ref = "accompanistSwiperefresh" }
|
accompanist-swiperefresh = { module = "com.google.accompanist:accompanist-swiperefresh", version.ref = "accompanistSwiperefresh" }
|
||||||
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-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
|
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
|
||||||
|
androidx-work-runtime = { module = "androidx.work:work-runtime", version.ref = "workRuntime" }
|
||||||
|
androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntime" }
|
||||||
firebase-analytics = { module = "com.google.firebase:firebase-analytics" }
|
firebase-analytics = { module = "com.google.firebase:firebase-analytics" }
|
||||||
firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" }
|
firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" }
|
||||||
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroidCompiler" }
|
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroidCompiler" }
|
||||||
@@ -40,7 +44,7 @@ androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
|||||||
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||||
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||||
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest", version = "1.7.2" }
|
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest", version = "1.7.3" }
|
||||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
|
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
|
||||||
@@ -48,6 +52,7 @@ protobuf-lite = { module = "com.google.protobuf:protobuf-lite", version.ref = "p
|
|||||||
volley = { group = "com.android.volley", name = "volley", version.ref = "volley" }
|
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-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics", version.ref = "firebaseCrashlytics" }
|
firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics", version.ref = "firebaseCrashlytics" }
|
||||||
|
firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging", version.ref = "firebaseMessaging" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|||||||
Reference in New Issue
Block a user