Block app timezone to polish timezone (#1598)

This commit is contained in:
Michael 2021-12-31 11:53:09 +01:00 committed by GitHub
parent bfd7f688ab
commit e6b2acabd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 2870 additions and 400 deletions

View File

@ -174,7 +174,7 @@ ext {
}
dependencies {
implementation "io.github.wulkanowy:sdk:1.4.4"
implementation "io.github.wulkanowy:sdk:42bce37748"
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'

File diff suppressed because it is too large Load Diff

View File

@ -67,49 +67,7 @@ import io.github.wulkanowy.data.db.entities.Teacher
import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.db.entities.TimetableAdditional
import io.github.wulkanowy.data.db.entities.TimetableHeader
import io.github.wulkanowy.data.db.migrations.Migration10
import io.github.wulkanowy.data.db.migrations.Migration11
import io.github.wulkanowy.data.db.migrations.Migration12
import io.github.wulkanowy.data.db.migrations.Migration13
import io.github.wulkanowy.data.db.migrations.Migration14
import io.github.wulkanowy.data.db.migrations.Migration15
import io.github.wulkanowy.data.db.migrations.Migration16
import io.github.wulkanowy.data.db.migrations.Migration17
import io.github.wulkanowy.data.db.migrations.Migration18
import io.github.wulkanowy.data.db.migrations.Migration19
import io.github.wulkanowy.data.db.migrations.Migration2
import io.github.wulkanowy.data.db.migrations.Migration20
import io.github.wulkanowy.data.db.migrations.Migration21
import io.github.wulkanowy.data.db.migrations.Migration22
import io.github.wulkanowy.data.db.migrations.Migration23
import io.github.wulkanowy.data.db.migrations.Migration24
import io.github.wulkanowy.data.db.migrations.Migration25
import io.github.wulkanowy.data.db.migrations.Migration26
import io.github.wulkanowy.data.db.migrations.Migration27
import io.github.wulkanowy.data.db.migrations.Migration28
import io.github.wulkanowy.data.db.migrations.Migration29
import io.github.wulkanowy.data.db.migrations.Migration3
import io.github.wulkanowy.data.db.migrations.Migration30
import io.github.wulkanowy.data.db.migrations.Migration31
import io.github.wulkanowy.data.db.migrations.Migration32
import io.github.wulkanowy.data.db.migrations.Migration33
import io.github.wulkanowy.data.db.migrations.Migration34
import io.github.wulkanowy.data.db.migrations.Migration35
import io.github.wulkanowy.data.db.migrations.Migration36
import io.github.wulkanowy.data.db.migrations.Migration37
import io.github.wulkanowy.data.db.migrations.Migration38
import io.github.wulkanowy.data.db.migrations.Migration39
import io.github.wulkanowy.data.db.migrations.Migration4
import io.github.wulkanowy.data.db.migrations.Migration40
import io.github.wulkanowy.data.db.migrations.Migration41
import io.github.wulkanowy.data.db.migrations.Migration42
import io.github.wulkanowy.data.db.migrations.Migration43
import io.github.wulkanowy.data.db.migrations.Migration44
import io.github.wulkanowy.data.db.migrations.Migration5
import io.github.wulkanowy.data.db.migrations.Migration6
import io.github.wulkanowy.data.db.migrations.Migration7
import io.github.wulkanowy.data.db.migrations.Migration8
import io.github.wulkanowy.data.db.migrations.Migration9
import io.github.wulkanowy.data.db.migrations.*
import io.github.wulkanowy.utils.AppInfo
import javax.inject.Singleton
@ -157,7 +115,7 @@ import javax.inject.Singleton
abstract class AppDatabase : RoomDatabase() {
companion object {
const val VERSION_SCHEMA = 45
const val VERSION_SCHEMA = 46
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
Migration2(),
@ -203,6 +161,7 @@ abstract class AppDatabase : RoomDatabase() {
Migration42(),
Migration43(),
Migration44(),
Migration46(),
)
fun newInstance(

View File

@ -1,40 +1,33 @@
package io.github.wulkanowy.data.db
import androidx.room.TypeConverter
import io.github.wulkanowy.utils.toTimestamp
import kotlinx.serialization.SerializationException
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.Month
import java.time.ZoneOffset
import java.util.Date
import java.util.*
class Converters {
private val json = Json
@TypeConverter
fun timestampToDate(value: Long?): LocalDate? = value?.run {
Date(value).toInstant().atZone(ZoneOffset.UTC).toLocalDate()
}
fun timestampToLocalDate(value: Long?): LocalDate? =
value?.let(::Date)?.toInstant()?.atZone(ZoneOffset.UTC)?.toLocalDate()
@TypeConverter
fun dateToTimestamp(date: LocalDate?): Long? {
return date?.atStartOfDay()?.toInstant(ZoneOffset.UTC)?.toEpochMilli()
}
fun dateToTimestamp(date: LocalDate?): Long? = date?.toTimestamp()
@TypeConverter
fun timestampToTime(value: Long?): LocalDateTime? = value?.let {
LocalDateTime.ofInstant(Instant.ofEpochMilli(value), ZoneOffset.UTC)
}
fun instantToTimestamp(instant: Instant?): Long? = instant?.toEpochMilli()
@TypeConverter
fun timeToTimestamp(date: LocalDateTime?): Long? {
return date?.atZone(ZoneOffset.UTC)?.toInstant()?.toEpochMilli()
}
fun timestampToInstant(timestamp: Long?): Instant? = timestamp?.let(Instant::ofEpochMilli)
@TypeConverter
fun monthToInt(month: Month?) = month?.value

View File

@ -4,7 +4,7 @@ import androidx.room.Dao
import androidx.room.Query
import io.github.wulkanowy.data.db.entities.Conference
import kotlinx.coroutines.flow.Flow
import java.time.LocalDateTime
import java.time.Instant
import javax.inject.Singleton
@Dao
@ -12,5 +12,5 @@ import javax.inject.Singleton
interface ConferenceDao : BaseDao<Conference> {
@Query("SELECT * FROM Conferences WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :startDate")
fun loadAll(diaryId: Int, studentId: Int, startDate: LocalDateTime): Flow<List<Conference>>
fun loadAll(diaryId: Int, studentId: Int, startDate: Instant): Flow<List<Conference>>
}

View File

@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
import java.time.LocalDateTime
import java.time.Instant
@Entity(tableName = "Conferences")
data class Conference(
@ -27,7 +27,7 @@ data class Conference(
@ColumnInfo(name = "conference_id")
val conferenceId: Int,
val date: LocalDateTime
val date: Instant,
) : Serializable {
@PrimaryKey(autoGenerate = true)

View File

@ -3,7 +3,7 @@ package io.github.wulkanowy.data.db.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.time.LocalDateTime
import java.time.Instant
@Entity(tableName = "GradesSummary")
data class GradeSummary(
@ -45,8 +45,8 @@ data class GradeSummary(
var isFinalGradeNotified: Boolean = true
@ColumnInfo(name = "predicted_grade_last_change")
var predictedGradeLastChange: LocalDateTime = LocalDateTime.now()
var predictedGradeLastChange: Instant = Instant.now()
@ColumnInfo(name = "final_grade_last_change")
var finalGradeLastChange: LocalDateTime = LocalDateTime.now()
var finalGradeLastChange: Instant = Instant.now()
}

View File

@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
import java.time.LocalDateTime
import java.time.Instant
@Entity(tableName = "Messages")
data class Message(
@ -29,7 +29,7 @@ data class Message(
val subject: String,
val date: LocalDateTime,
val date: Instant,
@ColumnInfo(name = "folder_id")
val folderId: Int,

View File

@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
import java.time.LocalDateTime
import java.time.Instant
@Entity(tableName = "MobileDevices")
data class MobileDevice(
@ -17,7 +17,7 @@ data class MobileDevice(
val name: String,
val date: LocalDateTime
val date: Instant,
) : Serializable {
@PrimaryKey(autoGenerate = true)

View File

@ -4,7 +4,7 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import io.github.wulkanowy.services.sync.notifications.NotificationType
import java.time.LocalDateTime
import java.time.Instant
@Entity(tableName = "Notifications")
data class Notification(
@ -18,7 +18,7 @@ data class Notification(
val type: NotificationType,
val date: LocalDateTime,
val date: Instant,
val data: String? = null
) {

View File

@ -5,7 +5,7 @@ import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import java.io.Serializable
import java.time.LocalDateTime
import java.time.Instant
@Entity(
tableName = "Students",
@ -74,7 +74,7 @@ data class Student(
val isCurrent: Boolean,
@ColumnInfo(name = "registration_date")
val registrationDate: LocalDateTime
val registrationDate: Instant,
) : Serializable {
@PrimaryKey(autoGenerate = true)

View File

@ -4,8 +4,8 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
@Entity(tableName = "Timetable")
data class Timetable(
@ -18,9 +18,9 @@ data class Timetable(
val number: Int,
val start: LocalDateTime,
val start: Instant,
val end: LocalDateTime,
val end: Instant,
val date: LocalDate,

View File

@ -4,9 +4,9 @@ import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import java.io.Serializable
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.util.UUID
import java.util.*
@Entity(tableName = "TimetableAdditional")
data class TimetableAdditional(
@ -17,9 +17,9 @@ data class TimetableAdditional(
@ColumnInfo(name = "diary_id")
val diaryId: Int,
val start: LocalDateTime,
val start: Instant,
val end: LocalDateTime,
val end: Instant,
val date: LocalDate,

View File

@ -43,12 +43,14 @@ class Migration12 : Migration(11, 12) {
private fun getStudentsIds(database: SupportSQLiteDatabase): List<Int> {
val students = mutableListOf<Int>()
val studentsCursor = database.query("SELECT student_id FROM Students")
if (studentsCursor.moveToFirst()) {
database.query("SELECT student_id FROM Students").use {
if (it.moveToFirst()) {
do {
students.add(studentsCursor.getInt(0))
} while (studentsCursor.moveToNext())
students.add(it.getInt(0))
} while (it.moveToNext())
}
}
return students
}

View File

@ -25,12 +25,14 @@ class Migration13 : Migration(12, 13) {
private fun getStudentsIds(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
val students = mutableListOf<Pair<Int, String>>()
val studentsCursor = database.query("SELECT id, school_name FROM Students")
if (studentsCursor.moveToFirst()) {
database.query("SELECT id, school_name FROM Students").use {
if (it.moveToFirst()) {
do {
students.add(studentsCursor.getInt(0) to studentsCursor.getString(1))
} while (studentsCursor.moveToNext())
students.add(it.getInt(0) to it.getString(1))
} while (it.moveToNext())
}
}
return students
}
@ -42,12 +44,14 @@ class Migration13 : Migration(12, 13) {
private fun getStudentsAndClassIds(database: SupportSQLiteDatabase): List<Pair<Int, Int>> {
val students = mutableListOf<Pair<Int, Int>>()
val studentsCursor = database.query("SELECT student_id, class_id FROM Students")
if (studentsCursor.moveToFirst()) {
database.query("SELECT student_id, class_id FROM Students").use {
if (it.moveToFirst()) {
do {
students.add(studentsCursor.getInt(0) to studentsCursor.getInt(1))
} while (studentsCursor.moveToNext())
students.add(it.getInt(0) to it.getInt(1))
} while (it.moveToNext())
}
}
return students
}

View File

@ -22,23 +22,27 @@ class Migration27 : Migration(26, 27) {
private fun getStudentsIdsAndNames(database: SupportSQLiteDatabase): MutableList<Triple<Long, Int, String>> {
val students = mutableListOf<Triple<Long, Int, String>>()
val studentsCursor = database.query("SELECT id, user_login_id, student_name FROM Students")
if (studentsCursor.moveToFirst()) {
database.query("SELECT id, user_login_id, student_name FROM Students").use {
if (it.moveToFirst()) {
do {
students.add(Triple(studentsCursor.getLong(0), studentsCursor.getInt(1), studentsCursor.getString(2)))
} while (studentsCursor.moveToNext())
students.add(Triple(it.getLong(0), it.getInt(1), it.getString(2)))
} while (it.moveToNext())
}
}
return students
}
private fun getReportingUnits(database: SupportSQLiteDatabase): MutableList<Pair<Int, String>> {
val units = mutableListOf<Pair<Int, String>>()
val unitsCursor = database.query("SELECT sender_id, sender_name FROM ReportingUnits")
if (unitsCursor.moveToFirst()) {
database.query("SELECT sender_id, sender_name FROM ReportingUnits").use {
if (it.moveToFirst()) {
do {
units.add(unitsCursor.getInt(0) to unitsCursor.getString(1))
} while (unitsCursor.moveToNext())
units.add(it.getInt(0) to it.getString(1))
} while (it.moveToNext())
}
}
return units
}

View File

@ -10,15 +10,17 @@ class Migration35(private val appInfo: AppInfo) : Migration(34, 35) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE Students ADD COLUMN `avatar_color` INTEGER NOT NULL DEFAULT 0")
val studentsCursor = database.query("SELECT * FROM Students")
while (studentsCursor.moveToNext()) {
val studentId = studentsCursor.getLongOrNull(0)
database.query("SELECT * FROM Students").use {
while (it.moveToNext()) {
val studentId = it.getLongOrNull(0)
database.execSQL(
"""UPDATE Students
"""
UPDATE Students
SET avatar_color = ${appInfo.defaultColorsForAvatar.random()}
WHERE id = $studentId"""
WHERE id = $studentId
"""
)
}
}
}
}

View File

@ -0,0 +1,102 @@
package io.github.wulkanowy.data.db.migrations
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import java.time.Instant
import java.time.ZoneId
import java.time.ZoneOffset
class Migration46 : Migration(45, 46) {
override fun migrate(database: SupportSQLiteDatabase) {
migrateConferences(database)
migrateMessages(database)
migrateMobileDevices(database)
migrateNotifications(database)
migrateTimetable(database)
migrateTimetableAdditional(database)
}
private fun migrateConferences(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM Conferences").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
val timestampUtc = timestampLocal.timestampLocalToUTC()
database.execSQL("UPDATE Conferences SET date = $timestampUtc WHERE id = $id")
}
}
}
private fun migrateMessages(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM Messages").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
val timestampUtc = timestampLocal.timestampLocalToUTC()
database.execSQL("UPDATE Messages SET date = $timestampUtc WHERE id = $id")
}
}
}
private fun migrateMobileDevices(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM MobileDevices").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
val timestampUtc = timestampLocal.timestampLocalToUTC()
database.execSQL("UPDATE MobileDevices SET date = $timestampUtc WHERE id = $id")
}
}
}
private fun migrateNotifications(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM Notifications").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocal = it.getLong(it.getColumnIndexOrThrow("date"))
val timestampUtc = timestampLocal.timestampLocalToUTC()
database.execSQL("UPDATE Notifications SET date = $timestampUtc WHERE id = $id")
}
}
}
private fun migrateTimetable(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM Timetable").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start"))
val timestampLocalEnd = it.getLong(it.getColumnIndexOrThrow("end"))
val timestampUtcStart = timestampLocalStart.timestampLocalToUTC()
val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC()
database.execSQL("UPDATE Timetable SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id")
}
}
}
private fun migrateTimetableAdditional(database: SupportSQLiteDatabase) {
database.query("SELECT * FROM TimetableAdditional").use {
while (it.moveToNext()) {
val id = it.getLong(it.getColumnIndexOrThrow("id"))
val timestampLocalStart = it.getLong(it.getColumnIndexOrThrow("start"))
val timestampLocalEnd = it.getLong(it.getColumnIndexOrThrow("end"))
val timestampUtcStart = timestampLocalStart.timestampLocalToUTC()
val timestampUtcEnd = timestampLocalEnd.timestampLocalToUTC()
database.execSQL("UPDATE TimetableAdditional SET start = $timestampUtcStart, end = $timestampUtcEnd WHERE id = $id")
}
}
}
private fun Long.timestampLocalToUTC(): Long = Instant.ofEpochMilli(this)
.atZone(ZoneOffset.UTC)
.withZoneSameLocal(ZoneId.of("Europe/Warsaw"))
.withZoneSameInstant(ZoneId.systemDefault())
.toInstant()
.toEpochMilli()
}

View File

@ -10,7 +10,7 @@ fun List<SdkConference>.mapToEntities(semester: Semester) = map {
diaryId = semester.diaryId,
agenda = it.agenda,
conferenceId = it.id,
date = it.date,
date = it.dateZoned.toInstant(),
presentOnConference = it.presentOnConference,
subject = it.subject,
title = it.title

View File

@ -4,7 +4,7 @@ import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.MessageAttachment
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.Student
import java.time.LocalDateTime
import java.time.Instant
import io.github.wulkanowy.sdk.pojo.Message as SdkMessage
import io.github.wulkanowy.sdk.pojo.MessageAttachment as SdkMessageAttachment
import io.github.wulkanowy.sdk.pojo.Recipient as SdkRecipient
@ -18,7 +18,7 @@ fun List<SdkMessage>.mapToEntities(student: Student) = map {
senderId = it.sender?.loginId ?: 0,
recipient = it.recipients.singleOrNull()?.name ?: "Wielu adresatów",
subject = it.subject.trim(),
date = it.date ?: LocalDateTime.now(),
date = it.dateZoned?.toInstant() ?: Instant.now(),
folderId = it.folderId,
unread = it.unread ?: false,
removed = it.removed,

View File

@ -3,13 +3,13 @@ package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.MobileDevice
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.pojos.MobileDeviceToken
import io.github.wulkanowy.sdk.pojo.Token as SdkToken
import io.github.wulkanowy.sdk.pojo.Device as SdkDevice
import io.github.wulkanowy.sdk.pojo.Token as SdkToken
fun List<SdkDevice>.mapToEntities(semester: Semester) = map {
MobileDevice(
userLoginId = semester.studentId,
date = it.createDate,
date = it.createDateZoned.toInstant(),
deviceId = it.id,
name = it.name
)

View File

@ -2,7 +2,7 @@ package io.github.wulkanowy.data.mappers
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import java.time.LocalDateTime
import java.time.Instant
import io.github.wulkanowy.sdk.pojo.Student as SdkStudent
fun List<SdkStudent>.mapToEntities(password: String = "", colors: List<Long>) = map {
@ -24,7 +24,7 @@ fun List<SdkStudent>.mapToEntities(password: String = "", colors: List<Long>) =
scrapperBaseUrl = it.scrapperBaseUrl,
loginType = it.loginType.name,
isCurrent = false,
registrationDate = LocalDateTime.now(),
registrationDate = Instant.now(),
mobileBaseUrl = it.mobileBaseUrl,
privateKey = it.privateKey,
certificateKey = it.certificateKey,

View File

@ -21,8 +21,8 @@ fun List<SdkTimetable>.mapToEntities(semester: Semester) = map {
studentId = semester.studentId,
diaryId = semester.diaryId,
number = it.number,
start = it.start,
end = it.end,
start = it.startZoned.toInstant(),
end = it.endZoned.toInstant(),
date = it.date,
subject = it.subject,
subjectOld = it.subjectOld,
@ -45,8 +45,8 @@ fun List<SdkTimetableAdditional>.mapToEntities(semester: Semester) = map {
diaryId = semester.diaryId,
subject = it.subject,
date = it.date,
start = it.start,
end = it.end
start = it.startZoned.toInstant(),
end = it.endZoned.toInstant(),
)
}

View File

@ -6,16 +6,10 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneOffset
import javax.inject.Inject
import javax.inject.Singleton
@ -35,7 +29,7 @@ class ConferenceRepository @Inject constructor(
semester: Semester,
forceRefresh: Boolean,
notify: Boolean = false,
startDate: LocalDateTime = LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC),
startDate: Instant = Instant.EPOCH,
) = networkBoundResource(
mutex = saveFetchResultMutex,
shouldFetch = {
@ -66,7 +60,7 @@ class ConferenceRepository @Inject constructor(
conferenceDb.loadAll(
diaryId = semester.diaryId,
studentId = semester.studentId,
startDate = LocalDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC)
startDate = Instant.EPOCH,
)
suspend fun updateConference(conference: List<Conference>) = conferenceDb.updateAll(conference)

View File

@ -8,16 +8,12 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.sync.Mutex
import java.time.LocalDateTime
import java.time.Instant
import javax.inject.Inject
import javax.inject.Singleton
@ -70,8 +66,8 @@ class GradeRepository @Inject constructor(
newDetails: List<Grade>,
notify: Boolean
) {
val notifyBreakDate = oldGrades.maxByOrNull {it.date }
?.date ?: student.registrationDate.toLocalDate()
val notifyBreakDate = oldGrades.maxByOrNull { it.date }?.date
?: student.registrationDate.toLocalDate()
gradeDb.deleteAll(oldGrades uniqueSubtract newDetails)
gradeDb.insertAll((newDetails uniqueSubtract oldGrades).onEach {
if (it.date >= notifyBreakDate) it.apply {
@ -101,13 +97,13 @@ class GradeRepository @Inject constructor(
}
summary.predictedGradeLastChange = when {
oldSummary == null -> LocalDateTime.now()
summary.predictedGrade != oldSummary.predictedGrade -> LocalDateTime.now()
oldSummary == null -> Instant.now()
summary.predictedGrade != oldSummary.predictedGrade -> Instant.now()
else -> oldSummary.predictedGradeLastChange
}
summary.finalGradeLastChange = when {
oldSummary == null -> LocalDateTime.now()
summary.finalGrade != oldSummary.finalGrade -> LocalDateTime.now()
oldSummary == null -> Instant.now()
summary.finalGrade != oldSummary.finalGrade -> Instant.now()
else -> oldSummary.finalGradeLastChange
}
})

View File

@ -6,11 +6,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.uniqueSubtract
import io.github.wulkanowy.utils.*
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.sync.Mutex
import javax.inject.Inject

View File

@ -7,24 +7,16 @@ import com.fredporciuncula.flow.preferences.FlowSharedPreferences
import com.fredporciuncula.flow.preferences.Preference
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.enums.AppTheme
import io.github.wulkanowy.data.enums.GradeColorTheme
import io.github.wulkanowy.data.enums.GradeExpandMode
import io.github.wulkanowy.data.enums.GradeSortingMode
import io.github.wulkanowy.data.enums.TimetableMode
import io.github.wulkanowy.sdk.toLocalDate
import io.github.wulkanowy.data.enums.*
import io.github.wulkanowy.ui.modules.dashboard.DashboardItem
import io.github.wulkanowy.ui.modules.grade.GradeAverageMode
import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.utils.toTimestamp
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.Instant
import javax.inject.Inject
import javax.inject.Singleton
@ -208,10 +200,10 @@ class PreferencesRepository @Inject constructor(
R.bool.pref_default_optional_arithmetic_average
)
var lasSyncDate: LocalDateTime
var lasSyncDate: Instant?
get() = getLong(R.string.pref_key_last_sync_date, R.string.pref_default_last_sync_date)
.toLocalDateTime()
set(value) = sharedPref.edit().putLong("last_sync_date", value.toTimestamp()).apply()
.takeIf { it != 0L }?.let(Instant::ofEpochMilli)
set(value) = sharedPref.edit().putLong("last_sync_date", value?.toEpochMilli() ?: 0).apply()
var dashboardItemsPosition: Map<DashboardItem.Type, Int>?
get() {
@ -270,11 +262,12 @@ class PreferencesRepository @Inject constructor(
get() = sharedPref.getInt(PREF_KEY_IN_APP_REVIEW_COUNT, 0)
set(value) = sharedPref.edit().putInt(PREF_KEY_IN_APP_REVIEW_COUNT, value).apply()
var inAppReviewDate: LocalDate?
var inAppReviewDate: Instant?
get() = sharedPref.getLong(PREF_KEY_IN_APP_REVIEW_DATE, 0).takeIf { it != 0L }
?.toLocalDate()
set(value) = sharedPref.edit().putLong(PREF_KEY_IN_APP_REVIEW_DATE, value!!.toTimestamp())
.apply()
?.let(Instant::ofEpochMilli)
set(value) = sharedPref.edit {
putLong(PREF_KEY_IN_APP_REVIEW_DATE, value?.toEpochMilli() ?: 0)
}
var isAppReviewDone: Boolean
get() = sharedPref.getBoolean(PREF_KEY_IN_APP_REVIEW_DONE, false)

View File

@ -19,7 +19,6 @@ import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.PendingIntentCompat
import io.github.wulkanowy.utils.flowWithResource
import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.utils.toLocalDateTime
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.launchIn
@ -95,7 +94,7 @@ class TimetableNotificationReceiver : BroadcastReceiver() {
val nextSubject = intent.getStringExtra(LESSON_NEXT_TITLE)
val nextRoom = intent.getStringExtra(LESSON_NEXT_ROOM)
Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: ${start.toLocalDateTime()}, student: $studentId")
Timber.d("TimetableNotification receive: type: $type, subject: $subject, start: $start, student: $studentId")
val notificationTitleResId =
if (type == NOTIFICATION_TYPE_CURRENT) R.string.timetable_now else R.string.timetable_next

View File

@ -28,12 +28,12 @@ import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companio
import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.PendingIntentCompat
import io.github.wulkanowy.utils.nickOrName
import io.github.wulkanowy.utils.toTimestamp
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.time.Duration.ofMinutes
import java.time.Instant
import java.time.Instant.now
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalDateTime.now
import javax.inject.Inject
class TimetableNotificationSchedulerHelper @Inject constructor(
@ -43,14 +43,14 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
private val dispatchersProvider: DispatchersProvider,
) {
private fun getRequestCode(time: LocalDateTime, studentId: Int) =
(time.toTimestamp() * studentId).toInt()
private fun getRequestCode(time: Instant, studentId: Int): Int =
(time.toEpochMilli() * studentId).toInt()
private fun getUpcomingLessonTime(
index: Int,
day: List<Timetable>,
lesson: Timetable
) = day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30)
): Instant = day.getOrNull(index - 1)?.end ?: lesson.start.minus(ofMinutes(30))
suspend fun cancelScheduled(lessons: List<Timetable>, student: Student) {
val studentId = student.studentId
@ -71,7 +71,7 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
}
}
private fun cancelScheduledTo(range: ClosedRange<LocalDateTime>, requestCode: Int) {
private fun cancelScheduledTo(range: ClosedRange<Instant>, requestCode: Int) {
if (now() in range) cancelNotification()
alarmManager.cancel(
@ -150,8 +150,8 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
putExtra(STUDENT_ID, student.studentId)
putExtra(STUDENT_NAME, student.nickOrName)
putExtra(LESSON_ROOM, lesson.room)
putExtra(LESSON_START, lesson.start.toTimestamp())
putExtra(LESSON_END, lesson.end.toTimestamp())
putExtra(LESSON_START, lesson.start.toEpochMilli())
putExtra(LESSON_END, lesson.end.toEpochMilli())
putExtra(LESSON_TITLE, lesson.subject)
putExtra(LESSON_NEXT_TITLE, nextLesson?.subject)
putExtra(LESSON_NEXT_ROOM, nextLesson?.room)
@ -162,11 +162,11 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
intent: Intent,
studentId: Int,
notificationType: Int,
time: LocalDateTime
time: Instant
) {
try {
AlarmManagerCompat.setExactAndAllowWhileIdle(
alarmManager, RTC_WAKEUP, time.toTimestamp(),
alarmManager, RTC_WAKEUP, time.toEpochMilli(),
PendingIntent.getBroadcast(context, getRequestCode(time, studentId), intent.also {
it.putExtra(LESSON_TYPE, notificationType)
}, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE)

View File

@ -23,7 +23,7 @@ import io.github.wulkanowy.utils.DispatchersProvider
import io.github.wulkanowy.utils.getCompatColor
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.time.LocalDateTime
import java.time.Instant
import kotlin.random.Random
@HiltWorker
@ -91,7 +91,7 @@ class SyncWorker @AssistedInject constructor(
}
errors.isNotEmpty() -> Result.retry()
else -> {
preferencesRepository.lasSyncDate = LocalDateTime.now()
preferencesRepository.lasSyncDate = Instant.now()
Result.success()
}
}

View File

@ -18,7 +18,7 @@ import io.github.wulkanowy.utils.PendingIntentCompat
import io.github.wulkanowy.utils.getCompatBitmap
import io.github.wulkanowy.utils.getCompatColor
import io.github.wulkanowy.utils.nickOrName
import java.time.LocalDateTime
import java.time.Instant
import javax.inject.Inject
import kotlin.random.Random
@ -169,7 +169,7 @@ class AppNotificationManager @Inject constructor(
title = notificationData.title,
content = notificationData.content,
type = notificationType,
date = LocalDateTime.now()
date = Instant.now(),
)
notificationRepository.saveNotification(notificationEntity)

View File

@ -11,8 +11,8 @@ import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.getPlural
import io.github.wulkanowy.utils.toFormattedString
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import javax.inject.Inject
class ChangeTimetableNotification @Inject constructor(
@ -21,7 +21,7 @@ class ChangeTimetableNotification @Inject constructor(
) {
suspend fun notify(items: List<Timetable>, student: Student) {
val currentTime = LocalDateTime.now()
val currentTime = Instant.now()
val changedLessons = items.filter { (it.canceled || it.changes) && it.start > currentTime }
val notificationDataList = changedLessons.groupBy { it.date }
.map { (date, lessons) ->

View File

@ -11,7 +11,7 @@ import io.github.wulkanowy.ui.modules.Destination
import io.github.wulkanowy.ui.modules.splash.SplashActivity
import io.github.wulkanowy.utils.getPlural
import io.github.wulkanowy.utils.toFormattedString
import java.time.LocalDateTime
import java.time.Instant
import javax.inject.Inject
class NewConferenceNotification @Inject constructor(
@ -20,7 +20,7 @@ class NewConferenceNotification @Inject constructor(
) {
suspend fun notify(items: List<Conference>, student: Student) {
val today = LocalDateTime.now()
val today = Instant.now()
val lines = items.filter { !it.date.isBefore(today) }
.map {
"${it.date.toFormattedString("dd.MM")} - ${it.title}: ${it.subject}"

View File

@ -13,13 +13,8 @@ import io.github.wulkanowy.ui.modules.about.license.LicenseFragment
import io.github.wulkanowy.ui.modules.debug.DebugFragment
import io.github.wulkanowy.ui.modules.main.MainActivity
import io.github.wulkanowy.ui.modules.main.MainView
import io.github.wulkanowy.utils.AppInfo
import io.github.wulkanowy.utils.getCompatDrawable
import io.github.wulkanowy.utils.openAppInMarket
import io.github.wulkanowy.utils.openEmailClient
import io.github.wulkanowy.utils.openInternetBrowser
import io.github.wulkanowy.utils.toFormattedString
import io.github.wulkanowy.utils.toLocalDateTime
import io.github.wulkanowy.utils.*
import java.time.Instant
import javax.inject.Inject
@AndroidEntryPoint
@ -38,7 +33,7 @@ class AboutFragment : BaseFragment<FragmentAboutBinding>(R.layout.fragment_about
override val versionRes: Triple<String, String, Drawable?>?
get() = context?.run {
val buildTimestamp =
appInfo.buildTimestamp.toLocalDateTime().toFormattedString("yyyy-MM-dd")
Instant.ofEpochMilli(appInfo.buildTimestamp).toFormattedString("yyyy-MM-dd")
val versionSignature =
"${appInfo.versionName}-${appInfo.buildFlavor} (${appInfo.versionCode}), $buildTimestamp"
Triple(

View File

@ -37,9 +37,7 @@ import io.github.wulkanowy.utils.left
import io.github.wulkanowy.utils.nickOrName
import io.github.wulkanowy.utils.toFormattedString
import timber.log.Timber
import java.time.Duration
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.*
import java.util.Timer
import javax.inject.Inject
import kotlin.concurrent.timer
@ -291,7 +289,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
var dateToNavigate = LocalDate.now()
fun updateLessonState() {
val currentDateTime = LocalDateTime.now()
val currentDateTime = Instant.now()
val currentDate = LocalDate.now()
val tomorrowDate = currentDate.plusDays(1)
@ -361,7 +359,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
binding: ItemDashboardLessonsBinding,
header: TimetableHeader? = null,
) {
val currentDateTime = LocalDateTime.now()
val currentDateTime = Instant.now()
val nextLessons = timetableToShow.filter { it.end.isAfter(currentDateTime) }
.sortedBy { it.start }
@ -386,7 +384,7 @@ class DashboardAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
private fun updateFirstLessonView(
binding: ItemDashboardLessonsBinding,
firstLesson: Timetable?,
currentDateTime: LocalDateTime
currentDateTime: Instant
) {
val context = binding.root.context
val sansSerifFont = Typeface.create("sans-serif", Typeface.NORMAL)

View File

@ -15,8 +15,8 @@ import io.github.wulkanowy.utils.nextOrSameSchoolDay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import javax.inject.Inject
class DashboardPresenter @Inject constructor(
@ -532,7 +532,7 @@ class DashboardPresenter @Inject constructor(
student = student,
semester = semester,
forceRefresh = forceRefresh,
startDate = LocalDateTime.now()
startDate = Instant.now(),
)
}.onEach {
when (it.status) {

View File

@ -1,7 +1,8 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.Conference
import java.time.LocalDateTime
import java.time.Duration
import java.time.Instant
val debugConferenceItems = listOf(
generateConference(
@ -53,6 +54,6 @@ private fun generateConference(title: String, subject: String) = Conference(
diaryId = 0,
agenda = "",
conferenceId = 0,
date = LocalDateTime.now().plusMinutes(10),
date = Instant.now().plus(Duration.ofMinutes(10)),
presentOnConference = "",
)

View File

@ -1,7 +1,7 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.Message
import java.time.LocalDateTime
import java.time.Instant
val debugMessageItems = listOf(
generateMessage("Kowalski Jan", "Tytuł"),
@ -24,7 +24,7 @@ private fun generateMessage(sender: String, subject: String) = Message(
messageId = 0,
senderId = 0,
recipient = "",
date = LocalDateTime.now(),
date = Instant.now(),
folderId = 0,
unread = true,
removed = false,

View File

@ -1,8 +1,9 @@
package io.github.wulkanowy.ui.modules.debug.notification.mock
import io.github.wulkanowy.data.db.entities.Timetable
import java.time.Duration
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import kotlin.random.Random
val debugTimetableItems = listOf(
@ -24,8 +25,8 @@ private fun generateTimetable(subject: String, room: String, roomOld: String) =
diaryId = 0,
date = LocalDate.now().minusDays(Random.nextLong(0, 8)),
number = 1,
start = LocalDateTime.now().plusHours(1),
end = LocalDateTime.now(),
start = Instant.now(),
end = Instant.now().plus(Duration.ofHours(1)),
subjectOld = "",
group = "",
room = room,

View File

@ -19,7 +19,8 @@ import io.github.wulkanowy.utils.AnalyticsHelper
import io.github.wulkanowy.utils.flowWithResource
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import java.time.LocalDate
import java.time.Duration
import java.time.Instant
import javax.inject.Inject
class MainPresenter @Inject constructor(
@ -158,11 +159,11 @@ class MainPresenter @Inject constructor(
prefRepository.inAppReviewCount++
if (prefRepository.inAppReviewDate == null) {
prefRepository.inAppReviewDate = LocalDate.now()
prefRepository.inAppReviewDate = Instant.now()
}
if (!prefRepository.isAppReviewDone && prefRepository.inAppReviewCount >= 50 &&
LocalDate.now().minusDays(14).isAfter(prefRepository.inAppReviewDate)
Instant.now().minus(Duration.ofDays(14)).isAfter(prefRepository.inAppReviewDate)
) {
view?.showInAppReview()
prefRepository.isAppReviewDone = true

View File

@ -79,9 +79,7 @@ class SyncPresenter @Inject constructor(
}
private fun setSyncDateInView() {
val lastSyncDate = preferencesRepository.lasSyncDate
if (lastSyncDate.year == 1970) return
val lastSyncDate = preferencesRepository.lasSyncDate ?: return
view?.setLastSyncDate(lastSyncDate.toFormattedString("dd.MM.yyyy HH:mm:ss"))
}

View File

@ -15,15 +15,10 @@ import io.github.wulkanowy.data.db.entities.Timetable
import io.github.wulkanowy.data.enums.TimetableMode
import io.github.wulkanowy.databinding.ItemTimetableBinding
import io.github.wulkanowy.databinding.ItemTimetableSmallBinding
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.isJustFinished
import io.github.wulkanowy.utils.isShowTimeUntil
import io.github.wulkanowy.utils.left
import io.github.wulkanowy.utils.toFormattedString
import io.github.wulkanowy.utils.until
import io.github.wulkanowy.utils.*
import timber.log.Timber
import java.time.LocalDateTime
import java.util.Timer
import java.time.Instant
import java.util.*
import javax.inject.Inject
import kotlin.concurrent.timer
@ -167,7 +162,7 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
}
}
private fun getPreviousLesson(position: Int): LocalDateTime? {
private fun getPreviousLesson(position: Int): Instant? {
return items.filter { it.isStudentPlan }
.getOrNull(position - 1 - items.filterIndexed { i, item -> i < position && !item.isStudentPlan }.size)
?.let {

View File

@ -16,7 +16,7 @@ import io.github.wulkanowy.utils.capitalise
import io.github.wulkanowy.utils.getThemeAttrColor
import io.github.wulkanowy.utils.lifecycleAwareVariable
import io.github.wulkanowy.utils.toFormattedString
import java.time.LocalDateTime
import java.time.Instant
class TimetableDialog : DialogFragment() {
@ -192,7 +192,7 @@ class TimetableDialog : DialogFragment() {
}
@SuppressLint("SetTextI18n")
private fun setTime(start: LocalDateTime, end: LocalDateTime) {
private fun setTime(start: Instant, end: Instant) {
binding.timetableDialogTimeValue.text =
"${start.toFormattedString("HH:mm")} - ${end.toFormattedString("HH:mm")}"
}

View File

@ -10,11 +10,9 @@ import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
import io.github.wulkanowy.utils.toLocalDate
import kotlinx.coroutines.launch
import timber.log.Timber
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.*
import java.time.temporal.ChronoUnit
import java.util.UUID
import java.util.*
import javax.inject.Inject
class AdditionalLessonAddPresenter @Inject constructor(
@ -138,8 +136,8 @@ class AdditionalLessonAddPresenter @Inject constructor(
TimetableAdditional(
studentId = semester.studentId,
diaryId = semester.diaryId,
start = LocalDateTime.of(date, start),
end = LocalDateTime.of(date, end),
start = ZonedDateTime.of(date, start, ZoneId.systemDefault()).toInstant(),
end = ZonedDateTime.of(date, end, ZoneId.systemDefault()).toInstant(),
date = date.plusWeeks(it),
subject = subject
).apply {

View File

@ -29,7 +29,6 @@ import io.github.wulkanowy.utils.toFormattedString
import kotlinx.coroutines.runBlocking
import timber.log.Timber
import java.time.LocalDate
import java.time.ZoneOffset
class TimetableWidgetFactory(
private val timetableRepository: TimetableRepository,
@ -77,7 +76,7 @@ class TimetableWidgetFactory(
if (date == LocalDate.now() && todayLastLessonEndTimestamp != null) {
sharedPref.putLong(
key = getTodayLastLessonEndDateTimeWidgetKey(appWidgetId),
value = todayLastLessonEndTimestamp.toEpochSecond(ZoneOffset.UTC),
value = todayLastLessonEndTimestamp.epochSecond,
sync = true
)
}

View File

@ -5,7 +5,6 @@ import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.MaterialDatePicker
import kotlinx.parcelize.Parcelize
import java.time.LocalDate
import java.time.ZoneOffset
import java.time.temporal.ChronoUnit
fun Fragment.openMaterialDatePicker(
@ -16,17 +15,17 @@ fun Fragment.openMaterialDatePicker(
) {
val constraintsBuilder = CalendarConstraints.Builder().apply {
setValidator(CalendarDayRangeValidator(rangeStart, rangeEnd))
setStart(rangeStart.toTimestamp(ZoneOffset.UTC))
setEnd(rangeEnd.toTimestamp(ZoneOffset.UTC))
setStart(rangeStart.toTimestamp())
setEnd(rangeEnd.toTimestamp())
}
val datePicker = MaterialDatePicker.Builder.datePicker()
.setCalendarConstraints(constraintsBuilder.build())
.setSelection(selected.toTimestamp(ZoneOffset.UTC))
.setSelection(selected.toTimestamp())
.build()
datePicker.addOnPositiveButtonClickListener {
val date = it.toLocalDateTime(ZoneOffset.UTC).toLocalDate()
val date = it.toLocalDateTime().toLocalDate()
onDateSelected(date)
}

View File

@ -8,8 +8,9 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.enums.MessageFolder
import timber.log.Timber
import java.time.Duration.ofMinutes
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import javax.inject.Inject
fun getRefreshKey(name: String, semester: Semester, start: LocalDate, end: LocalDate): String {
@ -34,10 +35,10 @@ class AutoRefreshHelper @Inject constructor(
) {
fun shouldBeRefreshed(key: String): Boolean {
val timestamp = sharedPref.getLong(key, 0).toLocalDateTime()
val timestamp = sharedPref.getLong(key, 0).let(Instant::ofEpochMilli)
val servicesInterval = sharedPref.getString(context.getString(R.string.pref_key_services_interval), context.getString(R.string.pref_default_services_interval)).toLong()
val shouldBeRefreshed = timestamp < LocalDateTime.now().minusMinutes(servicesInterval)
val shouldBeRefreshed = timestamp < Instant.now().minus(ofMinutes(servicesInterval))
Timber.d("Check if $key need to be refreshed: $shouldBeRefreshed (last refresh: $timestamp, interval: $servicesInterval min)")
@ -45,6 +46,6 @@ class AutoRefreshHelper @Inject constructor(
}
fun updateLastRefreshTimestamp(key: String) {
sharedPref.putLong(key, LocalDateTime.now().toTimestamp())
sharedPref.putLong(key, Instant.now().toEpochMilli())
}
}

View File

@ -12,12 +12,17 @@ import io.github.wulkanowy.sdk.scrapper.login.PasswordChangeRequiredException
import okhttp3.internal.http2.StreamResetException
import java.io.InterruptedIOException
import java.net.ConnectException
import java.net.SocketException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
fun Resources.getString(error: Throwable) = when (error) {
is UnknownHostException -> getString(R.string.error_no_internet)
is SocketTimeoutException, is InterruptedIOException, is ConnectException, is StreamResetException -> getString(R.string.error_timeout)
is SocketException,
is SocketTimeoutException,
is InterruptedIOException,
is ConnectException,
is StreamResetException -> getString(R.string.error_timeout)
is NotLoggedInException -> getString(R.string.error_login_failed)
is PasswordChangeRequiredException -> getString(R.string.error_password_change_required)
is ServiceUnavailableException -> getString(R.string.error_service_unavailable)

View File

@ -1,40 +1,34 @@
package io.github.wulkanowy.utils
import java.text.SimpleDateFormat
import java.time.DayOfWeek.FRIDAY
import java.time.DayOfWeek.MONDAY
import java.time.DayOfWeek.SATURDAY
import java.time.DayOfWeek.SUNDAY
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.Month
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.*
import java.time.DayOfWeek.*
import java.time.format.DateTimeFormatter
import java.time.temporal.TemporalAdjusters.firstInMonth
import java.time.temporal.TemporalAdjusters.next
import java.time.temporal.TemporalAdjusters.previous
import java.util.Locale
import java.time.temporal.TemporalAdjusters.*
import java.util.*
private const val DEFAULT_DATE_PATTERN = "dd.MM.yyyy"
fun LocalDate.toTimestamp(): Long = atStartOfDay()
.toInstant(ZoneOffset.UTC)
.toEpochMilli()
fun Long.toLocalDateTime(): LocalDateTime = LocalDateTime.ofInstant(
Instant.ofEpochMilli(this), ZoneOffset.UTC
)
fun Instant.toLocalDate(): LocalDate = atZone(ZoneOffset.UTC).toLocalDate()
fun String.toLocalDate(format: String = DEFAULT_DATE_PATTERN): LocalDate =
LocalDate.parse(this, DateTimeFormatter.ofPattern(format))
fun LocalDateTime.toTimestamp(tz: ZoneId = ZoneId.systemDefault()) =
atZone(tz).withZoneSameInstant(ZoneOffset.UTC).toInstant().toEpochMilli()
fun Long.toLocalDateTime(tz: ZoneId = ZoneId.systemDefault()): LocalDateTime =
LocalDateTime.ofInstant(Instant.ofEpochMilli(this), tz)
fun LocalDate.toTimestamp(tz: ZoneId = ZoneId.systemDefault()) = atStartOfDay().toTimestamp(tz)
fun LocalDate.toFormattedString(pattern: String = DEFAULT_DATE_PATTERN): String =
format(DateTimeFormatter.ofPattern(pattern))
fun LocalDateTime.toFormattedString(pattern: String = DEFAULT_DATE_PATTERN): String =
format(DateTimeFormatter.ofPattern(pattern))
fun Instant.toFormattedString(
pattern: String = DEFAULT_DATE_PATTERN,
tz: ZoneId = ZoneId.systemDefault()
): String = atZone(tz).format(DateTimeFormatter.ofPattern(pattern))
fun Month.getFormattedName(): String {
val formatter = SimpleDateFormat("LLLL", Locale.getDefault())

View File

@ -3,10 +3,10 @@ package io.github.wulkanowy.utils
import io.github.wulkanowy.data.db.entities.Timetable
import java.time.Duration
import java.time.Duration.between
import java.time.LocalDateTime
import java.time.LocalDateTime.now
import java.time.Instant
import java.time.Instant.now
fun Timetable.isShowTimeUntil(previousLessonEnd: LocalDateTime?) = when {
fun Timetable.isShowTimeUntil(previousLessonEnd: Instant?) = when {
!isStudentPlan -> false
canceled -> false
now().isAfter(start) -> false

View File

@ -4,7 +4,7 @@ import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.sdk.Sdk
import java.time.LocalDate
import java.time.LocalDateTime.now
import java.time.Instant.now
import io.github.wulkanowy.sdk.pojo.Semester as SdkSemester
fun getSemesterEntity(diaryId: Int = 1, semesterId: Int = 1, start: LocalDate = LocalDate.now(), end: LocalDate = LocalDate.now(), semesterName: Int = 1) = Semester(

View File

@ -153,23 +153,25 @@ class Migration13Test : AbstractMigrationTest() {
private fun getSemesters(db: SupportSQLiteDatabase, query: String): List<Pair<Semester, Boolean>> {
val semesters = mutableListOf<Pair<Semester, Boolean>>()
val cursor = db.query(query)
if (cursor.moveToFirst()) {
db.query(query).use {
if (it.moveToFirst()) {
do {
semesters.add(Semester(
studentId = cursor.getInt(1),
diaryId = cursor.getInt(2),
diaryName = cursor.getString(3),
semesterId = cursor.getInt(4),
semesterName = cursor.getInt(5),
classId = cursor.getInt(7),
unitId = cursor.getInt(8),
schoolYear = cursor.getInt(9),
start = Converters().timestampToDate(cursor.getLong(10))!!,
end = Converters().timestampToDate(cursor.getLong(11))!!
) to (cursor.getInt(6) == 1))
} while (cursor.moveToNext())
studentId = it.getInt(1),
diaryId = it.getInt(2),
diaryName = it.getString(3),
semesterId = it.getInt(4),
semesterName = it.getInt(5),
classId = it.getInt(7),
unitId = it.getInt(8),
schoolYear = it.getInt(9),
start = Converters().timestampToLocalDate(it.getLong(10))!!,
end = Converters().timestampToLocalDate(it.getLong(11))!!
) to (it.getInt(6) == 1))
} while (it.moveToNext())
}
}
return semesters.toList()
}

View File

@ -8,23 +8,17 @@ import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.toFirstResult
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import java.time.LocalDate
import java.time.LocalDate.of
import java.time.ZoneOffset
import io.github.wulkanowy.sdk.pojo.Grade as SdkGrade
class GradeRepositoryTest {
@ -57,7 +51,11 @@ class GradeRepositoryTest {
coEvery { gradeDb.deleteAll(any()) } just Runs
coEvery { gradeDb.insertAll(any()) } returns listOf()
coEvery { gradeSummaryDb.loadAll(1, 1) } returnsMany listOf(flowOf(listOf()), flowOf(listOf()), flowOf(listOf()))
coEvery { gradeSummaryDb.loadAll(1, 1) } returnsMany listOf(
flowOf(listOf()),
flowOf(listOf()),
flowOf(listOf())
)
coEvery { gradeSummaryDb.deleteAll(any()) } just Runs
coEvery { gradeSummaryDb.insertAll(any()) } returns listOf()
}
@ -65,7 +63,7 @@ class GradeRepositoryTest {
@Test
fun `mark grades older than registration date as read`() {
// prepare
val boundaryDate = of(2019, 2, 27).atStartOfDay()
val boundaryDate = of(2019, 2, 27).atStartOfDay().toInstant(ZoneOffset.UTC)
val remoteList = listOf(
createGradeApi(5, 4.0, of(2019, 2, 25), "Ocena pojawiła się"),
createGradeApi(5, 4.0, of(2019, 2, 26), "przed zalogowanie w aplikacji"),
@ -81,7 +79,13 @@ class GradeRepositoryTest {
)
// execute
val res = runBlocking { gradeRepository.getGrades(student.copy(registrationDate = boundaryDate), semester, true).toFirstResult() }
val res = runBlocking {
gradeRepository.getGrades(
student = student.copy(registrationDate = boundaryDate),
semester = semester,
forceRefresh = true
).toFirstResult()
}
// verify
assertEquals(null, res.error)
@ -101,9 +105,19 @@ class GradeRepositoryTest {
fun `mitigate mark grades as unread when old grades changed`() {
// prepare
val remoteList = listOf(
createGradeApi(5, 2.0, of(2019, 2, 25), "Ocena ma datę, jest inna, ale nie zostanie powiadomiona"),
createGradeApi(
5,
2.0,
of(2019, 2, 25),
"Ocena ma datę, jest inna, ale nie zostanie powiadomiona"
),
createGradeApi(4, 3.0, of(2019, 2, 26), "starszą niż ostatnia lokalnie"),
createGradeApi(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie"),
createGradeApi(
3,
4.0,
of(2019, 2, 27),
"Ta jest z tego samego dnia co ostatnia lokalnie"
),
createGradeApi(2, 5.0, of(2019, 2, 28), "Ta jest już w ogóle nowa")
)
coEvery { sdk.getGrades(1) } returns (remoteList to emptyList())
@ -111,7 +125,12 @@ class GradeRepositoryTest {
val localList = listOf(
createGradeApi(5, 3.0, of(2019, 2, 25), "Jedna ocena"),
createGradeApi(4, 4.0, of(2019, 2, 26), "Druga"),
createGradeApi(3, 4.0, of(2019, 2, 27), "Ta jest z tego samego dnia co ostatnia lokalnie")
createGradeApi(
3,
4.0,
of(2019, 2, 27),
"Ta jest z tego samego dnia co ostatnia lokalnie"
)
)
coEvery { gradeDb.loadAll(1, 1) } returnsMany listOf(
flowOf(localList.mapToEntities(semester)),
@ -248,7 +267,8 @@ class GradeRepositoryTest {
assertEquals(0, res.data?.first?.size)
}
private fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String) = SdkGrade(
private fun createGradeApi(value: Int, weight: Double, date: LocalDate, desc: String) =
SdkGrade(
subject = "",
color = "",
comment = "",

View File

@ -16,27 +16,25 @@ import io.github.wulkanowy.sdk.pojo.MessageDetails
import io.github.wulkanowy.sdk.pojo.Sender
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.toFirstResult
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.checkEquals
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.Json
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import java.net.UnknownHostException
import java.time.LocalDateTime
import java.time.Instant
import java.time.ZoneOffset
import kotlin.test.assertTrue
@OptIn(ExperimentalCoroutinesApi::class)
class MessageRepositoryTest {
@SpyK
@ -80,7 +78,7 @@ class MessageRepositoryTest {
}
@Test
fun `get messages when read by values was changed on already read message`() = runBlocking {
fun `get messages when read by values was changed on already read message`() = runTest {
every { messageDb.loadAll(any(), any()) } returns flow {
val dbMessage = getMessageEntity(3, "", false).apply {
unreadBy = 10
@ -239,7 +237,7 @@ class MessageRepositoryTest {
senderId = 0,
recipient = "Wielu adresatów",
subject = "",
date = LocalDateTime.MAX,
date = Instant.EPOCH,
folderId = 1,
unread = unread,
removed = false,
@ -261,7 +259,8 @@ class MessageRepositoryTest {
recipients = listOf(),
subject = "",
content = content,
date = LocalDateTime.MAX,
date = Instant.EPOCH.atZone(ZoneOffset.UTC).toLocalDateTime(),
dateZoned = Instant.EPOCH.atZone(ZoneOffset.UTC),
folderId = 1,
unread = unread,
unreadBy = 0,

View File

@ -22,6 +22,7 @@ import org.junit.Assert
import org.junit.Before
import org.junit.Test
import java.time.LocalDateTime.of
import java.time.ZoneId
class MobileDeviceRepositoryTest {
@ -137,6 +138,8 @@ class MobileDeviceRepositoryTest {
name = "",
deviceId = "",
createDate = of(2019, 5, day, 0, 0, 0),
modificationDate = of(2019, 5, day, 0, 0, 0)
modificationDate = of(2019, 5, day, 0, 0, 0),
createDateZoned = of(2019, 5, day, 0, 0, 0).atZone(ZoneId.systemDefault()),
modificationDateZoned = of(2019, 5, day, 0, 0, 0).atZone(ZoneId.systemDefault())
)
}

View File

@ -27,6 +27,7 @@ import org.junit.Test
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalDateTime.of
import java.time.ZoneId
import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetable
class TimetableRepositoryTest {
@ -107,6 +108,8 @@ class TimetableRepositoryTest {
number = number,
start = start,
end = start.plusMinutes(45),
startZoned = start.atZone(ZoneId.systemDefault()),
endZoned = start.plusMinutes(45).atZone(ZoneId.systemDefault()),
date = start.toLocalDate(),
subject = subject,
group = "",

View File

@ -23,9 +23,9 @@ import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import java.time.Instant
import java.time.LocalDate.now
import java.time.LocalDate.of
import java.time.LocalDateTime
class GradeAverageProviderTest {
@ -63,7 +63,7 @@ class GradeAverageProviderTest {
className = "",
classId = 1,
isCurrent = true,
registrationDate = LocalDateTime.now()
registrationDate = Instant.now()
)
private val semesters = mutableListOf(

View File

@ -6,18 +6,13 @@ import io.github.wulkanowy.data.db.entities.StudentWithSemesters
import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.coEvery
import io.mockk.every
import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.just
import io.mockk.verify
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import java.io.IOException
import java.time.LocalDateTime.now
import java.time.Instant
class LoginFormPresenterTest {
@ -121,7 +116,7 @@ class LoginFormPresenterTest {
classId = 1,
isCurrent = false,
symbol = "",
registrationDate = now(),
registrationDate = Instant.now(),
className = "",
mobileBaseUrl = "",
privateKey = "",

View File

@ -7,18 +7,12 @@ import io.github.wulkanowy.data.repositories.StudentRepository
import io.github.wulkanowy.services.sync.SyncManager
import io.github.wulkanowy.ui.modules.login.LoginErrorHandler
import io.github.wulkanowy.utils.AnalyticsHelper
import io.mockk.MockKAnnotations
import io.mockk.Runs
import io.mockk.clearMocks
import io.mockk.coEvery
import io.mockk.every
import io.mockk.*
import io.mockk.impl.annotations.MockK
import io.mockk.just
import io.mockk.verify
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import java.time.LocalDateTime.now
import java.time.Instant
class LoginStudentSelectPresenterTest {
@ -55,7 +49,7 @@ class LoginStudentSelectPresenterTest {
schoolSymbol = "",
classId = 1,
studentName = "",
registrationDate = now(),
registrationDate = Instant.now(),
className = "",
loginMode = "",
certificateKey = "",

View File

@ -1,14 +1,13 @@
package io.github.wulkanowy.utils
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Assert.*
import org.junit.Test
import java.time.Instant
import java.time.LocalDate.of
import java.time.LocalDateTime
import java.time.Month.JANUARY
import java.time.ZoneOffset
import java.util.Locale
import java.util.*
class TimeExtensionTest {
@ -24,11 +23,15 @@ class TimeExtensionTest {
}
@Test
fun toFormattedStringLocalDateTimeTest() {
assertEquals("01.10.2018", LocalDateTime.of(2018, 10, 1, 10, 0, 0).toFormattedString())
fun toFormattedStringFromInstantTest() {
assertEquals(
"01.10.2018",
LocalDateTime.of(2018, 10, 1, 10, 0, 0).toInstant(ZoneOffset.UTC).toFormattedString()
)
assertEquals(
"2018-10-01 10:00:00",
LocalDateTime.of(2018, 10, 1, 10, 0, 0).toFormattedString("uuuu-MM-dd HH:mm:ss")
LocalDateTime.of(2018, 10, 1, 10, 0, 0).toInstant(ZoneOffset.UTC)
.toFormattedString("uuuu-MM-dd HH:mm:ss", ZoneOffset.UTC)
)
}
@ -228,16 +231,23 @@ class TimeExtensionTest {
}
@Test
fun getLocalDateToTimestampUTC() {
assertEquals(0L, of(1970, 1, 1).toTimestamp(ZoneOffset.UTC))
assertEquals(946684800000L, of(2000, 1, 1).toTimestamp(ZoneOffset.UTC))
assertEquals(1640131200000L, of(2021, 12, 22).toTimestamp(ZoneOffset.UTC))
fun getLocalDateToTimestamp() {
assertEquals(0L, of(1970, 1, 1).toTimestamp())
assertEquals(946684800000L, of(2000, 1, 1).toTimestamp())
assertEquals(1640131200000L, of(2021, 12, 22).toTimestamp())
}
@Test
fun getLocalDateTimeToUtcTimestamp() {
assertEquals(0L, LocalDateTime.of(1970, 1, 1, 0, 0, 0).toTimestamp(ZoneOffset.UTC))
assertEquals(946684800000L, LocalDateTime.of(2000, 1, 1, 0, 0, 0).toTimestamp(ZoneOffset.UTC))
assertEquals(1640131200000L, LocalDateTime.of(2021, 12, 22, 0, 0, 0).toTimestamp(ZoneOffset.UTC))
fun getLocalDateFromInstant() {
assertEquals(of(1970, 1, 1), Instant.ofEpochMilli(0).toLocalDate())
assertEquals(of(2000, 1, 1), Instant.ofEpochMilli(946684800000).toLocalDate())
assertEquals(of(2021, 12, 22), Instant.ofEpochMilli(1640131200000L).toLocalDate())
}
@Test
fun timestampToLocalDateTime() {
assertEquals(LocalDateTime.of(1970, 1, 1, 0, 0, 0), 0L.toLocalDateTime())
assertEquals(LocalDateTime.of(2000, 1, 1, 0, 0, 0), 946684800000.toLocalDateTime())
assertEquals(LocalDateTime.of(2021, 12, 22, 0, 0, 0), 1640131200000L.toLocalDateTime())
}
}

View File

@ -6,9 +6,8 @@ import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalDateTime.now
import java.time.*
import java.time.Duration.ofMinutes
class TimetableExtensionTest {
@ -17,52 +16,38 @@ class TimetableExtensionTest {
assertFalse(getTimetableEntity().isShowTimeUntil(null))
assertFalse(getTimetableEntity(isStudentPlan = false).isShowTimeUntil(null))
assertFalse(getTimetableEntity(isStudentPlan = true, canceled = true).isShowTimeUntil(null))
assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = now().minusSeconds(1)).isShowTimeUntil(null))
assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = now().plusMinutes(5)).isShowTimeUntil(now().plusMinutes(5)))
assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = now().plusMinutes(61)).isShowTimeUntil(now().minusMinutes(5)))
assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = Instant.now().minusSeconds(1)).isShowTimeUntil(null))
assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = Instant.now().plus(ofMinutes(5))).isShowTimeUntil(Instant.now().plus(ofMinutes(5))))
assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = Instant.now().plus(ofMinutes(61))).isShowTimeUntil(Instant.now().minus(ofMinutes(5))))
assertTrue(getTimetableEntity(isStudentPlan = true, canceled = false, start = now().plusMinutes(60)).isShowTimeUntil(now().minusMinutes(5)))
assertTrue(getTimetableEntity(isStudentPlan = true, canceled = false, start = now().plusMinutes(60)).isShowTimeUntil(null))
assertTrue(getTimetableEntity(isStudentPlan = true, canceled = false, start = Instant.now().plus(ofMinutes(60))).isShowTimeUntil(Instant.now().minus(ofMinutes(5))))
assertTrue(getTimetableEntity(isStudentPlan = true, canceled = false, start = Instant.now().plus(ofMinutes(60))).isShowTimeUntil(null))
assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = now().minusSeconds(1)).isShowTimeUntil(null))
assertFalse(getTimetableEntity(isStudentPlan = true, canceled = false, start = Instant.now().minusSeconds(1)).isShowTimeUntil(null))
}
@Test
fun getLeft() {
assertEquals(null, getTimetableEntity(canceled = true).left)
assertEquals(null, getTimetableEntity(start = now().plusMinutes(5), end = now().plusMinutes(50)).left)
assertEquals(null, getTimetableEntity(start = now().minusMinutes(1), end = now().plusMinutes(44), isStudentPlan = false).left)
assertNotEquals(
null,
getTimetableEntity(
start = now().minusMinutes(1),
end = now().plusMinutes(44),
isStudentPlan = true
).left
)
assertNotEquals(
null,
getTimetableEntity(
start = now(),
end = now().plusMinutes(45),
isStudentPlan = true
).left
)
assertEquals(null, getTimetableEntity(start = Instant.now().plus(ofMinutes(5)), end = Instant.now().plus(ofMinutes(50))).left)
assertEquals(null, getTimetableEntity(start = Instant.now().minus(ofMinutes(1)), end = Instant.now().plus(ofMinutes(44)), isStudentPlan = false).left)
assertNotEquals(null, getTimetableEntity(start = Instant.now().minus(ofMinutes(1)), end = Instant.now().plus(ofMinutes(44)), isStudentPlan = true).left)
assertNotEquals(null, getTimetableEntity(start = Instant.now(), end = Instant.now().plus(ofMinutes(45)), isStudentPlan = true).left)
}
@Test
fun isJustFinished() {
assertFalse(getTimetableEntity(end = now().minusSeconds(16)).isJustFinished)
assertTrue(getTimetableEntity(end = now().minusSeconds(14)).isJustFinished)
assertTrue(getTimetableEntity(end = now().minusSeconds(1)).isJustFinished)
assertFalse(getTimetableEntity(end = now().plusSeconds(1)).isJustFinished)
assertFalse(getTimetableEntity(end = Instant.now().minusSeconds(16)).isJustFinished)
assertTrue(getTimetableEntity(end = Instant.now().minusSeconds(14)).isJustFinished)
assertTrue(getTimetableEntity(end = Instant.now().minusSeconds(1)).isJustFinished)
assertFalse(getTimetableEntity(end = Instant.now().plusSeconds(1)).isJustFinished)
}
private fun getTimetableEntity(
isStudentPlan: Boolean = false,
canceled: Boolean = false,
start: LocalDateTime = now(),
end: LocalDateTime = now()
start: Instant = Instant.now(),
end: Instant = Instant.now()
) = Timetable(
studentId = 0,
subject = "",