diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 1b976a0..04e5d2d 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -46,8 +46,8 @@ android {
applicationId = "ru.n08i40k.polytechnic.next"
minSdk = 26
targetSdk = 35
- versionCode = 25
- versionName = "3.0.1"
+ versionCode = 27
+ versionName = "3.1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -55,6 +55,8 @@ android {
buildTypes {
release {
isMinifyEnabled = true
+ isShrinkResources = true
+
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 457e895..067849d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -13,11 +13,11 @@
+
+
+
+
{
- override fun onComplete(token: Task) {
- if (!token.isSuccessful)
- return
-
- UpdateFCMTokenWorker.schedule(applicationContext, token.result!!)
+ private fun setupFirebase() {
+ fun scheduleUpdateLinkWorker() {
+ container.remoteConfig.activate().addOnCompleteListener {
+ UpdateLinkWorker.schedule(this@Application)
}
- })
- }
+ }
- override fun onCreate() {
- super.onCreate()
+ fun fixupToken() {
+ if (runBlocking { settings.data.map { it.fcmToken }.first() }.isNotEmpty())
+ return
- VKID.init(this)
+ FirebaseMessaging.getInstance().token.addOnCompleteListener(object :
+ OnCompleteListener {
+ override fun onComplete(token: Task) {
+ if (!token.isSuccessful)
+ return
+
+ UpdateFCMTokenWorker.schedule(applicationContext, token.result!!)
+ }
+ })
+ }
val remoteConfig = container.remoteConfig
- remoteConfig.setConfigSettingsAsync(remoteConfigSettings {
- minimumFetchIntervalInSeconds = 3600
- })
+ remoteConfig.setConfigSettingsAsync(
+ remoteConfigSettings { minimumFetchIntervalInSeconds = 3600 }
+ )
remoteConfig.setDefaultsAsync(R.xml.remote_config_defaults)
remoteConfig.addOnConfigUpdateListener(object : ConfigUpdateListener {
@@ -86,4 +93,12 @@ class Application : Application() {
scheduleUpdateLinkWorker()
fixupToken()
}
+
+ override fun onCreate() {
+ super.onCreate()
+
+ VKID.init(this)
+
+ setupFirebase()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/MainActivity.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/MainActivity.kt
index 02354fd..9bf7ba2 100644
--- a/app/src/main/java/ru/n08i40k/polytechnic/next/MainActivity.kt
+++ b/app/src/main/java/ru/n08i40k/polytechnic/next/MainActivity.kt
@@ -3,8 +3,6 @@ package ru.n08i40k.polytechnic.next
import android.Manifest
import android.app.NotificationChannel
import android.app.NotificationManager
-import android.content.pm.PackageManager
-import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
@@ -19,7 +17,6 @@ import androidx.compose.foundation.layout.safeContent
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
-import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.first
@@ -28,6 +25,7 @@ import ru.n08i40k.polytechnic.next.app.NotificationChannels
import ru.n08i40k.polytechnic.next.settings.settings
import ru.n08i40k.polytechnic.next.ui.PolytechnicApp
import ru.n08i40k.polytechnic.next.ui.theme.AppTheme
+import ru.n08i40k.polytechnic.next.utils.app
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@@ -44,9 +42,6 @@ class MainActivity : ComponentActivity() {
}
private fun createNotificationChannels() {
- if (!hasNotificationPermission())
- return
-
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
createNotificationChannel(
@@ -63,37 +58,33 @@ class MainActivity : ComponentActivity() {
NotificationChannels.APP_UPDATE
)
-// createNotificationChannel(
-// notificationManager,
-// getString(R.string.lesson_view_channel_name),
-// getString(R.string.lesson_view_channel_description),
-// NotificationChannels.LESSON_VIEW
-// )
+ createNotificationChannel(
+ notificationManager,
+ getString(R.string.day_view_channel_name),
+ getString(R.string.day_view_channel_description),
+ NotificationChannels.DAY_VIEW
+ )
}
- private val requestPermissionLauncher =
+ private val notificationRPL =
registerForActivityResult(ActivityResultContracts.RequestPermission()) {
if (it) createNotificationChannels()
}
- private fun askNotificationPermission() {
- if (hasNotificationPermission())
+ private fun setupNotifications() {
+ if (app.hasNotificationPermission) {
+ createNotificationChannels()
return
+ }
- requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
+ notificationRPL.launch(Manifest.permission.POST_NOTIFICATIONS)
}
- private fun hasNotificationPermission(): Boolean =
- (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
- || ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
- == PackageManager.PERMISSION_GRANTED)
-
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
- askNotificationPermission()
- createNotificationChannels()
+ setupNotifications()
lifecycleScope.launch {
settings.data.first()
diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/app/NotificationChannels.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/app/NotificationChannels.kt
index b221a47..43f3850 100644
--- a/app/src/main/java/ru/n08i40k/polytechnic/next/app/NotificationChannels.kt
+++ b/app/src/main/java/ru/n08i40k/polytechnic/next/app/NotificationChannels.kt
@@ -3,4 +3,5 @@ package ru.n08i40k.polytechnic.next.app
object NotificationChannels {
const val SCHEDULE_UPDATE = "schedule-update"
const val APP_UPDATE = "app-update"
+ const val DAY_VIEW = "day-view"
}
\ No newline at end of file
diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/model/GroupOrTeacher.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/model/GroupOrTeacher.kt
index 41f154a..5877c88 100644
--- a/app/src/main/java/ru/n08i40k/polytechnic/next/model/GroupOrTeacher.kt
+++ b/app/src/main/java/ru/n08i40k/polytechnic/next/model/GroupOrTeacher.kt
@@ -31,12 +31,4 @@ data class GroupOrTeacher(
get() {
return days.getOrNull(currentIdx ?: return null)
}
-
- // TODO: вернуть
- @Suppress("unused")
- val currentKV: Pair?
- get() {
- val idx = currentIdx ?: return null
- return Pair(idx, days[idx])
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/model/Lesson.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/model/Lesson.kt
index 323b531..2d091da 100644
--- a/app/src/main/java/ru/n08i40k/polytechnic/next/model/Lesson.kt
+++ b/app/src/main/java/ru/n08i40k/polytechnic/next/model/Lesson.kt
@@ -5,7 +5,6 @@ import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import ru.n08i40k.polytechnic.next.R
-import ru.n08i40k.polytechnic.next.utils.dayMinutes
import ru.n08i40k.polytechnic.next.utils.limit
@Parcelize
@@ -18,25 +17,22 @@ data class Lesson(
val group: String? = null,
val subGroups: List
) : Parcelable {
- // TODO: вернуть
- @Suppress("unused")
- val duration get() = time.end.dayMinutes - time.start.dayMinutes
-
- @Suppress("unused")
- fun getNameAndCabinetsShort(context: Context): String {
+ fun getShortName(context: Context): String {
val name =
- if (type == LessonType.BREAK) context.getString(
- if (group == null)
- R.string.student_break
- else
- R.string.teacher_break
- )
- else this.name
+ if (type == LessonType.BREAK)
+ context.getString(
+ if (group == null)
+ R.string.student_break
+ else
+ R.string.teacher_break
+ )
+ else
+ this.name
val shortName = name!! limit 15
val cabinetList = subGroups.map { it.cabinet }
- if (cabinetList.isEmpty())
+ if (cabinetList.isEmpty() || (cabinetList.size == 1 && cabinetList[0].isEmpty()))
return shortName
if (cabinetList.size == 1 && cabinetList[0] == "с/з")
diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/network/request/AuthorizedRequest.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/network/request/AuthorizedRequest.kt
index 9e086d2..9b4ed9f 100644
--- a/app/src/main/java/ru/n08i40k/polytechnic/next/network/request/AuthorizedRequest.kt
+++ b/app/src/main/java/ru/n08i40k/polytechnic/next/network/request/AuthorizedRequest.kt
@@ -3,7 +3,6 @@ package ru.n08i40k.polytechnic.next.network.request
import com.android.volley.AuthFailureError
import com.android.volley.Response
import com.android.volley.VolleyError
-import jakarta.inject.Singleton
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
@@ -22,30 +21,12 @@ open class AuthorizedRequest(
method,
url,
listener,
- @Singleton
object : Response.ErrorListener {
override fun onErrorResponse(error: VolleyError?) {
- val context = appContainer.context
-
- if (!canBeUnauthorized && error is AuthFailureError) {
- runBlocking {
- context.settings.updateData { currentSettings ->
- currentSettings
- .toBuilder()
- .clear()
- .build()
- }
- }
-
- // TODO: если не авторизован
-// if (context.profileViewModel != null)
-// context.profileViewModel!!.onUnauthorized()
- }
-
- runBlocking { appContainer.profileRepository.signOut() }
+ if (!canBeUnauthorized && error is AuthFailureError)
+ runBlocking { appContainer.profileRepository.signOut() }
errorListener?.onErrorResponse(error)
-
}
}) {
@@ -58,15 +39,11 @@ open class AuthorizedRequest(
.first()
}
- // TODO: если не авторизован
-// if (accessToken.isEmpty() && context.profileViewModel != null)
-// context.profileViewModel!!.onUnauthorized()
-
val headers = super.getHeaders()
headers["Authorization"] = "Bearer $accessToken"
return headers
}
- val appContext get() = appContainer.context
+ protected val appContext get() = appContainer.context
}
\ No newline at end of file
diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/network/request/CachedRequest.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/network/request/CachedRequest.kt
index 4f234de..108062a 100644
--- a/app/src/main/java/ru/n08i40k/polytechnic/next/network/request/CachedRequest.kt
+++ b/app/src/main/java/ru/n08i40k/polytechnic/next/network/request/CachedRequest.kt
@@ -87,7 +87,6 @@ open class CachedRequest(
}
override fun send(context: Context) {
- // TODO: network cache
val logger = Logger.getLogger("CachedRequest")
val cache = appContainer.networkCacheRepository
diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/service/DayViewService.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/service/DayViewService.kt
new file mode 100644
index 0000000..c8face5
--- /dev/null
+++ b/app/src/main/java/ru/n08i40k/polytechnic/next/service/DayViewService.kt
@@ -0,0 +1,223 @@
+package ru.n08i40k.polytechnic.next.service
+
+import android.app.Notification
+import android.app.NotificationManager
+import android.app.Service
+import android.content.Intent
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import androidx.core.app.NotificationCompat
+import androidx.core.content.ContextCompat.startForegroundService
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.datetime.Clock
+import kotlinx.datetime.LocalDateTime
+import ru.n08i40k.polytechnic.next.Application
+import ru.n08i40k.polytechnic.next.R
+import ru.n08i40k.polytechnic.next.app.NotificationChannels
+import ru.n08i40k.polytechnic.next.app.appContainer
+import ru.n08i40k.polytechnic.next.model.Day
+import ru.n08i40k.polytechnic.next.model.UserRole
+import ru.n08i40k.polytechnic.next.utils.MyResult
+import ru.n08i40k.polytechnic.next.utils.dayMinutes
+import ru.n08i40k.polytechnic.next.utils.fmtAsClock
+import ru.n08i40k.polytechnic.next.utils.now
+import java.util.logging.Logger
+
+class DayViewService : Service() {
+ private val coroutineScope = CoroutineScope(Job() + Dispatchers.Main)
+
+ companion object {
+ private val logger = Logger.getLogger("DayView")
+
+ private const val NOTIFICATION_MAIN_ID = 3141_00
+ private const val NOTIFICATION_END_ID = 3141_59
+
+ private const val UPDATE_INTERVAL_MILLIS = 1_000L
+
+ fun start(app: Application) {
+ if (!app.hasNotificationPermission) {
+ logger.warning("Cannot start service, because app don't have notifications permission!")
+ return
+ }
+
+ val intent = Intent(app, DayViewService::class.java)
+
+ app.stopService(intent)
+ startForegroundService(app, intent)
+ }
+ }
+
+ private lateinit var day: Day
+ private val handler = Handler(Looper.getMainLooper())
+
+ private fun onLessonsEnd() {
+ notificationManager.notify(
+ NOTIFICATION_END_ID,
+ NotificationCompat
+ .Builder(this, NotificationChannels.DAY_VIEW)
+ .setSmallIcon(R.drawable.schedule)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setContentTitle(getString(R.string.day_view_end_title))
+ .setContentText(getString(R.string.day_view_end_description))
+ .build()
+ )
+
+ handler.removeCallbacks(runnable)
+
+ stopSelf()
+ }
+
+ private val runnable = object : Runnable {
+ override fun run() {
+ val (currentIndex, current) = day.currentKV ?: (null to null)
+ val (nextIndex, distanceToNext) = day.distanceToNext(currentIndex) ?: (null to null)
+ if (current == null && nextIndex == null) {
+ onLessonsEnd()
+ return
+ }
+
+ handler.postDelayed(this, UPDATE_INTERVAL_MILLIS)
+
+ val context = this@DayViewService
+
+ val next = nextIndex?.let { day.lessons[nextIndex] }
+ val nextName = next?.getShortName(context) ?: getString(R.string.day_view_lessons_end)
+
+ val nowMinutes = LocalDateTime.now().dayMinutes
+ val eventMinutes = (current?.time?.end ?: next!!.time.start).dayMinutes
+
+ // Если следующая пара - первая.
+ // Пока что вариантов, когда текущая пара null, а следующая нет я не видел.
+ if (current == null) {
+ notificationManager.notify(
+ NOTIFICATION_MAIN_ID,
+ createNotification(
+ getString(
+ R.string.day_view_wait_for_begin_title,
+ (eventMinutes - nowMinutes) / 60,
+ (eventMinutes - nowMinutes) % 60
+ ),
+ getString(
+ R.string.day_view_going_description,
+ getString(R.string.day_view_not_started),
+ eventMinutes.fmtAsClock(),
+ nextName,
+ ),
+ )
+ )
+
+ return
+ }
+
+ notificationManager.notify(
+ NOTIFICATION_MAIN_ID,
+ createNotification(
+ getString(
+ R.string.day_view_going_title,
+ (eventMinutes - nowMinutes) / 60,
+ (eventMinutes - nowMinutes) % 60
+ ),
+ getString(
+ R.string.day_view_going_description,
+ current.getShortName(context),
+ eventMinutes.fmtAsClock(),
+ nextName,
+ ),
+ )
+ )
+ }
+ }
+
+ private lateinit var notificationManager: NotificationManager
+
+ private fun createNotification(
+ title: String,
+ description: String
+ ): Notification {
+ return NotificationCompat
+ .Builder(this, NotificationChannels.DAY_VIEW)
+ .setSmallIcon(R.drawable.schedule)
+ .setContentTitle(title)
+ .setContentText(description)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setOngoing(true)
+ .setSilent(true)
+ .build()
+ }
+
+ private suspend fun loadSchedule(): Boolean {
+ val profileRepository = appContainer.profileRepository
+ val scheduleRepository = appContainer.scheduleRepository
+
+ val profile = when (val result = profileRepository.getProfile()) {
+ is MyResult.Success -> result.data
+ else -> {
+ logger.warning("Cannot start service, because get profile request failed!")
+ return false
+ }
+ }
+
+ val schedule = when (
+ val result = when (profile.role) {
+ UserRole.TEACHER -> {
+ // TODO: implement schedule breaks for teachers on server-side.
+ return false
+ }
+
+ else -> scheduleRepository.getGroup()
+ }
+ ) {
+ is MyResult.Success -> result.data
+ else -> {
+ logger.warning("Cannot start service, because get schedule request failed!")
+ return false
+ }
+ }
+
+ val currentDay = schedule.current
+
+ if (currentDay == null || currentDay.lessons.isEmpty()) {
+ logger.warning("Cannot start service, because no lessons today!")
+ return false
+ }
+
+ if (Clock.System.now() > currentDay.lessons.first().time.start && currentDay.current == null) {
+ logger.warning("Cannot start service, because it started after lessons end!")
+ return false
+ }
+
+ this.day = currentDay
+ return true
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
+
+ val notification = createNotification(
+ getString(R.string.day_view_title),
+ getString(R.string.day_view_description)
+ )
+ startForeground(NOTIFICATION_MAIN_ID, notification)
+
+ coroutineScope
+ .launch {
+ if (!loadSchedule()) {
+ stopSelf()
+ return@launch
+ }
+
+ this@DayViewService.handler.removeCallbacks(runnable)
+ this@DayViewService.runnable.run()
+ }
+
+ return START_STICKY
+ }
+
+ override fun onBind(p0: Intent?): IBinder? {
+ return null
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/service/FCMService.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/service/FCMService.kt
index 63aea01..73e82ce 100644
--- a/app/src/main/java/ru/n08i40k/polytechnic/next/service/FCMService.kt
+++ b/app/src/main/java/ru/n08i40k/polytechnic/next/service/FCMService.kt
@@ -11,19 +11,21 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
import ru.n08i40k.polytechnic.next.R
import ru.n08i40k.polytechnic.next.app.NotificationChannels
+import ru.n08i40k.polytechnic.next.utils.app
import ru.n08i40k.polytechnic.next.worker.UpdateFCMTokenWorker
+private interface MessageHandler {
+ fun execute(service: FCMService)
+}
+
private data class ScheduleUpdateData(
val type: String,
val replaced: Boolean,
val etag: String
-) {
+) : MessageHandler {
constructor(message: RemoteMessage) : this(
type = message.data["type"]
?: throw IllegalArgumentException("Type is missing in RemoteMessage"),
@@ -33,7 +35,7 @@ private data class ScheduleUpdateData(
?: throw IllegalArgumentException("Etag is missing in RemoteMessage")
)
- fun handleMessage(service: FCMService) {
+ override fun execute(service: FCMService) {
service.sendNotification(
NotificationChannels.SCHEDULE_UPDATE,
R.drawable.schedule,
@@ -51,20 +53,14 @@ private data class ScheduleUpdateData(
private data class LessonsStartData(
val type: String
-) {
+) : MessageHandler {
constructor(message: RemoteMessage) : this(
type = message.data["type"]
?: throw IllegalArgumentException("Type is missing in RemoteMessage")
)
- // TODO: вернуть
- @Suppress("unused")
- fun handleMessage(service: FCMService) {
- // Uncomment and implement if needed
- // service.scope.launch {
- // CurrentLessonViewService
- // .startService(service.applicationContext as PolytechnicApplication)
- // }
+ override fun execute(service: FCMService) {
+ DayViewService.start(service.app)
}
}
@@ -72,7 +68,7 @@ private data class AppUpdateData(
val type: String,
val version: String,
val downloadLink: String
-) {
+) : MessageHandler {
constructor(message: RemoteMessage) : this(
type = message.data["type"]
?: throw IllegalArgumentException("Type is missing in RemoteMessage"),
@@ -82,7 +78,7 @@ private data class AppUpdateData(
?: throw IllegalArgumentException("DownloadLink is missing in RemoteMessage")
)
- fun handleMessage(service: FCMService) {
+ override fun execute(service: FCMService) {
service.sendNotification(
NotificationChannels.APP_UPDATE,
R.drawable.download,
@@ -95,10 +91,6 @@ private data class AppUpdateData(
}
class FCMService : FirebaseMessagingService() {
- // TODO: вернуть
- @Suppress("unused")
- private val scope = CoroutineScope(Job() + Dispatchers.Main)
-
override fun onNewToken(token: String) {
super.onNewToken(token)
@@ -149,10 +141,11 @@ class FCMService : FirebaseMessagingService() {
val type = message.data["type"]
when (type) {
- "schedule-update" -> ScheduleUpdateData(message).handleMessage(this)
- "lessons-start" -> LessonsStartData(message).handleMessage(this)
- "app-update" -> AppUpdateData(message).handleMessage(this)
- }
+ "schedule-update" -> ScheduleUpdateData(message)
+ "lessons-start" -> LessonsStartData(message)
+ "app-update" -> AppUpdateData(message)
+ else -> null
+ }?.execute(this)
super.onMessageReceived(message)
}
diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/model/ProfileViewModel.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/model/ProfileViewModel.kt
index e8c36a7..398eb41 100644
--- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/model/ProfileViewModel.kt
+++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/model/ProfileViewModel.kt
@@ -54,8 +54,6 @@ class ProfileViewModel @Inject constructor(
refresh()
}
- // TODO: сделать хук на unauthorized и сделать так что бы waiter удалялся, если сход контекст
-
fun refresh(): SingleHook {
val singleHook = SingleHook()
diff --git a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/widgets/schedule/DayCard.kt b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/widgets/schedule/DayCard.kt
index 608c59a..a34803c 100644
--- a/app/src/main/java/ru/n08i40k/polytechnic/next/ui/widgets/schedule/DayCard.kt
+++ b/app/src/main/java/ru/n08i40k/polytechnic/next/ui/widgets/schedule/DayCard.kt
@@ -82,7 +82,7 @@ fun DayCard(
modifier: Modifier = Modifier,
day: Day = MockScheduleRepository.exampleTeacher.days[0]
) {
- val offset = remember { getDayOffset(day) }
+ val offset = remember(day) { getDayOffset(day) }
val defaultCardColors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.secondaryContainer,
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 4358c2d..6871a69 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -89,4 +89,15 @@
Уведомления об обновлении расписания
Обновления приложения
Уведомления о выходе новой версии этого приложения
+ Загрузка расписания…
+ Это уведомление обновится в течение нескольких секунд!
+ Пары ещё не начались
+ До конца %1$d ч. %2$d мин.
+ До начала пар %1$d ч. %2$d мин.
+ Конец пар
+ %1$s\n| Далее в %2$s - %3$s
+ Пары закончились!
+ Ура, можно идти домой! Наверное :(
+ Текущая пара
+ Отображает текущую пару или перемену в уведомлении
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a4e34a5..130a269 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -89,4 +89,15 @@
Inform when schedule has been updated
Application update
Inform about a new version of this app has been released
+ Loading schedule…
+ This notification will be updated in several seconds!
+ %1$d h. %2$d min. before lessons start
+ Lessons end
+ %1$s\n| After in %2$s - %3$s
+ Lessons haven\'t started yet
+ To end %1$d h. %2$d min.
+ Lessons finished!
+ ya ne budu eto perevidit\'
+ Current lesson
+ View the current lesson and breaks in notification
\ No newline at end of file
diff --git a/app/src/main/res/xml/remote_config_defaults.xml b/app/src/main/res/xml/remote_config_defaults.xml
index 74c6f74..d99b1ba 100644
--- a/app/src/main/res/xml/remote_config_defaults.xml
+++ b/app/src/main/res/xml/remote_config_defaults.xml
@@ -2,7 +2,7 @@
serverVersion
- 2.2.2
+ 3.0.1
linkUpdateDelay
@@ -10,7 +10,7 @@
minVersion
- 2.0.1
+ 3.0.0
telegramLink
@@ -22,10 +22,10 @@
downloadLink
- https://t.me/polytechnic_next/68
+ https://t.me/polytechnic_next/99
currVersion
- 2.2.1
+ 3.1.0
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 20e2a01..c43fb91 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,23 +1,4 @@
-# Project-wide Gradle settings.
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. For more details, visit
-# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
-# org.gradle.parallel=true
-# AndroidX package structure to make it clearer which packages are bundled with the
-# Android operating system, and which are packaged with your app's APK
-# https://developer.android.com/topic/libraries/support-library/androidx-rn
+org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
android.useAndroidX=true
-# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
-# Enables namespacing of each library's R class so that its R class includes only the
-# resources declared in the library itself and none from the library's dependencies,
-# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index c1c843c..1708352 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -8,17 +8,17 @@ junitVersion = "1.2.1"
espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.10.0"
-composeBom = "2025.01.00"
+composeBom = "2025.01.01"
accompanistSwiperefresh = "0.36.0"
firebaseBom = "33.8.0"
-hiltAndroid = "2.53.1"
-hiltAndroidCompiler = "2.53.1"
+hiltAndroid = "2.55"
+hiltAndroidCompiler = "2.55"
hiltNavigationCompose = "1.2.0"
-kotlinxSerializationJson = "1.7.3"
+kotlinxSerializationJson = "1.8.0"
protobufLite = "3.0.1"
volley = "1.2.1"
datastore = "1.1.2"
-navigationCompose = "2.8.5"
+navigationCompose = "2.8.6"
googleFirebaseCrashlytics = "3.0.2"
workRuntime = "2.10.0"
vkid = "2.2.2"