From 4593a67c28d690714831504cf7bcf1ade7b6c219 Mon Sep 17 00:00:00 2001 From: N08I40K Date: Fri, 31 Jan 2025 22:25:15 +0400 Subject: [PATCH] 3.1.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Возвращено и исправлено отображение текущих пар в уведомлении. Исправлен баг при котором происходил выхода из аккаунта. Исправлен баг с неправильным отображением "сегодня", "завтра" и т.п. --- app/build.gradle.kts | 6 +- app/src/main/AndroidManifest.xml | 11 +- .../n08i40k/polytechnic/next/Application.kt | 67 ++++-- .../n08i40k/polytechnic/next/MainActivity.kt | 37 ++- .../next/app/NotificationChannels.kt | 1 + .../polytechnic/next/model/GroupOrTeacher.kt | 8 - .../n08i40k/polytechnic/next/model/Lesson.kt | 26 +- .../next/network/request/AuthorizedRequest.kt | 29 +-- .../next/network/request/CachedRequest.kt | 1 - .../next/service/DayViewService.kt | 223 ++++++++++++++++++ .../polytechnic/next/service/FCMService.kt | 41 ++-- .../next/ui/model/ProfileViewModel.kt | 2 - .../next/ui/widgets/schedule/DayCard.kt | 2 +- app/src/main/res/values-ru/strings.xml | 11 + app/src/main/res/values/strings.xml | 11 + .../main/res/xml/remote_config_defaults.xml | 8 +- gradle.properties | 21 +- gradle/libs.versions.toml | 10 +- 18 files changed, 357 insertions(+), 158 deletions(-) create mode 100644 app/src/main/java/ru/n08i40k/polytechnic/next/service/DayViewService.kt 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"