mirror of
https://github.com/n08i40k/polytechnic-android.git
synced 2025-12-06 17:57:46 +03:00
1.7.1
Фикс невозможности запуска службы переднего плана из LinkUpdateWorker. Небольшие визуальные изменения.
This commit is contained in:
@@ -2,11 +2,20 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<!-- bruh -->
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<!-- For posting notifications from FCM and CLV services -->
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<!-- For CLV service able to work -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||
|
||||
<!-- For schedule CLV service-->
|
||||
<!-- <uses-permission android:name="android.permission.USE_EXACT_ALARM" />-->
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
|
||||
<application
|
||||
android:name=".PolytechnicApplication"
|
||||
android:allowBackup="true"
|
||||
@@ -18,6 +27,16 @@
|
||||
android:theme="@style/Theme.PolytechnicNext"
|
||||
tools:targetApi="35">
|
||||
|
||||
<receiver
|
||||
android:name=".receiver.BootCompletedBroadcastReceiver"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name=".receiver.AlarmReceiver" />
|
||||
|
||||
<service
|
||||
android:name=".service.MyFirebaseMessagingService"
|
||||
android:exported="false">
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package ru.n08i40k.polytechnic.next
|
||||
|
||||
object IntentRequestCodes {
|
||||
const val ALARM_CLV = 1337
|
||||
}
|
||||
@@ -1,13 +1,20 @@
|
||||
package ru.n08i40k.polytechnic.next
|
||||
|
||||
import android.Manifest
|
||||
import android.app.AlarmManager
|
||||
import android.app.Application
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import androidx.core.content.ContextCompat
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import ru.n08i40k.polytechnic.next.data.AppContainer
|
||||
import ru.n08i40k.polytechnic.next.model.Group
|
||||
import ru.n08i40k.polytechnic.next.receiver.AlarmReceiver
|
||||
import ru.n08i40k.polytechnic.next.utils.or
|
||||
import java.util.Calendar
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltAndroidApp
|
||||
@@ -27,4 +34,84 @@ class PolytechnicApplication : Application() {
|
||||
|| ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
|
||||
== PackageManager.PERMISSION_GRANTED)
|
||||
}
|
||||
|
||||
private fun getDate(group: Group): Calendar? {
|
||||
val javaCalendar = Calendar.getInstance()
|
||||
val currentMinutes = javaCalendar.get(Calendar.HOUR_OF_DAY) * 60 +
|
||||
javaCalendar.get(Calendar.MINUTE)
|
||||
var startDayIdx = javaCalendar.get(Calendar.DAY_OF_WEEK) - 2
|
||||
|
||||
println("Current day is $startDayIdx")
|
||||
|
||||
val currentDay = group.days[startDayIdx]
|
||||
if (currentDay != null) {
|
||||
val firstLesson = currentDay.first
|
||||
|
||||
if (firstLesson == null || firstLesson.time.start < currentMinutes) {
|
||||
println("Current day already started or ended!")
|
||||
++startDayIdx
|
||||
}
|
||||
}
|
||||
|
||||
for (dayIdx in startDayIdx..5) {
|
||||
println("Trying $dayIdx day...")
|
||||
val day = group.days[dayIdx] ?: continue
|
||||
println("Day isn't null")
|
||||
val firstLesson = day.first ?: continue
|
||||
println("Day isn't empty")
|
||||
|
||||
val executeMinutes = (firstLesson.time.start - 15).coerceAtLeast(0)
|
||||
|
||||
println("Schedule minutes at $executeMinutes")
|
||||
|
||||
return Calendar.getInstance().apply {
|
||||
set(Calendar.DAY_OF_WEEK, dayIdx + 2) // sunday is first + index from 0
|
||||
set(Calendar.HOUR_OF_DAY, executeMinutes / 60)
|
||||
set(Calendar.MINUTE, executeMinutes % 60)
|
||||
set(Calendar.SECOND, 0)
|
||||
// set(Calendar.MINUTE, get(Calendar.MINUTE) + 1)
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun scheduleClvService(group: Group) {
|
||||
// -1 = вс
|
||||
// 0 = пн
|
||||
// 1 = вт
|
||||
// 2 = ср
|
||||
// 3 = чт
|
||||
// 4 = пт
|
||||
// 5 = сб
|
||||
|
||||
println("Getting date...")
|
||||
|
||||
val date = getDate(group) ?: return
|
||||
|
||||
println("Alarm on this week!")
|
||||
|
||||
val alarmManager = applicationContext
|
||||
.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
|
||||
|
||||
val pendingIntent =
|
||||
Intent(applicationContext, AlarmReceiver::class.java).let {
|
||||
PendingIntent.getBroadcast(
|
||||
applicationContext,
|
||||
IntentRequestCodes.ALARM_CLV,
|
||||
it,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
}
|
||||
|
||||
if (alarmManager != null)
|
||||
println("Alarm manager isn't null.")
|
||||
|
||||
alarmManager?.cancel(pendingIntent)
|
||||
alarmManager?.set(
|
||||
AlarmManager.RTC_WAKEUP,
|
||||
date.timeInMillis,
|
||||
pendingIntent
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,11 @@ package ru.n08i40k.polytechnic.next.model
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import kotlinx.serialization.Serializable
|
||||
import ru.n08i40k.polytechnic.next.utils.getDayMinutes
|
||||
import java.util.Calendar
|
||||
|
||||
@Parcelize
|
||||
@Suppress("unused")
|
||||
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
@Serializable
|
||||
class Day(
|
||||
val name: String,
|
||||
@@ -15,7 +16,7 @@ class Day(
|
||||
val customIndices: ArrayList<Int>,
|
||||
val lessons: ArrayList<Lesson?>
|
||||
) : Parcelable {
|
||||
fun getDistanceToNextByMinutes(from: Int): Map.Entry<Int, Int>? {
|
||||
fun distanceToNextByMinutes(from: Int): Pair<Int, Int>? {
|
||||
val toIdx = lessons
|
||||
.map { if (it?.time == null) null else it.time.start }
|
||||
.indexOfFirst { if (it == null) false else it > from }
|
||||
@@ -23,53 +24,79 @@ class Day(
|
||||
if (toIdx == -1)
|
||||
return null
|
||||
|
||||
return object : Map.Entry<Int, Int> {
|
||||
override val key: Int
|
||||
get() = toIdx
|
||||
override val value: Int
|
||||
get() = lessons[toIdx]!!.time!!.start - from
|
||||
}
|
||||
return Pair(toIdx, lessons[toIdx]!!.time.start - from)
|
||||
}
|
||||
|
||||
fun getDistanceToNextByIdx(from: Int? = null): Map.Entry<Int, Int>? {
|
||||
fun distanceToNextByIdx(from: Int? = null): Pair<Int, Int>? {
|
||||
val fromLesson = if (from != null) lessons[from] else null
|
||||
|
||||
if (from != null && fromLesson?.time == null)
|
||||
if (from != null && fromLesson == null)
|
||||
throw NullPointerException("Lesson (by given index) and it's time should be non-null!")
|
||||
|
||||
val fromTime =
|
||||
if (from != null)
|
||||
fromLesson!!.time!!.end
|
||||
fromLesson!!.time.end
|
||||
else
|
||||
Calendar.getInstance()
|
||||
.get(Calendar.HOUR_OF_DAY) * 60 + Calendar.getInstance()
|
||||
.get(Calendar.MINUTE)
|
||||
|
||||
return getDistanceToNextByMinutes(fromTime)
|
||||
return distanceToNextByMinutes(fromTime)
|
||||
}
|
||||
|
||||
fun getCurrentLesson(): Map.Entry<Int, Lesson>? {
|
||||
val minutes = Calendar.getInstance()
|
||||
.get(Calendar.HOUR_OF_DAY) * 60 + Calendar.getInstance()
|
||||
.get(Calendar.MINUTE)
|
||||
// current
|
||||
val currentIdx: Int?
|
||||
get() {
|
||||
val minutes = Calendar.getInstance().getDayMinutes()
|
||||
|
||||
for (lessonIdx in 0..<lessons.size) {
|
||||
val lesson = lessons[lessonIdx] ?: continue
|
||||
for (lessonIdx in nonNullIndices) {
|
||||
val lesson = lessons[lessonIdx]!!
|
||||
|
||||
if (lesson.time == null
|
||||
|| minutes < lesson.time.start
|
||||
|| minutes >= lesson.time.end
|
||||
)
|
||||
continue
|
||||
|
||||
return object : Map.Entry<Int, Lesson> {
|
||||
override val key: Int
|
||||
get() = lessonIdx
|
||||
override val value: Lesson
|
||||
get() = lesson
|
||||
if (lesson.time.start <= minutes && minutes < lesson.time.end)
|
||||
return lessonIdx
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
val current: Lesson?
|
||||
get() {
|
||||
return lessons[currentIdx ?: return null]
|
||||
}
|
||||
|
||||
val currentKV: Pair<Int, Lesson>?
|
||||
get() {
|
||||
val idx = currentIdx ?: return null
|
||||
return Pair(idx, lessons[idx]!!)
|
||||
}
|
||||
|
||||
// first
|
||||
val firstIdx: Int?
|
||||
get() = nonNullIndices.getOrNull(0)
|
||||
|
||||
val first: Lesson?
|
||||
get() {
|
||||
return lessons[firstIdx ?: return null]!!
|
||||
}
|
||||
|
||||
val firstKV: Pair<Int, Lesson>?
|
||||
get() {
|
||||
val idx = firstIdx ?: return null
|
||||
return Pair(idx, lessons[idx]!!)
|
||||
}
|
||||
|
||||
// last
|
||||
val lastIdx: Int?
|
||||
get() = nonNullIndices.getOrNull(nonNullIndices.size - 1)
|
||||
|
||||
val last: Lesson?
|
||||
get() {
|
||||
return lessons[lastIdx ?: return null]!!
|
||||
}
|
||||
|
||||
val lastKV: Pair<Int, Lesson>?
|
||||
get() {
|
||||
val idx = lastIdx ?: return null
|
||||
return Pair(idx, lessons[idx]!!)
|
||||
}
|
||||
}
|
||||
@@ -5,23 +5,31 @@ import kotlinx.parcelize.Parcelize
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.util.Calendar
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
@Parcelize
|
||||
@Serializable
|
||||
data class Group(
|
||||
val name: String,
|
||||
val days: ArrayList<Day?>
|
||||
) : Parcelable {
|
||||
fun getCurrentDay(): Map.Entry<Int, Day?>? {
|
||||
val currentDay = (Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 2)
|
||||
val currentIdx: Int?
|
||||
get() {
|
||||
val currentDay = (Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 2)
|
||||
|
||||
if (currentDay < 0 || currentDay > days.size - 1)
|
||||
return null
|
||||
if (currentDay < 0 || currentDay > days.size - 1)
|
||||
return null
|
||||
|
||||
return object : Map.Entry<Int, Day?> {
|
||||
override val key: Int
|
||||
get() = currentDay
|
||||
override val value: Day?
|
||||
get() = days[currentDay]
|
||||
return currentDay
|
||||
}
|
||||
|
||||
val current: Day?
|
||||
get() {
|
||||
return days.getOrNull(currentIdx ?: return null)
|
||||
}
|
||||
|
||||
val currentKV: Pair<Int, Day?>?
|
||||
get() {
|
||||
val idx = currentIdx ?: return null
|
||||
return Pair(idx, days[idx])
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,16 +13,14 @@ data class Lesson(
|
||||
val type: LessonType,
|
||||
val defaultIndex: Int,
|
||||
val name: String,
|
||||
val time: LessonTime?,
|
||||
val time: LessonTime,
|
||||
val cabinets: ArrayList<String>,
|
||||
val teacherNames: ArrayList<String>
|
||||
) : Parcelable {
|
||||
fun getDuration(): Int? {
|
||||
if (this.time == null)
|
||||
return null
|
||||
|
||||
return time.end - time.start
|
||||
}
|
||||
val duration: Int
|
||||
get() {
|
||||
return time.end - time.start
|
||||
}
|
||||
|
||||
fun getNameAndCabinetsShort(context: Context): String {
|
||||
val limitedName = name limit 15
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package ru.n08i40k.polytechnic.next.receiver
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.NetworkType
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import ru.n08i40k.polytechnic.next.service.CurrentLessonViewService
|
||||
import ru.n08i40k.polytechnic.next.work.ScheduleClvAlarm
|
||||
|
||||
class AlarmReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
println("Hi from AlarmReceiver")
|
||||
|
||||
if (intent == null) {
|
||||
println("No intend provided!")
|
||||
return
|
||||
}
|
||||
|
||||
if (context == null) {
|
||||
println("No context provided!")
|
||||
return
|
||||
}
|
||||
println(intent.action)
|
||||
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
|
||||
val rescheduleRequest = OneTimeWorkRequestBuilder<ScheduleClvAlarm>()
|
||||
.setConstraints(constraints)
|
||||
.build()
|
||||
|
||||
WorkManager
|
||||
.getInstance(context)
|
||||
.enqueue(rescheduleRequest)
|
||||
|
||||
CurrentLessonViewService.startService(context.applicationContext)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package ru.n08i40k.polytechnic.next.receiver
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.NetworkType
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import ru.n08i40k.polytechnic.next.work.ScheduleClvAlarm
|
||||
import java.util.logging.Logger
|
||||
|
||||
class BootCompletedBroadcastReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
val logger = Logger.getLogger("BootCompletedBroadcastReceiver")
|
||||
|
||||
if (context == null) {
|
||||
logger.warning("No context provided!")
|
||||
return
|
||||
}
|
||||
|
||||
if (intent == null) {
|
||||
logger.warning("No intend provided!")
|
||||
return
|
||||
}
|
||||
|
||||
if (intent.action != "android.intent.action.BOOT_COMPLETED") {
|
||||
logger.warning("Strange intent action passed!")
|
||||
logger.warning(intent.action)
|
||||
return
|
||||
}
|
||||
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
|
||||
val request = OneTimeWorkRequestBuilder<ScheduleClvAlarm>()
|
||||
.setConstraints(constraints)
|
||||
.build()
|
||||
|
||||
WorkManager
|
||||
.getInstance(context)
|
||||
.enqueue(request)
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,11 @@ import ru.n08i40k.polytechnic.next.model.Day
|
||||
import ru.n08i40k.polytechnic.next.model.Group
|
||||
import ru.n08i40k.polytechnic.next.model.Lesson
|
||||
import ru.n08i40k.polytechnic.next.utils.fmtAsClock
|
||||
import ru.n08i40k.polytechnic.next.utils.getDayMinutes
|
||||
import ru.n08i40k.polytechnic.next.work.StartClvService
|
||||
import java.util.Calendar
|
||||
|
||||
@Suppress("UNNECESSARY_NOT_NULL_ASSERTION")
|
||||
class CurrentLessonViewService : Service() {
|
||||
companion object {
|
||||
private const val NOTIFICATION_STATUS_ID = 1337
|
||||
@@ -58,16 +60,16 @@ class CurrentLessonViewService : Service() {
|
||||
.get(Calendar.HOUR_OF_DAY) * 60 + Calendar.getInstance()
|
||||
.get(Calendar.MINUTE)
|
||||
|
||||
val currentLessonEntry = day!!.getCurrentLesson()
|
||||
val currentLessonIdx: Int? = currentLessonEntry?.key
|
||||
val currentLesson: Lesson? = currentLessonEntry?.value
|
||||
val currentLessonEntry = day!!.currentKV
|
||||
val currentLessonIdx: Int? = currentLessonEntry?.first
|
||||
val currentLesson: Lesson? = currentLessonEntry?.second
|
||||
|
||||
val nextLessonEntry = day!!.getDistanceToNextByIdx(currentLessonIdx)
|
||||
val nextLessonEntry = day!!.distanceToNextByIdx(currentLessonIdx)
|
||||
val nextLesson =
|
||||
if (nextLessonEntry == null)
|
||||
null
|
||||
else
|
||||
day!!.lessons[nextLessonEntry.key]
|
||||
day!!.lessons[nextLessonEntry.first]
|
||||
|
||||
if (currentLesson == null && nextLesson == null) {
|
||||
val notification = NotificationCompat
|
||||
@@ -83,13 +85,13 @@ class CurrentLessonViewService : Service() {
|
||||
return
|
||||
}
|
||||
|
||||
val firstLessonIdx = day!!.getDistanceToNextByMinutes(0)?.key
|
||||
val firstLessonIdx = day!!.distanceToNextByMinutes(0)?.first
|
||||
?: throw NullPointerException("Is this even real?")
|
||||
val distanceToFirst = day!!.lessons[firstLessonIdx]!!.time!!.start - currentMinutes
|
||||
|
||||
val currentLessonDelay =
|
||||
if (currentLesson == null) // Если эта пара - перемена, то конец перемены через (результат getDistanceToNext)
|
||||
nextLessonEntry!!.value
|
||||
nextLessonEntry!!.second
|
||||
else // Если эта пара - обычная пара, то конец пары через (конец этой пары - текущее кол-во минут)
|
||||
currentLesson.time!!.end - currentMinutes
|
||||
|
||||
@@ -157,32 +159,29 @@ class CurrentLessonViewService : Service() {
|
||||
return getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
}
|
||||
|
||||
fun updateSchedule(group: Group?): Boolean {
|
||||
private fun updateSchedule(group: Group?): Boolean {
|
||||
if (group == null) {
|
||||
stopSelf()
|
||||
return false
|
||||
}
|
||||
|
||||
val day = group.getCurrentDay()
|
||||
if (day?.value == null) {
|
||||
val currentDay = group.current
|
||||
if (currentDay == null || currentDay.nonNullIndices.isEmpty()) {
|
||||
stopSelf()
|
||||
return false
|
||||
}
|
||||
|
||||
val dayValue = day.value!!
|
||||
|
||||
if (this.day == null) {
|
||||
if (dayValue.lessons[dayValue.defaultIndices[dayValue.defaultIndices.lastIndex]]!!.time!!.end
|
||||
<= Calendar.getInstance()
|
||||
.get(Calendar.HOUR_OF_DAY) * 60 + Calendar.getInstance()
|
||||
.get(Calendar.MINUTE)
|
||||
) {
|
||||
val nowMinutes = Calendar.getInstance().getDayMinutes()
|
||||
|
||||
if (currentDay.first!!.time.start - nowMinutes > 30
|
||||
|| currentDay.last!!.time.end < nowMinutes) {
|
||||
stopSelf()
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
this.day = dayValue
|
||||
this.day = currentDay
|
||||
|
||||
this.handler.removeCallbacks(updateRunnable)
|
||||
updateRunnable.run()
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.google.firebase.messaging.RemoteMessage
|
||||
import ru.n08i40k.polytechnic.next.NotificationChannels
|
||||
import ru.n08i40k.polytechnic.next.R
|
||||
import ru.n08i40k.polytechnic.next.work.FcmSetTokenWorker
|
||||
import ru.n08i40k.polytechnic.next.work.ScheduleClvAlarm
|
||||
import java.time.Duration
|
||||
|
||||
class MyFirebaseMessagingService : FirebaseMessagingService() {
|
||||
@@ -100,6 +101,18 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
|
||||
NotificationCompat.PRIORITY_DEFAULT,
|
||||
message.data["etag"]
|
||||
)
|
||||
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.build()
|
||||
|
||||
val request = OneTimeWorkRequestBuilder<ScheduleClvAlarm>()
|
||||
.setConstraints(constraints)
|
||||
.build()
|
||||
|
||||
WorkManager
|
||||
.getInstance(applicationContext)
|
||||
.enqueue(request)
|
||||
}
|
||||
|
||||
"app-update" -> {
|
||||
|
||||
@@ -10,6 +10,7 @@ import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -18,6 +19,8 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardColors
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -30,6 +33,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
@@ -53,7 +57,8 @@ fun ExpandableCard(
|
||||
onExpandedChange()
|
||||
transitionState.targetState = expanded
|
||||
},
|
||||
shape = RectangleShape
|
||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface),
|
||||
border = BorderStroke(Dp.Hairline, MaterialTheme.colorScheme.inverseSurface)
|
||||
) {
|
||||
Column {
|
||||
ExpandableCardHeader(title, transition)
|
||||
|
||||
@@ -3,7 +3,6 @@ package ru.n08i40k.polytechnic.next.ui
|
||||
import android.Manifest
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
@@ -38,7 +37,6 @@ 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.service.CurrentLessonViewService
|
||||
import ru.n08i40k.polytechnic.next.settings.settingsDataStore
|
||||
import ru.n08i40k.polytechnic.next.work.FcmUpdateCallbackWorker
|
||||
import ru.n08i40k.polytechnic.next.work.LinkUpdateWorker
|
||||
@@ -103,10 +101,7 @@ class MainActivity : ComponentActivity() {
|
||||
requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||
}
|
||||
|
||||
private fun startTestService() {
|
||||
if (!(applicationContext as PolytechnicApplication).hasNotificationPermission())
|
||||
return
|
||||
|
||||
private fun scheduleAlarm() {
|
||||
lifecycleScope.launch {
|
||||
val schedule = (applicationContext as PolytechnicApplication)
|
||||
.container
|
||||
@@ -116,11 +111,8 @@ class MainActivity : ComponentActivity() {
|
||||
if (schedule is MyResult.Failure)
|
||||
return@launch
|
||||
|
||||
val intent = Intent(this@MainActivity, CurrentLessonViewService::class.java)
|
||||
.apply {
|
||||
putExtra("group", (schedule as MyResult.Success).data)
|
||||
}
|
||||
startForegroundService(intent)
|
||||
(applicationContext as PolytechnicApplication)
|
||||
.scheduleClvService((schedule as MyResult.Success).data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +182,7 @@ class MainActivity : ComponentActivity() {
|
||||
setupFirebaseConfig()
|
||||
|
||||
handleUpdate()
|
||||
startTestService()
|
||||
scheduleAlarm()
|
||||
|
||||
setContent {
|
||||
Box(Modifier.windowInsetsPadding(WindowInsets.safeContent.only(WindowInsetsSides.Top))) {
|
||||
|
||||
@@ -7,8 +7,9 @@ import androidx.activity.ComponentActivity
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideIn
|
||||
import androidx.compose.animation.slideOut
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -95,19 +96,25 @@ private fun NavHostContainer(
|
||||
enterTransition = {
|
||||
slideIn(
|
||||
animationSpec = tween(
|
||||
500,
|
||||
400,
|
||||
delayMillis = 250,
|
||||
easing = LinearOutSlowInEasing
|
||||
)
|
||||
) { fullSize -> IntOffset(-fullSize.width, 0) }
|
||||
) { fullSize -> IntOffset(0, fullSize.height / 16) } + fadeIn(
|
||||
animationSpec = tween(
|
||||
400,
|
||||
delayMillis = 250,
|
||||
easing = LinearOutSlowInEasing
|
||||
)
|
||||
)
|
||||
},
|
||||
exitTransition = {
|
||||
slideOut(
|
||||
fadeOut(
|
||||
animationSpec = tween(
|
||||
500,
|
||||
250,
|
||||
easing = FastOutSlowInEasing
|
||||
)
|
||||
) { fullSize -> IntOffset(fullSize.width, 0) }
|
||||
)
|
||||
},
|
||||
builder = {
|
||||
composable("profile") {
|
||||
|
||||
@@ -66,9 +66,7 @@ fun calculateCurrentLessonIdx(lessons: ArrayList<Lesson?>): Int {
|
||||
val filteredLessons = lessons
|
||||
.filterNotNull()
|
||||
.filter {
|
||||
it.time != null
|
||||
&& it.time.start <= currentMinutes
|
||||
&& it.time.end >= currentMinutes
|
||||
it.time.start <= currentMinutes && it.time.end >= currentMinutes
|
||||
}
|
||||
|
||||
if (filteredLessons.isEmpty())
|
||||
@@ -101,16 +99,20 @@ fun DayCard(
|
||||
modifier = modifier,
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor =
|
||||
if (current) MaterialTheme.colorScheme.surfaceContainerHighest
|
||||
else MaterialTheme.colorScheme.surfaceContainerLowest
|
||||
)
|
||||
if (current) MaterialTheme.colorScheme.inverseSurface
|
||||
else MaterialTheme.colorScheme.surface
|
||||
),
|
||||
border = BorderStroke(1.dp, MaterialTheme.colorScheme.inverseSurface)
|
||||
) {
|
||||
if (day == null) {
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
fontWeight = FontWeight.Bold,
|
||||
textAlign = TextAlign.Center,
|
||||
text = stringResource(R.string.day_null)
|
||||
text = stringResource(R.string.day_null),
|
||||
color =
|
||||
if (current) MaterialTheme.colorScheme.inverseOnSurface
|
||||
else MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
return@Card
|
||||
}
|
||||
|
||||
@@ -37,10 +37,10 @@ fun DayPager(group: Group = FakeScheduleRepository.exampleGroup) {
|
||||
val offset = pagerState.getOffsetDistanceInPages(page).absoluteValue
|
||||
|
||||
lerp(
|
||||
start = 0.95f, stop = 1f, fraction = 1f - offset.coerceIn(0f, 1f)
|
||||
start = 1f, stop = 0.95f, fraction = 1f - offset.coerceIn(0f, 1f)
|
||||
).also { scale ->
|
||||
scaleX = 1F - scale + 0.95F
|
||||
scaleY = 1F - scale + 0.95F
|
||||
scaleX = scale
|
||||
scaleY = scale
|
||||
}
|
||||
alpha = lerp(
|
||||
start = 0.5f, stop = 1f, fraction = 1f - offset.coerceIn(0f, 1f)
|
||||
|
||||
@@ -79,7 +79,7 @@ fun LessonExtraInfo(
|
||||
append(stringResource(R.string.lesson_duration))
|
||||
append(" - ")
|
||||
val duration =
|
||||
if (lesson.time != null) lesson.time.end - lesson.time.start else 0
|
||||
lesson.time.end - lesson.time.start
|
||||
|
||||
append(duration / 60)
|
||||
append(stringResource(R.string.hours))
|
||||
@@ -215,9 +215,7 @@ fun FreeLessonRow(
|
||||
) {
|
||||
LessonViewRow(
|
||||
-1,
|
||||
if (lesson.time != null && nextLesson.time != null) LessonTime(
|
||||
lesson.time.end, nextLesson.time.start
|
||||
) else null,
|
||||
LessonTime(lesson.time.end, nextLesson.time.start),
|
||||
LessonTimeFormat.ONLY_MINUTES_DURATION,
|
||||
stringResource(R.string.lesson_break),
|
||||
arrayListOf(),
|
||||
|
||||
@@ -2,7 +2,9 @@ package ru.n08i40k.polytechnic.next.ui.main.schedule
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -15,6 +17,7 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
@@ -79,6 +82,7 @@ fun ScheduleScreen(
|
||||
val hasSchedule = uiState as ScheduleUiState.HasSchedule
|
||||
|
||||
UpdateInfo(hasSchedule.lastUpdateAt, hasSchedule.updateDates)
|
||||
Spacer(Modifier.height(10.dp))
|
||||
DayPager(hasSchedule.group)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package ru.n08i40k.polytechnic.next.utils
|
||||
|
||||
import java.util.Calendar
|
||||
|
||||
infix fun <T> T?.or(data: T): T {
|
||||
if (this == null)
|
||||
return data
|
||||
@@ -25,4 +27,7 @@ infix fun String.limit(count: Int): String {
|
||||
.substring(0, count - 1)
|
||||
.trimEnd()
|
||||
.plus("…")
|
||||
}
|
||||
}
|
||||
|
||||
fun Calendar.getDayMinutes(): Int =
|
||||
this.get(Calendar.HOUR_OF_DAY) * 60 + this.get(Calendar.MINUTE)
|
||||
@@ -17,7 +17,7 @@ class LinkUpdateWorker(context: Context, params: WorkerParameters) :
|
||||
.getGroup()
|
||||
}
|
||||
|
||||
CurrentLessonViewService.startService(applicationContext)
|
||||
// CurrentLessonViewService.startService(applicationContext)
|
||||
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package ru.n08i40k.polytechnic.next.work
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import ru.n08i40k.polytechnic.next.PolytechnicApplication
|
||||
import ru.n08i40k.polytechnic.next.data.MyResult
|
||||
|
||||
class ScheduleClvAlarm(context: Context, workerParams: WorkerParameters) :
|
||||
Worker(context, workerParams) {
|
||||
override fun doWork(): Result {
|
||||
val application = applicationContext as PolytechnicApplication
|
||||
|
||||
val result = runBlocking {
|
||||
application
|
||||
.container
|
||||
.scheduleRepository
|
||||
.getGroup()
|
||||
}
|
||||
|
||||
if (result is MyResult.Failure)
|
||||
return Result.failure()
|
||||
|
||||
application.scheduleClvService((result as MyResult.Success).data)
|
||||
|
||||
return Result.success()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user