mirror of
https://github.com/n08i40k/polytechnic-android.git
synced 2025-12-06 09:47:48 +03:00
2.0.2
Исправлено отображение уведомления о просмотре устаревшего расписания. Исправлен запуск сервиса постоянного уведомления.
This commit is contained in:
14
.idea/appInsightsSettings.xml
generated
14
.idea/appInsightsSettings.xml
generated
@@ -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>
|
||||||
|
|||||||
4
.idea/runConfigurations.xml
generated
4
.idea/runConfigurations.xml
generated
@@ -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>
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
package ru.n08i40k.polytechnic.next
|
|
||||||
|
|
||||||
object IntentRequestCodes {
|
|
||||||
const val ALARM_CLV = 1337
|
|
||||||
}
|
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,8 +16,6 @@ class LinkUpdateWorker(context: Context, params: WorkerParameters) :
|
|||||||
.getGroup()
|
.getGroup()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CurrentLessonViewService.startService(applicationContext)
|
|
||||||
|
|
||||||
return Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user