Исправлено отображение уведомления о просмотре устаревшего расписания.

Исправлен запуск сервиса постоянного уведомления.
This commit is contained in:
2024-10-26 18:32:33 +04:00
parent 2b2c202cfa
commit c4444ff2ca
17 changed files with 116 additions and 48 deletions

View File

@@ -17,6 +17,20 @@
</option> </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">
<list>
<VersionSetting>
<option name="buildVersion" value="17" />
<option name="displayName" value="2.0.1 (17)" />
<option name="displayVersion" value="2.0.1" />
</VersionSetting>
<VersionSetting>
<option name="buildVersion" value="16" />
<option name="displayName" value="2.0.0prod (16)" />
<option name="displayVersion" value="2.0.0prod" />
</VersionSetting>
</list>
</option>
<option name="visibilityType" value="ALL" /> <option name="visibilityType" value="ALL" />
</InsightsFilterSettings> </InsightsFilterSettings>
</value> </value>

View File

@@ -5,8 +5,12 @@
<set> <set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" /> <option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" /> <option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" /> <option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" /> <option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set> </set>
</option> </option>
</component> </component>

View File

@@ -33,14 +33,13 @@ android {
applicationId = "ru.n08i40k.polytechnic.next" applicationId = "ru.n08i40k.polytechnic.next"
minSdk = 26 minSdk = 26
targetSdk = 35 targetSdk = 35
versionCode = 16 versionCode = 18
versionName = "2.0.0" versionName = "2.0.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {
useSupportLibrary = true useSupportLibrary = true
} }
versionNameSuffix = "prod"
} }
buildTypes { buildTypes {

View File

@@ -1,5 +0,0 @@
package ru.n08i40k.polytechnic.next
object IntentRequestCodes {
const val ALARM_CLV = 1337
}

View File

@@ -7,7 +7,6 @@ import android.os.Build
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import dagger.hilt.android.HiltAndroidApp import dagger.hilt.android.HiltAndroidApp
import ru.n08i40k.polytechnic.next.data.AppContainer import ru.n08i40k.polytechnic.next.data.AppContainer
import ru.n08i40k.polytechnic.next.utils.or
import javax.inject.Inject import javax.inject.Inject
@HiltAndroidApp @HiltAndroidApp
@@ -18,7 +17,7 @@ class PolytechnicApplication : Application() {
fun getAppVersion(): String { fun getAppVersion(): String {
return applicationContext.packageManager return applicationContext.packageManager
.getPackageInfo(this.packageName, 0) .getPackageInfo(this.packageName, 0)
.versionName or "1.0.0" .versionName!!
} }
fun hasNotificationPermission(): Boolean { fun hasNotificationPermission(): Boolean {

View File

@@ -4,8 +4,9 @@ import android.os.Parcelable
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalDateTime
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import ru.n08i40k.polytechnic.next.utils.InstantAsLongSerializer
import ru.n08i40k.polytechnic.next.utils.dateTime import ru.n08i40k.polytechnic.next.utils.dateTime
import ru.n08i40k.polytechnic.next.utils.dayMinutes import ru.n08i40k.polytechnic.next.utils.dayMinutes
import ru.n08i40k.polytechnic.next.utils.now import ru.n08i40k.polytechnic.next.utils.now
@@ -16,9 +17,18 @@ import ru.n08i40k.polytechnic.next.utils.now
@Serializable @Serializable
class Day( class Day(
val name: String, val name: String,
val date: @RawValue Instant, @Serializable(with = InstantAsLongSerializer::class)
@SerialName("date")
private val dateMillis: Long,
val lessons: List<Lesson> val lessons: List<Lesson>
) : Parcelable { ) : Parcelable {
constructor(name: String, date: Instant, lessons: List<Lesson>) : this(
name, date.toEpochMilliseconds(), lessons
)
val date: Instant
get() = Instant.fromEpochMilliseconds(dateMillis)
fun distanceToNextByLocalDateTime(from: LocalDateTime): Pair<Int, Int>? { fun distanceToNextByLocalDateTime(from: LocalDateTime): Pair<Int, Int>? {
val toIdx = lessons val toIdx = lessons
.map { it.time.start } .map { it.time.start }

View File

@@ -6,19 +6,37 @@ import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.toInstant import kotlinx.datetime.toInstant
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import ru.n08i40k.polytechnic.next.utils.InstantAsLongSerializer
@Parcelize @Parcelize
@Serializable @Serializable
data class LessonTime( data class LessonTime(
val start: @RawValue Instant, @Serializable(with = InstantAsLongSerializer::class)
val end: @RawValue Instant @SerialName("start")
private val startMillis: Long,
@Serializable(with = InstantAsLongSerializer::class)
@SerialName("end")
private val endMillis: Long
) : Parcelable { ) : Parcelable {
constructor(start: Instant, end: Instant) : this(
start.toEpochMilliseconds(),
end.toEpochMilliseconds()
)
val start: Instant
get() = Instant.fromEpochMilliseconds(startMillis)
val end: Instant
get() = Instant.fromEpochMilliseconds(endMillis)
companion object { companion object {
fun fromLocalDateTime(start: LocalDateTime, end: LocalDateTime): LessonTime { fun fromLocalDateTime(start: LocalDateTime, end: LocalDateTime): LessonTime {
val timeZone = TimeZone.currentSystemDefault() val timeZone = TimeZone.currentSystemDefault()
return LessonTime(start.toInstant(timeZone), end.toInstant(timeZone)) return LessonTime(
start.toInstant(timeZone),
end.toInstant(timeZone)
)
} }
} }
} }

View File

@@ -17,7 +17,8 @@ fun PolytechnicApp() {
val navController = rememberNavController() val navController = rememberNavController()
NavHost( NavHost(
navController = navController, startDestination = "auth" navController = navController,
startDestination = "auth"
) { ) {
composable(route = "auth") { composable(route = "auth") {
AuthScreen(navController) AuthScreen(navController)

View File

@@ -157,7 +157,10 @@ private fun LinkButton(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
BadgedBox(badge = { if (badged) Badge() }) { BadgedBox(badge = { if (badged) Badge() }) {
Icon(imageVector = icon, contentDescription = text) Icon(
imageVector = icon,
contentDescription = text
)
} }
Spacer(Modifier.width(5.dp)) Spacer(Modifier.width(5.dp))
Text(text) Text(text)
@@ -189,7 +192,10 @@ private fun TopNavBar(
actions = { actions = {
IconButton(onClick = { dropdownExpanded = true }) { IconButton(onClick = { dropdownExpanded = true }) {
BadgedBox(badge = { if (updateAvailable) Badge() }) { BadgedBox(badge = { if (updateAvailable) Badge() }) {
Icon(imageVector = Icons.Filled.Menu, contentDescription = "TopAppBar Menu") Icon(
imageVector = Icons.Filled.Menu,
contentDescription = "top app bar menu"
)
} }
} }
DropdownMenu( DropdownMenu(

View File

@@ -14,35 +14,23 @@ import androidx.compose.ui.res.stringResource
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.compose.ui.util.lerp import androidx.compose.ui.util.lerp
import kotlinx.datetime.LocalDateTime
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.Group import ru.n08i40k.polytechnic.next.model.Group
import ru.n08i40k.polytechnic.next.ui.widgets.NotificationCard import ru.n08i40k.polytechnic.next.ui.widgets.NotificationCard
import java.time.LocalDate import ru.n08i40k.polytechnic.next.utils.dateTime
import java.time.format.DateTimeFormatter import ru.n08i40k.polytechnic.next.utils.now
import java.time.temporal.WeekFields
import java.util.Calendar import java.util.Calendar
import java.util.Locale
import java.util.logging.Level import java.util.logging.Level
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
private fun isCurrentWeek(group: Group): Boolean { private fun isScheduleOutdated(group: Group): Boolean {
if (group.days.isEmpty()) val nowDateTime = LocalDateTime.now()
return true val lastDay = group.days.lastOrNull() ?: return true
val lastLesson = lastDay.last ?: return true
val dateString = group.days[0].name return nowDateTime > lastLesson.time.end.dateTime
val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy", Locale("ru"))
val datePart = dateString.split(" ").getOrNull(1) ?: return true
val date = LocalDate.parse(datePart, formatter)
val currentDate = LocalDate.now()
val weekField = WeekFields.of(Locale.getDefault()).weekOfWeekBasedYear()
val currentWeek = currentDate.get(weekField)
val dateWeek = date.get(weekField)
return dateWeek >= currentWeek
} }
@Preview @Preview
@@ -57,7 +45,7 @@ fun DayPager(group: Group = FakeScheduleRepository.exampleGroup) {
pageCount = { group.days.size }) pageCount = { group.days.size })
Column { Column {
if (!isCurrentWeek(group)) { if (isScheduleOutdated(group)) {
NotificationCard( NotificationCard(
level = Level.WARNING, level = Level.WARNING,
title = stringResource(R.string.outdated_schedule) title = stringResource(R.string.outdated_schedule)

View File

@@ -11,6 +11,9 @@ import ru.n08i40k.polytechnic.next.R
@Composable @Composable
internal fun PaskhalkoDialog() { internal fun PaskhalkoDialog() {
Dialog(onDismissRequest = {}) { Dialog(onDismissRequest = {}) {
Image(painterResource(R.drawable.paskhalko), contentDescription = "Paskhalko") Image(
painter = painterResource(R.drawable.paskhalko),
contentDescription = "paskhalko"
)
} }
} }

View File

@@ -147,7 +147,7 @@ private fun ExpandableCardArrow(
Icon( Icon(
modifier = Modifier.rotate(rotationDegree), modifier = Modifier.rotate(rotationDegree),
imageVector = Icons.Filled.ArrowDropDown, imageVector = Icons.Filled.ArrowDropDown,
contentDescription = "Expandable Arrow" contentDescription = "expandable arrow"
) )
} }

View File

@@ -76,7 +76,7 @@ fun GroupSelector(
value = value ?: groups.getOrElse(1) { "TODO" }!!, value = value ?: groups.getOrElse(1) { "TODO" }!!,
leadingIcon = { leadingIcon = {
Icon( Icon(
imageVector = Icons.Filled.Email, Icons.Filled.Email,
contentDescription = "group" contentDescription = "group"
) )
}, },

View File

@@ -51,8 +51,14 @@ fun NotificationCard(
else -> AppIcons.Filled.Info else -> AppIcons.Filled.Info
} }
Icon(imageVector = icon, contentDescription = "Level") Icon(
Icon(imageVector = icon, contentDescription = "Level") imageVector = icon,
contentDescription = "level"
)
Icon(
imageVector = icon,
contentDescription = "level"
)
} }
Text( Text(
text = title, text = title,

View File

@@ -62,7 +62,12 @@ fun RoleSelector(
onDismissRequest = { expanded = false }) { onDismissRequest = { expanded = false }) {
AcceptableUserRoles.forEach { AcceptableUserRoles.forEach {
DropdownMenuItem( DropdownMenuItem(
leadingIcon = { Icon(it.icon, contentDescription = "Role icon") }, leadingIcon = {
Icon(
imageVector = it.icon,
contentDescription = "role icon"
)
},
text = { Text(stringResource(it.stringId)) }, text = { Text(stringResource(it.stringId)) },
onClick = { onClick = {
expanded = false expanded = false

View File

@@ -0,0 +1,22 @@
package ru.n08i40k.polytechnic.next.utils
import kotlinx.datetime.Instant
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
class InstantAsLongSerializer : KSerializer<Long> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("Instant", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Long) {
encoder.encodeString(Instant.fromEpochMilliseconds(value).toString())
}
override fun deserialize(decoder: Decoder): Long {
return Instant.parse(decoder.decodeString()).toEpochMilliseconds()
}
}

View File

@@ -16,8 +16,6 @@ class LinkUpdateWorker(context: Context, params: WorkerParameters) :
.getGroup() .getGroup()
} }
// CurrentLessonViewService.startService(applicationContext)
return Result.success() return Result.success()
} }
} }