diff --git a/.travis.yml b/.travis.yml index 1c1038b8..eadf43fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,7 +14,7 @@ cache: branches: only: - develop - - 0.20.3 + - 0.20.4 android: licenses: diff --git a/app/build.gradle b/app/build.gradle index 2d608f97..d9936480 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,8 +18,8 @@ android { testApplicationId "io.github.tests.wulkanowy" minSdkVersion 17 targetSdkVersion 29 - versionCode 67 - versionName "0.20.3" + versionCode 68 + versionName "0.20.4" multiDexEnabled true resValue "string", "app_name", "Wulkanowy" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -126,7 +126,7 @@ configurations.all { } dependencies { - implementation "io.github.wulkanowy:sdk:0.20.3" + implementation "io.github.wulkanowy:sdk:0.20.4" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' @@ -147,7 +147,7 @@ dependencies { implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation "androidx.constraintlayout:constraintlayout:2.0.1" implementation "androidx.coordinatorlayout:coordinatorlayout:1.1.0" - implementation "com.google.android.material:material:1.2.0" + implementation "com.google.android.material:material:1.2.1" implementation "com.github.wulkanowy:material-chips-input:2.1.1" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation "me.zhanghai.android.materialprogressbar:library:1.6.1" @@ -176,7 +176,7 @@ dependencies { implementation "fr.bipi.treessence:treessence:0.3.2" implementation "com.mikepenz:aboutlibraries-core:$about_libraries" implementation 'com.wdullaer:materialdatetimepicker:4.2.3' - implementation "io.coil-kt:coil:1.0.0-rc1" + implementation "io.coil-kt:coil:1.0.0-rc2" implementation "io.github.wulkanowy:AppKillerManager:3.0.0" implementation 'me.xdrop:fuzzywuzzy:1.3.1' diff --git a/app/src/main/java/io/github/wulkanowy/data/Resource.kt b/app/src/main/java/io/github/wulkanowy/data/Resource.kt index ae22832a..406440c8 100644 --- a/app/src/main/java/io/github/wulkanowy/data/Resource.kt +++ b/app/src/main/java/io/github/wulkanowy/data/Resource.kt @@ -1,6 +1,6 @@ package io.github.wulkanowy.data -data class Resource(val status: Status, val data: T?, val error: Throwable?) { +data class Resource(val status: Status, val data: T?, val error: Throwable?) { companion object { fun success(data: T?): Resource { return Resource(Status.SUCCESS, data, null) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt index e7f115ac..3f4591a2 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/exam/ExamRepository.kt @@ -2,9 +2,9 @@ package io.github.wulkanowy.data.repositories.exam import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student -import io.github.wulkanowy.utils.monday +import io.github.wulkanowy.utils.endExamsDay import io.github.wulkanowy.utils.networkBoundResource -import io.github.wulkanowy.utils.sunday +import io.github.wulkanowy.utils.startExamsDay import io.github.wulkanowy.utils.uniqueSubtract import java.time.LocalDate import javax.inject.Inject @@ -18,8 +18,8 @@ class ExamRepository @Inject constructor( fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource( shouldFetch = { it.isEmpty() || forceRefresh }, - query = { local.getExams(semester, start.monday, end.sunday) }, - fetch = { remote.getExams(student, semester, start.monday, end.sunday) }, + query = { local.getExams(semester, start.startExamsDay, start.endExamsDay) }, + fetch = { remote.getExams(student, semester, start.startExamsDay, start.endExamsDay) }, saveFetchResult = { old, new -> local.deleteExams(old uniqueSubtract new) local.saveExams(new uniqueSubtract old) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt index 7a76503f..2748f1df 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/semester/SemesterRepository.kt @@ -8,6 +8,7 @@ import io.github.wulkanowy.utils.getCurrentOrLast import io.github.wulkanowy.utils.isCurrent import io.github.wulkanowy.utils.uniqueSubtract import kotlinx.coroutines.withContext +import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -41,7 +42,7 @@ class SemesterRepository @Inject constructor( private suspend fun refreshSemesters(student: Student) { val new = remote.getSemesters(student) - if (new.isEmpty()) throw IllegalArgumentException("Empty semester list!") + if (new.isEmpty()) return Timber.i("Empty semester list!") val old = local.getSemesters(student) local.deleteSemesters(old.uniqueSubtract(new)) diff --git a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt index 8f8782a2..5c4b916b 100644 --- a/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt +++ b/app/src/main/java/io/github/wulkanowy/services/alarm/TimetableNotificationSchedulerHelper.kt @@ -26,7 +26,9 @@ import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companio import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_ID import io.github.wulkanowy.services.alarm.TimetableNotificationReceiver.Companion.STUDENT_NAME import io.github.wulkanowy.ui.modules.main.MainView +import io.github.wulkanowy.utils.DispatchersProvider import io.github.wulkanowy.utils.toTimestamp +import kotlinx.coroutines.withContext import timber.log.Timber import java.time.LocalDateTime import java.time.LocalDateTime.now @@ -35,7 +37,8 @@ import javax.inject.Inject class TimetableNotificationSchedulerHelper @Inject constructor( @ApplicationContext private val context: Context, private val alarmManager: AlarmManager, - private val preferencesRepository: PreferencesRepository + private val preferencesRepository: PreferencesRepository, + private val dispatchersProvider: DispatchersProvider, ) { private fun getRequestCode(time: LocalDateTime, studentId: Int) = (time.toTimestamp() * studentId).toInt() @@ -44,13 +47,15 @@ class TimetableNotificationSchedulerHelper @Inject constructor( return day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30) } - fun cancelScheduled(lessons: List, studentId: Int = 1) { - lessons.sortedBy { it.start }.forEachIndexed { index, lesson -> - val upcomingTime = getUpcomingLessonTime(index, lessons, lesson) - cancelScheduledTo(upcomingTime..lesson.start, getRequestCode(upcomingTime, studentId)) - cancelScheduledTo(lesson.start..lesson.end, getRequestCode(lesson.start, studentId)) + suspend fun cancelScheduled(lessons: List, studentId: Int = 1) { + withContext(dispatchersProvider.backgroundThread) { + lessons.sortedBy { it.start }.forEachIndexed { index, lesson -> + val upcomingTime = getUpcomingLessonTime(index, lessons, lesson) + cancelScheduledTo(upcomingTime..lesson.start, getRequestCode(upcomingTime, studentId)) + cancelScheduledTo(lesson.start..lesson.end, getRequestCode(lesson.start, studentId)) - Timber.d("TimetableNotification canceled: type 1 & 2, subject: ${lesson.subject}, start: ${lesson.start}, student: $studentId") + Timber.d("TimetableNotification canceled: type 1 & 2, subject: ${lesson.subject}, start: ${lesson.start}, student: $studentId") + } } } @@ -61,28 +66,30 @@ class TimetableNotificationSchedulerHelper @Inject constructor( fun cancelNotification() = NotificationManagerCompat.from(context).cancel(MainView.Section.TIMETABLE.id) - fun scheduleNotifications(lessons: List, student: Student) { + suspend fun scheduleNotifications(lessons: List, student: Student) { if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) return cancelScheduled(lessons, student.studentId) - lessons.groupBy { it.date } - .map { it.value.sortedBy { lesson -> lesson.start } } - .map { it.filter { lesson -> !lesson.canceled && lesson.isStudentPlan } } - .map { day -> - day.forEachIndexed { index, lesson -> - val intent = createIntent(student, lesson, day.getOrNull(index + 1)) + withContext(dispatchersProvider.backgroundThread) { + lessons.groupBy { it.date } + .map { it.value.sortedBy { lesson -> lesson.start } } + .map { it.filter { lesson -> !lesson.canceled && lesson.isStudentPlan } } + .map { day -> + day.forEachIndexed { index, lesson -> + val intent = createIntent(student, lesson, day.getOrNull(index + 1)) - if (lesson.start > now()) { - scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_UPCOMING, getUpcomingLessonTime(index, day, lesson)) - } + if (lesson.start > now()) { + scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_UPCOMING, getUpcomingLessonTime(index, day, lesson)) + } - if (lesson.end > now()) { - scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_CURRENT, lesson.start) - if (day.lastIndex == index) { - scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, lesson.end) + if (lesson.end > now()) { + scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_CURRENT, lesson.start) + if (day.lastIndex == index) { + scheduleBroadcast(intent, student.studentId, NOTIFICATION_TYPE_LAST_LESSON_CANCELLATION, lesson.end) + } } } } - } + } } private fun createIntent(student: Student, lesson: Timetable, nextLesson: Timetable?): Intent { diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt index 1d005ae8..47a94927 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/SyncManager.kt @@ -44,6 +44,7 @@ class SyncManager @Inject constructor( if (SDK_INT >= O) { channels.forEach { it.create() } + notificationManager.deleteNotificationChannel("lesson_channel") notificationManager.deleteNotificationChannel("new_entries_channel") } diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/channels/UpcomingLessonsChannel.kt b/app/src/main/java/io/github/wulkanowy/services/sync/channels/UpcomingLessonsChannel.kt index 41fb6192..63b3a4f9 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/channels/UpcomingLessonsChannel.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/channels/UpcomingLessonsChannel.kt @@ -17,7 +17,7 @@ class UpcomingLessonsChannel @Inject constructor( ) : Channel { companion object { - const val CHANNEL_ID = "lesson_channel" + const val CHANNEL_ID = "upcoming_lesson_channel" } override fun create() { diff --git a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt index f7d8db0a..899d45cb 100644 --- a/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt +++ b/app/src/main/java/io/github/wulkanowy/services/sync/works/ExamWork.kt @@ -3,8 +3,6 @@ package io.github.wulkanowy.services.sync.works import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Student import io.github.wulkanowy.data.repositories.exam.ExamRepository -import io.github.wulkanowy.utils.monday -import io.github.wulkanowy.utils.sunday import io.github.wulkanowy.utils.waitForResult import java.time.LocalDate.now import javax.inject.Inject @@ -12,6 +10,6 @@ import javax.inject.Inject class ExamWork @Inject constructor(private val examRepository: ExamRepository) : Work { override suspend fun doWork(student: Student, semester: Semester) { - examRepository.getExams(student, semester, now().monday, now().sunday, true).waitForResult() + examRepository.getExams(student, semester, now(), now(), true).waitForResult() } } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt index 112f4c58..73cee9e9 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/statistics/GradeStatisticsPresenter.kt @@ -164,8 +164,8 @@ class GradeStatisticsPresenter @Inject constructor( Status.SUCCESS -> { Timber.i("Loading grade stats result: Success") view?.run { - showEmpty(it.data!!.isEmpty()) - showContent(it.data.isNotEmpty()) + showEmpty(it.data!!.isEmpty() || it.data.first().partial.isEmpty()) + showContent(it.data.isNotEmpty() && it.data.first().partial.isNotEmpty()) showErrorView(false) updateData(it.data, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList) showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt index d87f0620..58be38ce 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt @@ -6,6 +6,7 @@ import android.view.View.GONE import android.view.View.VISIBLE import android.view.ViewGroup import android.widget.TextView +import androidx.core.view.ViewCompat import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Timetable @@ -44,8 +45,8 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter() - private fun resetTimers() { - Timber.d("Timetable timers reset") + fun resetTimers() { + Timber.d("Timetable timers (${timers.size}) reset") with(timers) { forEach { (_, timer) -> timer.cancel() } clear() @@ -69,11 +70,6 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter(R.layout.fragme } override fun onDestroyView() { + timetableAdapter.resetTimers() presenter.onDetachView() super.onDestroyView() } diff --git a/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt index 92e8661f..f10b00a0 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/AttendanceExtension.kt @@ -27,7 +27,7 @@ private fun calculatePercentage(presence: Double, absence: Double): Double { } inline val Attendance.description - get() = when (AttendanceCategory.valueOf(name)) { + get() = when (AttendanceCategory.getCategoryByName(name)) { AttendanceCategory.PRESENCE -> R.string.attendance_present AttendanceCategory.ABSENCE_UNEXCUSED -> R.string.attendance_absence_unexcused AttendanceCategory.ABSENCE_EXCUSED -> R.string.attendance_absence_excused diff --git a/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt index 31499405..23b86dd3 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt @@ -89,7 +89,7 @@ fun flowWithResourceIn(block: suspend () -> Flow>) = flow { } } } catch (e: Throwable) { - emit(Resource.error(e)) + emit(Resource.error(e)) } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt index d1aba160..9bd30e87 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/TimeExtension.kt @@ -13,8 +13,7 @@ import java.time.Month import java.time.ZoneId import java.time.ZoneOffset import java.time.format.DateTimeFormatter.ofPattern -import java.time.format.TextStyle.FULL_STANDALONE -import java.time.format.TextStyle.* +import java.time.format.TextStyle.FULL import java.time.temporal.TemporalAdjusters.firstInMonth import java.time.temporal.TemporalAdjusters.next import java.time.temporal.TemporalAdjusters.previous @@ -78,6 +77,12 @@ inline val LocalDate.nextOrSameSchoolDay: LocalDate } } +inline val LocalDate.startExamsDay: LocalDate + get() = nextOrSameSchoolDay.monday + +inline val LocalDate.endExamsDay: LocalDate + get() = nextOrSameSchoolDay.monday.plusWeeks(4).minusDays(1) + inline val LocalDate.previousOrSameSchoolDay: LocalDate get() { return when (dayOfWeek) { diff --git a/app/src/main/play/release-notes/pl-PL/default.txt b/app/src/main/play/release-notes/pl-PL/default.txt index 47df9980..6ca04c57 100644 --- a/app/src/main/play/release-notes/pl-PL/default.txt +++ b/app/src/main/play/release-notes/pl-PL/default.txt @@ -1,4 +1,7 @@ -Wersja 0.20.3 -- naprawiliśmy opisy wpisów frekwencyjnych, które teraz będą już w całości po polsku +Wersja 0.20.4 +- dodaliśmy obsługę koszalińskiego dziennika +- poprawiliśmy synchronizację sprawdzianów +- wyłączyliśmy dźwięk powiadomienia przy włączonej opcji pokazywania nadchodzących lekcji w powiadomieniu +- poprawiliśmy problemy ze stabilnością we frekwencji i planie lekcji Pełna lista zmian: https://github.com/wulkanowy/wulkanowy/releases diff --git a/app/src/main/res/values/api_hosts.xml b/app/src/main/res/values/api_hosts.xml index 64618568..29434602 100644 --- a/app/src/main/res/values/api_hosts.xml +++ b/app/src/main/res/values/api_hosts.xml @@ -7,6 +7,7 @@ Lubelski Portal Oświatowy EduNet Miasta Tarnowa ResMan Rzeszów + Platforma Edukacyjna Koszalina Rawa Mazowiecka - Platforma vEdukacja Zduńska Wola - e-Urząd Sieradz - Portal oświatowy @@ -27,6 +28,7 @@ https://edu.lublin.eu https://umt.tarnow.pl https://resman.pl + https://eduportal.koszalin.pl https://vulcan.net.pl/ https://vulcan.net.pl/ https://vulcan.net.pl/ @@ -47,6 +49,7 @@ lublin tarnow rzeszow + koszalin rawamazowiecka zdunskawola sieradz diff --git a/app/src/test/java/io/github/wulkanowy/utils/TimeExtensionTest.kt b/app/src/test/java/io/github/wulkanowy/utils/TimeExtensionTest.kt index 9709ded3..d604dfef 100644 --- a/app/src/test/java/io/github/wulkanowy/utils/TimeExtensionTest.kt +++ b/app/src/test/java/io/github/wulkanowy/utils/TimeExtensionTest.kt @@ -171,4 +171,42 @@ class TimeExtensionTest { assertEquals(of(2019, 5, 1), of(2019, 5, 1).getLastSchoolDayIfHoliday(2018)) assertEquals(of(2018, 5, 1), of(2019, 5, 1).getLastSchoolDayIfHoliday(2017)) } + + @Test + fun getExamsCutOffDates() { + with(of(2020, 9, 13)) { + assertEquals(of(2020, 9, 14), startExamsDay) + assertEquals(of(2020, 10, 11), endExamsDay) + } + + with(of(2020, 9, 14)) { + assertEquals(of(2020, 9, 14), startExamsDay) + assertEquals(of(2020, 10, 11), endExamsDay) + } + + with(of(2020, 9, 15)) { + assertEquals(of(2020, 9, 14), startExamsDay) + assertEquals(of(2020, 10, 11), endExamsDay) + } + + with(of(2020, 9, 16)) { + assertEquals(of(2020, 9, 14), startExamsDay) + assertEquals(of(2020, 10, 11), endExamsDay) + } + + with(of(2020, 9, 17)) { + assertEquals(of(2020, 9, 14), startExamsDay) + assertEquals(of(2020, 10, 11), endExamsDay) + } + + with(of(2020, 9, 18)) { + assertEquals(of(2020, 9, 14), startExamsDay) + assertEquals(of(2020, 10, 11), endExamsDay) + } + + with(of(2020, 9, 19)) { + assertEquals(of(2020, 9, 21), startExamsDay) + assertEquals(of(2020, 10, 18), endExamsDay) + } + } } diff --git a/build.gradle b/build.gradle index 9f239fdd..4205cce5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,8 @@ buildscript { ext { - kotlin_version = '1.4.0' + kotlin_version = '1.4.10' about_libraries = '8.3.0' - hilt_version = "2.28.3-alpha" + hilt_version = "2.29.1-alpha" } repositories { mavenCentral() @@ -15,7 +15,7 @@ buildscript { classpath 'com.android.tools.build:gradle:4.0.1' classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version" classpath 'com.google.gms:google-services:4.3.3' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.2.1' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0' classpath "com.github.triplet.gradle:play-publisher:2.7.5" classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.0" classpath "gradle.plugin.com.star-zero.gradle:githook:1.2.0"