Auto-refresh displayed data after some time (#1068)

* Auto-refresh displayed data after some time

* Use refresh interval from settings

* Auto-refresh exams

* Add refresh utils

* Auto-refresh timetable

* Auto-refresh grade details and summary

* Auto-refresh grades

* Auto-refresh attendance summary

* Add cacheKey variables

* Auto-refres completed lessons

* Auto-refres conferences

* Auto-refres homework

* Auto-refresh messages

* Auto-refresh mobile devices

* Auto-refresh notes

* Fix tests

* Fix instrumentation tests

* Create AutoRefreshHelper
This commit is contained in:
Mikołaj Pich 2021-01-13 11:01:45 +01:00 committed by GitHub
parent 64cc49055b
commit a1d4b3d19e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 558 additions and 183 deletions

View File

@ -1,5 +1,6 @@
package io.github.wulkanowy.data.db.migrations
import android.content.Context
import androidx.preference.PreferenceManager
import androidx.room.Room
import androidx.room.testing.MigrationTestHelper
@ -22,10 +23,11 @@ abstract class AbstractMigrationTest {
)
fun getMigratedRoomDatabase(): AppDatabase {
val context = ApplicationProvider.getApplicationContext<Context>()
val database = Room.databaseBuilder(ApplicationProvider.getApplicationContext(),
AppDatabase::class.java, dbName)
.addMigrations(*AppDatabase.getMigrations(SharedPrefProvider(PreferenceManager
.getDefaultSharedPreferences(ApplicationProvider.getApplicationContext())))
.getDefaultSharedPreferences(context)))
)
.build()
// close the database and release any stream resources when the test finishes

View File

@ -6,7 +6,9 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class SharedPrefProvider @Inject constructor(private val sharedPref: SharedPreferences) {
class SharedPrefProvider @Inject constructor(
private val sharedPref: SharedPreferences
) {
companion object {
const val APP_VERSION_CODE_KEY = "app_version_code"

View File

@ -12,5 +12,5 @@ import javax.inject.Singleton
interface CompletedLessonsDao : BaseDao<CompletedLesson> {
@Query("SELECT * FROM CompletedLesson WHERE diary_id = :diaryId AND student_id = :studentId AND date >= :from AND date <= :end")
fun loadAll(diaryId: Int, studentId: Int, from: LocalDate, end: LocalDate): Flow<List<CompletedLesson>>
fun loadAll(studentId: Int, diaryId: Int, from: LocalDate, end: LocalDate): Flow<List<CompletedLesson>>
}

View File

@ -7,6 +7,8 @@ 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.sdk.pojo.Absent
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
@ -21,11 +23,14 @@ import javax.inject.Singleton
@Singleton
class AttendanceRepository @Inject constructor(
private val attendanceDb: AttendanceDao,
private val sdk: Sdk
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val cacheKey = "attendance"
fun getAttendance(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday) },
fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
@ -35,6 +40,8 @@ class AttendanceRepository @Inject constructor(
saveFetchResult = { old, new ->
attendanceDb.deleteAll(old uniqueSubtract new)
attendanceDb.insertAll(new uniqueSubtract old)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
},
filterResult = { it.filter { item -> item.date in start..end } }
)

View File

@ -5,6 +5,8 @@ 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
@ -14,11 +16,14 @@ import javax.inject.Singleton
@Singleton
class AttendanceSummaryRepository @Inject constructor(
private val attendanceDb: AttendanceSummaryDao,
private val sdk: Sdk
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val cacheKey = "attendance_summary"
fun getAttendanceSummary(student: Student, semester: Semester, subjectId: Int, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) },
query = { attendanceDb.loadAll(semester.diaryId, semester.studentId, subjectId) },
fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
@ -28,6 +33,7 @@ class AttendanceSummaryRepository @Inject constructor(
saveFetchResult = { old, new ->
attendanceDb.deleteAll(old uniqueSubtract new)
attendanceDb.insertAll(new uniqueSubtract old)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)
}

View File

@ -5,6 +5,8 @@ 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.monday
import io.github.wulkanowy.utils.networkBoundResource
@ -17,12 +19,15 @@ import javax.inject.Singleton
@Singleton
class CompletedLessonsRepository @Inject constructor(
private val completedLessonsDb: CompletedLessonsDao,
private val sdk: Sdk
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val cacheKey = "completed"
fun getCompletedLessons(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
query = { completedLessonsDb.loadAll(semester.diaryId, semester.studentId, start, end) },
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
query = { completedLessonsDb.loadAll(semester.studentId, semester.diaryId, start.monday, end.sunday) },
fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
.getCompletedLessons(start.monday, end.sunday)
@ -31,6 +36,7 @@ class CompletedLessonsRepository @Inject constructor(
saveFetchResult = { old, new ->
completedLessonsDb.deleteAll(old uniqueSubtract new)
completedLessonsDb.insertAll(new uniqueSubtract old)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
},
filterResult = { it.filter { item -> item.date in start..end } }
)

View File

@ -5,6 +5,8 @@ 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
@ -14,11 +16,14 @@ import javax.inject.Singleton
@Singleton
class ConferenceRepository @Inject constructor(
private val conferenceDb: ConferenceDao,
private val sdk: Sdk
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val cacheKey = "conference"
fun getConferences(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) },
query = { conferenceDb.loadAll(semester.diaryId, student.studentId) },
fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
@ -28,6 +33,7 @@ class ConferenceRepository @Inject constructor(
saveFetchResult = { old, new ->
conferenceDb.deleteAll(old uniqueSubtract new)
conferenceDb.insertAll(new uniqueSubtract old)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)
}

View File

@ -5,7 +5,9 @@ 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.endExamsDay
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.networkBoundResource
import io.github.wulkanowy.utils.startExamsDay
@ -17,11 +19,14 @@ import javax.inject.Singleton
@Singleton
class ExamRepository @Inject constructor(
private val examDb: ExamDao,
private val sdk: Sdk
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val cacheKey = "exam"
fun getExams(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
query = { examDb.loadAll(semester.diaryId, semester.studentId, start.startExamsDay, start.endExamsDay) },
fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
@ -31,6 +36,7 @@ class ExamRepository @Inject constructor(
saveFetchResult = { old, new ->
examDb.deleteAll(old uniqueSubtract new)
examDb.insertAll(new uniqueSubtract old)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
},
filterResult = { it.filter { item -> item.date in start..end } }
)

View File

@ -8,6 +8,8 @@ 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
@ -22,11 +24,14 @@ import javax.inject.Singleton
class GradeRepository @Inject constructor(
private val gradeDb: GradeDao,
private val gradeSummaryDb: GradeSummaryDao,
private val sdk: Sdk
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val cacheKey = "grade"
fun getGrades(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
shouldFetch = { (details, summaries) -> details.isEmpty() || summaries.isEmpty() || forceRefresh },
shouldFetch = { (details, summaries) -> details.isEmpty() || summaries.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) },
query = {
gradeDb.loadAll(semester.semesterId, semester.studentId).combine(gradeSummaryDb.loadAll(semester.semesterId, semester.studentId)) { details, summaries ->
details to summaries
@ -42,6 +47,8 @@ class GradeRepository @Inject constructor(
saveFetchResult = { (oldDetails, oldSummary), (newDetails, newSummary) ->
refreshGradeDetails(student, oldDetails, newDetails, notify)
refreshGradeSummaries(oldSummary, newSummary, notify)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)

View File

@ -12,6 +12,8 @@ import io.github.wulkanowy.data.mappers.mapPointsToStatisticsItems
import io.github.wulkanowy.data.mappers.mapSemesterToStatisticItems
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
@ -24,11 +26,16 @@ class GradeStatisticsRepository @Inject constructor(
private val gradePartialStatisticsDb: GradePartialStatisticsDao,
private val gradePointsStatisticsDb: GradePointsStatisticsDao,
private val gradeSemesterStatisticsDb: GradeSemesterStatisticsDao,
private val sdk: Sdk
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val partialCacheKey = "grade_stats_partial"
private val semesterCacheKey = "grade_stats_semester"
private val pointsCacheKey = "grade_stats_points"
fun getGradesPartialStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(partialCacheKey, semester)) },
query = { gradePartialStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
@ -38,6 +45,7 @@ class GradeStatisticsRepository @Inject constructor(
saveFetchResult = { old, new ->
gradePartialStatisticsDb.deleteAll(old uniqueSubtract new)
gradePartialStatisticsDb.insertAll(new uniqueSubtract old)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(partialCacheKey, semester))
},
mapResult = { items ->
when (subjectName) {
@ -63,7 +71,7 @@ class GradeStatisticsRepository @Inject constructor(
)
fun getGradesSemesterStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(semesterCacheKey, semester)) },
query = { gradeSemesterStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
@ -73,6 +81,7 @@ class GradeStatisticsRepository @Inject constructor(
saveFetchResult = { old, new ->
gradeSemesterStatisticsDb.deleteAll(old uniqueSubtract new)
gradeSemesterStatisticsDb.insertAll(new uniqueSubtract old)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(semesterCacheKey, semester))
},
mapResult = { items ->
val itemsWithAverage = items.map { item ->
@ -103,7 +112,7 @@ class GradeStatisticsRepository @Inject constructor(
)
fun getGradesPointsStatistics(student: Student, semester: Semester, subjectName: String, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(pointsCacheKey, semester)) },
query = { gradePointsStatisticsDb.loadAll(semester.semesterId, semester.studentId) },
fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
@ -113,6 +122,7 @@ class GradeStatisticsRepository @Inject constructor(
saveFetchResult = { old, new ->
gradePointsStatisticsDb.deleteAll(old uniqueSubtract new)
gradePointsStatisticsDb.insertAll(new uniqueSubtract old)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(pointsCacheKey, semester))
},
mapResult = { items ->
when (subjectName) {

View File

@ -6,6 +6,8 @@ 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.monday
import io.github.wulkanowy.utils.networkBoundResource
@ -18,11 +20,14 @@ import javax.inject.Singleton
@Singleton
class HomeworkRepository @Inject constructor(
private val homeworkDb: HomeworkDao,
private val sdk: Sdk
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val cacheKey = "homework"
fun getHomework(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
query = { homeworkDb.loadAll(semester.semesterId, semester.studentId, start.monday, end.sunday) },
fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
@ -32,6 +37,8 @@ class HomeworkRepository @Inject constructor(
saveFetchResult = { old, new ->
homeworkDb.deleteAll(old uniqueSubtract new)
homeworkDb.insertAll(new uniqueSubtract old)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
}
)

View File

@ -6,13 +6,15 @@ import io.github.wulkanowy.data.db.entities.Message
import io.github.wulkanowy.data.db.entities.Recipient
import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.db.entities.Student
import io.github.wulkanowy.data.mappers.mapFromEntities
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.enums.MessageFolder
import io.github.wulkanowy.data.enums.MessageFolder.RECEIVED
import io.github.wulkanowy.data.mappers.mapFromEntities
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Folder
import io.github.wulkanowy.sdk.pojo.SentMessage
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
@ -27,12 +29,15 @@ import javax.inject.Singleton
class MessageRepository @Inject constructor(
private val messagesDb: MessagesDao,
private val messageAttachmentDao: MessageAttachmentDao,
private val sdk: Sdk
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val cacheKey = "message"
@Suppress("UNUSED_PARAMETER")
fun getMessages(student: Student, semester: Semester, folder: MessageFolder, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, student, folder)) },
query = { messagesDb.loadAll(student.id.toInt(), folder.id) },
fetch = { sdk.init(student).getMessages(Folder.valueOf(folder.name), now().minusMonths(3), now()).mapToEntities(student) },
saveFetchResult = { old, new ->
@ -40,6 +45,8 @@ class MessageRepository @Inject constructor(
messagesDb.insertAll((new uniqueSubtract old).onEach {
it.isNotified = !notify
})
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student, folder))
}
)

View File

@ -8,6 +8,8 @@ import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.data.mappers.mapToMobileDeviceToken
import io.github.wulkanowy.data.pojos.MobileDeviceToken
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
@ -17,11 +19,14 @@ import javax.inject.Singleton
@Singleton
class MobileDeviceRepository @Inject constructor(
private val mobileDb: MobileDeviceDao,
private val sdk: Sdk
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val cacheKey = "devices"
fun getDevices(student: Student, semester: Semester, forceRefresh: Boolean) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, student)) },
query = { mobileDb.loadAll(semester.studentId) },
fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
@ -31,6 +36,8 @@ class MobileDeviceRepository @Inject constructor(
saveFetchResult = { old, new ->
mobileDb.deleteAll(old uniqueSubtract new)
mobileDb.insertAll(new uniqueSubtract old)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, student))
}
)

View File

@ -6,6 +6,8 @@ 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
@ -17,11 +19,14 @@ import javax.inject.Singleton
@Singleton
class NoteRepository @Inject constructor(
private val noteDb: NoteDao,
private val sdk: Sdk
private val sdk: Sdk,
private val refreshHelper: AutoRefreshHelper,
) {
private val cacheKey = "note"
fun getNotes(student: Student, semester: Semester, forceRefresh: Boolean, notify: Boolean = false) = networkBoundResource(
shouldFetch = { it.isEmpty() || forceRefresh },
shouldFetch = { it.isEmpty() || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester)) },
query = { noteDb.loadAll(student.studentId) },
fetch = {
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
@ -36,6 +41,8 @@ class NoteRepository @Inject constructor(
if (notify) isNotified = false
}
})
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester))
}
)

View File

@ -9,6 +9,8 @@ import io.github.wulkanowy.data.db.entities.TimetableAdditional
import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
import io.github.wulkanowy.utils.AutoRefreshHelper
import io.github.wulkanowy.utils.getRefreshKey
import io.github.wulkanowy.utils.init
import io.github.wulkanowy.utils.monday
import io.github.wulkanowy.utils.networkBoundResource
@ -25,11 +27,14 @@ class TimetableRepository @Inject constructor(
private val timetableDb: TimetableDao,
private val timetableAdditionalDb: TimetableAdditionalDao,
private val sdk: Sdk,
private val schedulerHelper: TimetableNotificationSchedulerHelper
private val schedulerHelper: TimetableNotificationSchedulerHelper,
private val refreshHelper: AutoRefreshHelper,
) {
private val cacheKey = "timetable"
fun getTimetable(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean, refreshAdditional: Boolean = false) = networkBoundResource(
shouldFetch = { (timetable, additional) -> timetable.isEmpty() || (additional.isEmpty() && refreshAdditional) || forceRefresh },
shouldFetch = { (timetable, additional) -> timetable.isEmpty() || (additional.isEmpty() && refreshAdditional) || forceRefresh || refreshHelper.isShouldBeRefreshed(getRefreshKey(cacheKey, semester, start, end)) },
query = {
timetableDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)
.map { schedulerHelper.scheduleNotifications(it, student); it }
@ -47,6 +52,7 @@ class TimetableRepository @Inject constructor(
refreshTimetable(student, oldTimetable, newTimetable)
refreshAdditional(oldAdditional, newAdditional)
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
},
filterResult = { (timetable, additional) ->
timetable.filter { item ->

View File

@ -191,8 +191,8 @@ class AttendanceFragment : BaseFragment<FragmentAttendanceBinding>(R.layout.frag
binding. attendanceRecycler.visibility = if (show) VISIBLE else GONE
}
override fun hideRefresh() {
binding.attendanceSwipe.isRefreshing = false
override fun showRefresh(show: Boolean) {
binding.attendanceSwipe.isRefreshing = show
}
override fun showPreButton(show: Boolean) {

View File

@ -51,23 +51,23 @@ class AttendancePresenter @Inject constructor(
view.initView()
Timber.i("Attendance view was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData(ofEpochDay(date ?: baseDate.toEpochDay()))
reloadView(ofEpochDay(date ?: baseDate.toEpochDay()))
loadData()
if (currentDate.isHolidays) setBaseDateOnHolidays()
reloadView()
}
fun onPreviousDay() {
view?.finishActionMode()
attendanceToExcuseList.clear()
loadData(currentDate.previousSchoolDay)
reloadView()
reloadView(currentDate.previousSchoolDay)
loadData()
}
fun onNextDay() {
view?.finishActionMode()
attendanceToExcuseList.clear()
loadData(currentDate.nextSchoolDay)
reloadView()
reloadView(currentDate.nextSchoolDay)
loadData()
}
fun onPickDate() {
@ -75,13 +75,13 @@ class AttendancePresenter @Inject constructor(
}
fun onDateSet(year: Int, month: Int, day: Int) {
loadData(LocalDate.of(year, month, day))
reloadView()
reloadView(LocalDate.of(year, month, day))
loadData()
}
fun onSwipeRefresh() {
Timber.i("Force refreshing the attendance")
loadData(currentDate, true)
loadData(true)
}
fun onRetry() {
@ -89,7 +89,7 @@ class AttendancePresenter @Inject constructor(
showErrorView(false)
showProgress(true)
}
loadData(currentDate, true)
loadData(true)
}
fun onDetailsClick() {
@ -102,8 +102,8 @@ class AttendancePresenter @Inject constructor(
if (view.currentStackSize == 1) {
baseDate.also {
if (currentDate != it) {
loadData(it)
reloadView()
reloadView(it)
loadData()
} else if (!view.isViewEmpty) view.resetView()
}
} else view.popView()
@ -184,17 +184,27 @@ class AttendancePresenter @Inject constructor(
}.launch("holidays")
}
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading attendance data started")
currentDate = date
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
attendanceRepository.getAttendance(student, semester, date, date, forceRefresh)
attendanceRepository.getAttendance(student, semester, currentDate, currentDate, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> view?.showExcuseButton(false)
Status.LOADING -> {
view?.showExcuseButton(false)
if (!it.data.isNullOrEmpty()) {
view?.run {
enableSwipe(true)
showRefresh(true)
showProgress(false)
showContent(true)
updateData(it.data)
}
}
}
Status.SUCCESS -> {
Timber.i("Loading attendance result: Success")
view?.apply {
@ -220,7 +230,7 @@ class AttendancePresenter @Inject constructor(
}
}.afterLoading {
view?.run {
hideRefresh()
showRefresh(false)
showProgress(false)
enableSwipe(true)
}
@ -250,12 +260,12 @@ class AttendancePresenter @Inject constructor(
showContent(true)
showProgress(false)
}
loadData(currentDate, forceRefresh = true)
loadData(forceRefresh = true)
}
Status.ERROR -> {
Timber.i("Excusing for absence result: An exception occurred")
errorHandler.dispatch(it.error!!)
loadData(currentDate)
loadData()
}
}
}.launch("excuse")
@ -272,11 +282,14 @@ class AttendancePresenter @Inject constructor(
}
}
private fun reloadView() {
private fun reloadView(date: LocalDate) {
currentDate = date
Timber.i("Reload attendance view with the date ${currentDate.toFormattedString()}")
view?.apply {
showProgress(true)
enableSwipe(false)
showRefresh(false)
showContent(false)
showEmpty(false)
showErrorView(false)

View File

@ -24,7 +24,7 @@ interface AttendanceView : BaseView {
fun clearData()
fun hideRefresh()
fun showRefresh(show: Boolean)
fun resetView()

View File

@ -121,8 +121,8 @@ class AttendanceSummaryFragment :
binding.attendanceSummarySubjectsContainer.visibility = if (show) VISIBLE else INVISIBLE
}
override fun hideRefresh() {
binding.attendanceSummarySwipe.isRefreshing = false
override fun showRefresh(show: Boolean) {
binding.attendanceSummarySwipe.isRefreshing = show
}
override fun onSaveInstanceState(outState: Bundle) {

View File

@ -1,6 +1,7 @@
package io.github.wulkanowy.ui.modules.attendance.summary
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.AttendanceSummary
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.repositories.AttendanceSummaryRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
@ -74,6 +75,8 @@ class AttendanceSummaryPresenter @Inject constructor(
}
private fun loadData(subjectId: Int, forceRefresh: Boolean = false) {
Timber.i("Loading attendance summary data started")
currentSubjectId = subjectId
flowWithResourceIn {
@ -82,15 +85,23 @@ class AttendanceSummaryPresenter @Inject constructor(
attendanceSummaryRepository.getAttendanceSummary(student, semester, subjectId, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading attendance summary data started")
Status.LOADING -> {
if (!it.data.isNullOrEmpty()) {
view?.run {
enableSwipe(true)
showRefresh(true)
showProgress(false)
showContent(true)
updateDataSet(sortItems(it.data))
}
}
}
Status.SUCCESS -> {
Timber.i("Loading attendance summary result: Success")
view?.apply {
showEmpty(it.data!!.isEmpty())
showContent(it.data.isNotEmpty())
updateDataSet(it.data.sortedByDescending { item ->
if (item.month.value <= Month.JUNE.value) item.month.value + 12 else item.month.value
})
updateDataSet(sortItems(it.data))
}
analytics.logEvent(
"load_data",
@ -106,13 +117,17 @@ class AttendanceSummaryPresenter @Inject constructor(
}
}.afterLoading {
view?.run {
hideRefresh()
showRefresh(false)
showProgress(false)
enableSwipe(true)
}
}.launch()
}
private fun sortItems(items: List<AttendanceSummary>) = items.sortedByDescending { item ->
if (item.month.value <= Month.JUNE.value) item.month.value + 12 else item.month.value
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {

View File

@ -9,7 +9,7 @@ interface AttendanceSummaryView : BaseView {
fun initView()
fun hideRefresh()
fun showRefresh(show: Boolean)
fun showContent(show: Boolean)

View File

@ -67,8 +67,8 @@ class ConferenceFragment : BaseFragment<FragmentConferenceBinding>(R.layout.frag
}
}
override fun hideRefresh() {
binding.conferenceSwipe.isRefreshing = false
override fun showRefresh(show: Boolean) {
binding.conferenceSwipe.isRefreshing = show
}
override fun showProgress(show: Boolean) {

View File

@ -59,13 +59,25 @@ class ConferencePresenter @Inject constructor(
}
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading conference data started")
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
conferenceRepository.getConferences(student, semester, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading conference data started")
Status.LOADING -> {
if (!it.data.isNullOrEmpty()) {
view?.run {
enableSwipe(true)
showRefresh(true)
showProgress(false)
showContent(true)
updateData(it.data.sortedByDescending { conference -> conference.date })
}
}
}
Status.SUCCESS -> {
Timber.i("Loading conference result: Success")
view?.run {
@ -87,7 +99,7 @@ class ConferencePresenter @Inject constructor(
}
}.afterLoading {
view?.run {
hideRefresh()
showRefresh(false)
showProgress(false)
enableSwipe(true)
}

View File

@ -13,7 +13,7 @@ interface ConferenceView : BaseView {
fun clearData()
fun hideRefresh()
fun showRefresh(show: Boolean)
fun showEmpty(show: Boolean)

View File

@ -65,8 +65,8 @@ class ExamFragment : BaseFragment<FragmentExamBinding>(R.layout.fragment_exam),
}
}
override fun hideRefresh() {
binding.examSwipe.isRefreshing = false
override fun showRefresh(show: Boolean) {
binding.examSwipe.isRefreshing = show
}
override fun updateData(data: List<ExamItem<*>>) {

View File

@ -45,24 +45,24 @@ class ExamPresenter @Inject constructor(
view.initView()
Timber.i("Exam view was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData(ofEpochDay(date ?: baseDate.toEpochDay()))
reloadView(ofEpochDay(date ?: baseDate.toEpochDay()))
loadData()
if (currentDate.isHolidays) setBaseDateOnHolidays()
reloadView()
}
fun onPreviousWeek() {
loadData(currentDate.minusDays(7))
reloadView()
reloadView(currentDate.minusDays(7))
loadData()
}
fun onNextWeek() {
loadData(currentDate.plusDays(7))
reloadView()
reloadView(currentDate.plusDays(7))
loadData()
}
fun onSwipeRefresh() {
Timber.i("Force refreshing the exam")
loadData(currentDate, true)
loadData(true)
}
fun onRetry() {
@ -70,7 +70,7 @@ class ExamPresenter @Inject constructor(
showErrorView(false)
showProgress(true)
}
loadData(currentDate, true)
loadData(true)
}
fun onDetailsClick() {
@ -86,8 +86,8 @@ class ExamPresenter @Inject constructor(
Timber.i("Exam view is reselected")
baseDate.also {
if (currentDate != it) {
loadData(it)
reloadView()
reloadView(it)
loadData()
} else if (view?.isViewEmpty == false) view?.resetView()
}
}
@ -105,8 +105,8 @@ class ExamPresenter @Inject constructor(
}.launch("holidays")
}
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
currentDate = date
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading exam data started")
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
@ -114,7 +114,17 @@ class ExamPresenter @Inject constructor(
examRepository.getExams(student, semester, currentDate.monday, currentDate.sunday, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading exam data started")
Status.LOADING -> {
if (!it.data.isNullOrEmpty()) {
view?.run {
enableSwipe(true)
showRefresh(true)
showProgress(false)
showContent(true)
updateData(createExamItems(it.data))
}
}
}
Status.SUCCESS -> {
Timber.i("Loading exam result: Success")
view?.apply {
@ -136,7 +146,7 @@ class ExamPresenter @Inject constructor(
}
}.afterLoading {
view?.run {
hideRefresh()
showRefresh(false)
showProgress(false)
enableSwipe(true)
}
@ -162,7 +172,9 @@ class ExamPresenter @Inject constructor(
}.flatten()
}
private fun reloadView() {
private fun reloadView(date: LocalDate) {
currentDate = date
Timber.i("Reload exam view with the date ${currentDate.toFormattedString()}")
view?.apply {
showProgress(true)

View File

@ -15,7 +15,7 @@ interface ExamView : BaseView {
fun clearData()
fun hideRefresh()
fun showRefresh(show: Boolean)
fun resetView()

View File

@ -137,15 +137,35 @@ class GradeDetailsPresenter @Inject constructor(
}
private fun loadData(semesterId: Int, forceRefresh: Boolean) {
Timber.i("Loading grade details data started")
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading grade details data started")
Status.LOADING -> {
val items = createGradeItems(it.data.orEmpty())
if (items.isNotEmpty()) {
Timber.i("Loading gradle details result: load cached data")
view?.run {
updateNewGradesAmount(it.data.orEmpty())
enableSwipe(true)
showRefresh(true)
showProgress(false)
showContent(true)
updateData(
data = items,
isGradeExpandable = preferencesRepository.isGradeExpandable,
gradeColorTheme = preferencesRepository.gradeColorTheme
)
notifyParentDataLoaded(semesterId)
}
}
}
Status.SUCCESS -> {
Timber.i("Loading grade details result: Success")
newGradesAmount = it.data!!.sumBy { item -> item.grades.sumBy { grade -> if (!grade.isRead) 1 else 0 } }
updateNewGradesAmount(it.data!!)
updateMarkAsDoneButton()
val items = createGradeItems(it.data)
view?.run {
@ -179,6 +199,10 @@ class GradeDetailsPresenter @Inject constructor(
}.launch()
}
private fun updateNewGradesAmount(grades: List<GradeDetailsWithAverage>) {
newGradesAmount = grades.sumBy { item -> item.grades.sumBy { grade -> if (!grade.isRead) 1 else 0 } }
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {

View File

@ -2,6 +2,7 @@ package io.github.wulkanowy.ui.modules.grade.statistics
import io.github.wulkanowy.data.Status
import io.github.wulkanowy.data.db.entities.Subject
import io.github.wulkanowy.data.pojos.GradeStatisticsItem
import io.github.wulkanowy.data.repositories.GradeStatisticsRepository
import io.github.wulkanowy.data.repositories.PreferencesRepository
import io.github.wulkanowy.data.repositories.SemesterRepository
@ -143,6 +144,8 @@ class GradeStatisticsPresenter @Inject constructor(
}
private fun loadDataByType(semesterId: Int, subjectName: String, type: ViewType, forceRefresh: Boolean = false) {
Timber.i("Loading grade stats data started")
currentSubjectName = if (preferencesRepository.showAllSubjectsOnStatisticsList) "Wszystkie" else subjectName
currentType = type
@ -160,17 +163,24 @@ class GradeStatisticsPresenter @Inject constructor(
}
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading grade stats data started")
Status.LOADING -> {
val isNoContent = it.data == null || checkIsNoContent(it.data, type)
if (!isNoContent) {
view?.run {
showEmpty(isNoContent)
showContent(!isNoContent)
showErrorView(false)
enableSwipe(true)
showRefresh(true)
updateData(it.data!!, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList)
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
}
}
}
Status.SUCCESS -> {
Timber.i("Loading grade stats result: Success")
view?.run {
val isNoContent = it.data!!.isEmpty() || when (type) {
ViewType.SEMESTER -> it.data.firstOrNull()?.semester?.amounts.orEmpty().sum() == 0
ViewType.PARTIAL -> it.data.firstOrNull()?.partial?.classAmounts.orEmpty().sum() == 0
ViewType.POINTS -> it.data.firstOrNull()?.points?.let { points ->
points.student == .0 && points.others == .0
} ?: false
}
val isNoContent = checkIsNoContent(it.data!!, type)
showEmpty(isNoContent)
showContent(!isNoContent)
showErrorView(false)
@ -198,6 +208,16 @@ class GradeStatisticsPresenter @Inject constructor(
}.launch("load")
}
private fun checkIsNoContent(items: List<GradeStatisticsItem>, type: ViewType): Boolean {
return items.isEmpty() || when (type) {
ViewType.SEMESTER -> items.firstOrNull()?.semester?.amounts.orEmpty().sum() == 0
ViewType.PARTIAL -> items.firstOrNull()?.partial?.classAmounts.orEmpty().sum() == 0
ViewType.POINTS -> items.firstOrNull()?.points?.let { points ->
points.student == .0 && points.others == .0
} ?: false
}
}
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {

View File

@ -37,12 +37,26 @@ class GradeSummaryPresenter @Inject constructor(
}
private fun loadData(semesterId: Int, forceRefresh: Boolean) {
Timber.i("Loading grade summary started")
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
averageProvider.getGradesDetailsWithAverage(student, semesterId, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading grade summary started")
Status.LOADING -> {
val items = createGradeSummaryItems(it.data.orEmpty())
if (items.isNotEmpty()) {
Timber.i("Loading grade summary result: load cached data")
view?.run {
enableSwipe(true)
showRefresh(true)
showProgress(false)
showContent(true)
updateData(items)
}
}
}
Status.SUCCESS -> {
Timber.i("Loading grade summary result: Success")
val items = createGradeSummaryItems(it.data!!)

View File

@ -83,8 +83,8 @@ class HomeworkFragment : BaseFragment<FragmentHomeworkBinding>(R.layout.fragment
binding.homeworkNavDate.text = date
}
override fun hideRefresh() {
binding.homeworkSwipe.isRefreshing = false
override fun showRefresh(show: Boolean) {
binding.homeworkSwipe.isRefreshing = show
}
override fun showEmpty(show: Boolean) {

View File

@ -44,24 +44,24 @@ class HomeworkPresenter @Inject constructor(
view.initView()
Timber.i("Homework view was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData(ofEpochDay(date ?: baseDate.toEpochDay()))
reloadView(ofEpochDay(date ?: baseDate.toEpochDay()))
loadData()
if (currentDate.isHolidays) setBaseDateOnHolidays()
reloadView()
}
fun onPreviousDay() {
loadData(currentDate.minusDays(7))
reloadView()
reloadView(currentDate.minusDays(7))
loadData()
}
fun onNextDay() {
loadData(currentDate.plusDays(7))
reloadView()
reloadView(currentDate.plusDays(7))
loadData()
}
fun onSwipeRefresh() {
Timber.i("Force refreshing the homework")
loadData(currentDate, true)
loadData(true)
}
fun onRetry() {
@ -69,7 +69,7 @@ class HomeworkPresenter @Inject constructor(
showErrorView(false)
showProgress(true)
}
loadData(currentDate, true)
loadData(true)
}
fun onDetailsClick() {
@ -94,16 +94,26 @@ class HomeworkPresenter @Inject constructor(
}.launch("holidays")
}
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
currentDate = date
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading homework data started")
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
homeworkRepository.getHomework(student, semester, date, date, forceRefresh)
homeworkRepository.getHomework(student, semester, currentDate, currentDate, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading homework data started")
Status.LOADING -> {
if (!it.data.isNullOrEmpty()) {
view?.run {
enableSwipe(true)
showRefresh(true)
showProgress(false)
showContent(true)
updateData(createHomeworkItem(it.data))
}
}
}
Status.SUCCESS -> {
Timber.i("Loading homework result: Success")
view?.apply {
@ -125,7 +135,7 @@ class HomeworkPresenter @Inject constructor(
}
}.afterLoading {
view?.run {
hideRefresh()
showRefresh(false)
showProgress(false)
enableSwipe(true)
}
@ -151,7 +161,9 @@ class HomeworkPresenter @Inject constructor(
}.flatten()
}
private fun reloadView() {
private fun reloadView(date: LocalDate) {
currentDate = date
Timber.i("Reload homework view with the date ${currentDate.toFormattedString()}")
view?.apply {
showProgress(true)

View File

@ -15,7 +15,7 @@ interface HomeworkView : BaseView {
fun updateNavigationWeek(date: String)
fun hideRefresh()
fun showRefresh(show: Boolean)
fun showEmpty(show: Boolean)

View File

@ -85,13 +85,27 @@ class MessageTabPresenter @Inject constructor(
}
private fun loadData(forceRefresh: Boolean) {
Timber.i("Loading $folder message data started")
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
messageRepository.getMessages(student, semester, folder, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading $folder message data started")
Status.LOADING -> {
if (!it.data.isNullOrEmpty()) {
view?.run {
enableSwipe(true)
showRefresh(true)
showProgress(false)
showContent(true)
messages = it.data
updateData(getFilteredData(lastSearchQuery))
notifyParentDataLoaded()
}
}
}
Status.SUCCESS -> {
Timber.i("Loading $folder message result: Success")
messages = it.data!!

View File

@ -100,8 +100,8 @@ class MobileDeviceFragment :
}
}
override fun hideRefresh() {
binding.mobileDevicesSwipe.isRefreshing = false
override fun showRefresh(show: Boolean) {
binding.mobileDevicesSwipe.isRefreshing = show
}
override fun showProgress(show: Boolean) {

View File

@ -51,13 +51,25 @@ class MobileDevicePresenter @Inject constructor(
}
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading mobile devices data started")
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
mobileDeviceRepository.getDevices(student, semester, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading mobile devices data started")
Status.LOADING -> {
if (!it.data.isNullOrEmpty()) {
view?.run {
enableSwipe(true)
showRefresh(true)
showProgress(false)
showContent(true)
updateData(it.data)
}
}
}
Status.SUCCESS -> {
Timber.i("Loading mobile devices result: Success")
view?.run {
@ -79,7 +91,7 @@ class MobileDevicePresenter @Inject constructor(
}
}.afterLoading {
view?.run {
hideRefresh()
showRefresh(false)
showProgress(false)
enableSwipe(true)
}

View File

@ -17,7 +17,7 @@ interface MobileDeviceView : BaseView {
fun showUndo(device: MobileDevice, position: Int)
fun hideRefresh()
fun showRefresh(show: Boolean)
fun showProgress(show: Boolean)

View File

@ -105,8 +105,8 @@ class NoteFragment : BaseFragment<FragmentNoteBinding>(R.layout.fragment_note),
binding.noteRecycler.visibility = if (show) VISIBLE else GONE
}
override fun hideRefresh() {
binding.noteSwipe.isRefreshing = false
override fun showRefresh(show: Boolean) {
binding.noteSwipe.isRefreshing = show
}
override fun onDestroyView() {

View File

@ -52,13 +52,25 @@ class NotePresenter @Inject constructor(
}
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading note data started")
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
noteRepository.getNotes(student, semester, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading note data started")
Status.LOADING -> {
if (!it.data.isNullOrEmpty()) {
view?.run {
enableSwipe(true)
showRefresh(true)
showProgress(false)
showContent(true)
updateData(it.data)
}
}
}
Status.SUCCESS -> {
Timber.i("Loading note result: Success")
view?.apply {
@ -80,7 +92,7 @@ class NotePresenter @Inject constructor(
}
}.afterLoading {
view?.run {
hideRefresh()
showRefresh(false)
showProgress(false)
enableSwipe(true)
}

View File

@ -27,7 +27,7 @@ interface NoteView : BaseView {
fun showContent(show: Boolean)
fun hideRefresh()
fun showRefresh(show: Boolean)
fun showNoteDialog(note: Note)
}

View File

@ -113,8 +113,8 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
binding.timetableNavDate.text = date
}
override fun hideRefresh() {
binding.timetableSwipe.isRefreshing = false
override fun showRefresh(show: Boolean) {
binding.timetableSwipe.isRefreshing = show
}
override fun resetView() {

View File

@ -34,7 +34,7 @@ class TimetablePresenter @Inject constructor(
private val timetableRepository: TimetableRepository,
private val semesterRepository: SemesterRepository,
private val prefRepository: PreferencesRepository,
private val analytics: AnalyticsHelper
private val analytics: AnalyticsHelper,
) : BasePresenter<TimetableView>(errorHandler, studentRepository) {
private var baseDate: LocalDate = now().nextOrSameSchoolDay
@ -49,19 +49,19 @@ class TimetablePresenter @Inject constructor(
view.initView()
Timber.i("Timetable was initialized")
errorHandler.showErrorMessage = ::showErrorViewOnError
loadData(ofEpochDay(date ?: baseDate.toEpochDay()))
reloadView(ofEpochDay(date ?: baseDate.toEpochDay()))
loadData()
if (currentDate.isHolidays) setBaseDateOnHolidays()
reloadView()
}
fun onPreviousDay() {
loadData(currentDate.previousSchoolDay)
reloadView()
reloadView(currentDate.previousSchoolDay)
loadData()
}
fun onNextDay() {
loadData(currentDate.nextSchoolDay)
reloadView()
reloadView(currentDate.nextSchoolDay)
loadData()
}
fun onPickDate() {
@ -69,13 +69,13 @@ class TimetablePresenter @Inject constructor(
}
fun onDateSet(year: Int, month: Int, day: Int) {
loadData(of(year, month, day))
reloadView()
reloadView(of(year, month, day))
loadData()
}
fun onSwipeRefresh() {
Timber.i("Force refreshing the timetable")
loadData(currentDate, true)
loadData(true)
}
fun onRetry() {
@ -83,7 +83,7 @@ class TimetablePresenter @Inject constructor(
showErrorView(false)
showProgress(true)
}
loadData(currentDate, true)
loadData(true)
}
fun onDetailsClick() {
@ -96,8 +96,8 @@ class TimetablePresenter @Inject constructor(
if (view.currentStackSize == 1) {
baseDate.also {
if (currentDate != it) {
loadData(it)
reloadView()
reloadView(it)
loadData()
} else if (!view.isViewEmpty) view.resetView()
}
} else view.popView()
@ -132,27 +132,30 @@ class TimetablePresenter @Inject constructor(
}.launch("holidays")
}
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
currentDate = date
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading timetable data started")
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
timetableRepository.getTimetable(student, semester, date, date, forceRefresh)
timetableRepository.getTimetable(student, semester, currentDate, currentDate, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading timetable data started")
Status.LOADING -> {
if (!it.data?.first.isNullOrEmpty()) {
view?.run {
enableSwipe(true)
showRefresh(true)
showProgress(false)
showContent(true)
updateData(it.data!!.first)
}
}
}
Status.SUCCESS -> {
Timber.i("Loading timetable result: Success")
view?.apply {
updateData(
showWholeClassPlanType = prefRepository.showWholeClassPlan,
showGroupsInPlanType = prefRepository.showGroupsInPlan,
showTimetableTimers = prefRepository.showTimetableTimers,
data = it.data!!.first
.filter { item -> if (prefRepository.showWholeClassPlan == "no") item.isStudentPlan else true }
.sortedWith(compareBy({ item -> item.number }, { item -> !item.isStudentPlan }))
)
updateData(it.data!!.first)
showEmpty(it.data.first.isEmpty())
showErrorView(false)
showContent(it.data.first.isNotEmpty())
@ -170,13 +173,26 @@ class TimetablePresenter @Inject constructor(
}
}.afterLoading {
view?.run {
hideRefresh()
showRefresh(false)
showProgress(false)
enableSwipe(true)
}
}.launch()
}
private fun updateData(lessons: List<Timetable>) {
view?.updateData(
showWholeClassPlanType = prefRepository.showWholeClassPlan,
showGroupsInPlanType = prefRepository.showGroupsInPlan,
showTimetableTimers = prefRepository.showTimetableTimers,
data = createItems(lessons)
)
}
private fun createItems(items: List<Timetable>) = items.filter { item ->
if (prefRepository.showWholeClassPlan == "no") item.isStudentPlan else true
}.sortedWith(compareBy({ item -> item.number }, { item -> !item.isStudentPlan }))
private fun showErrorViewOnError(message: String, error: Throwable) {
view?.run {
if (isViewEmpty) {
@ -188,7 +204,9 @@ class TimetablePresenter @Inject constructor(
}
}
private fun reloadView() {
private fun reloadView(date: LocalDate) {
currentDate = date
Timber.i("Reload timetable view with the date ${currentDate.toFormattedString()}")
view?.apply {
showProgress(true)

View File

@ -18,7 +18,7 @@ interface TimetableView : BaseView {
fun clearData()
fun hideRefresh()
fun showRefresh(show: Boolean)
fun resetView()

View File

@ -89,8 +89,8 @@ class CompletedLessonsFragment :
binding.completedLessonsNavDate.text = date
}
override fun hideRefresh() {
binding.completedLessonsSwipe.isRefreshing = false
override fun showRefresh(show: Boolean) {
binding.completedLessonsSwipe.isRefreshing = show
}
override fun showEmpty(show: Boolean) {

View File

@ -50,19 +50,19 @@ class CompletedLessonsPresenter @Inject constructor(
this.view?.showEmpty(true)
Timber.i("Completed lessons feature disabled by school")
}
loadData(ofEpochDay(date ?: baseDate.toEpochDay()))
reloadView(ofEpochDay(date ?: baseDate.toEpochDay()))
loadData()
if (currentDate.isHolidays) setBaseDateOnHolidays()
reloadView()
}
fun onPreviousDay() {
loadData(currentDate.previousSchoolDay)
reloadView()
reloadView(currentDate.previousSchoolDay)
loadData()
}
fun onNextDay() {
loadData(currentDate.nextSchoolDay)
reloadView()
reloadView(currentDate.nextSchoolDay)
loadData()
}
fun onPickDate() {
@ -70,13 +70,13 @@ class CompletedLessonsPresenter @Inject constructor(
}
fun onDateSet(year: Int, month: Int, day: Int) {
loadData(LocalDate.of(year, month, day))
reloadView()
reloadView(LocalDate.of(year, month, day))
loadData()
}
fun onSwipeRefresh() {
Timber.i("Force refreshing the completed lessons")
loadData(currentDate, true)
loadData(true)
}
fun onRetry() {
@ -84,7 +84,7 @@ class CompletedLessonsPresenter @Inject constructor(
showErrorView(false)
showProgress(true)
}
loadData(currentDate, true)
loadData(true)
}
fun onDetailsClick() {
@ -109,16 +109,26 @@ class CompletedLessonsPresenter @Inject constructor(
}.launch("holidays")
}
private fun loadData(date: LocalDate, forceRefresh: Boolean = false) {
currentDate = date
private fun loadData(forceRefresh: Boolean = false) {
Timber.i("Loading completed lessons data started")
flowWithResourceIn {
val student = studentRepository.getCurrentStudent()
val semester = semesterRepository.getCurrentSemester(student)
completedLessonsRepository.getCompletedLessons(student, semester, date, date, forceRefresh)
completedLessonsRepository.getCompletedLessons(student, semester, currentDate, currentDate, forceRefresh)
}.onEach {
when (it.status) {
Status.LOADING -> Timber.i("Loading completed lessons data started")
Status.LOADING -> {
if (!it.data.isNullOrEmpty()) {
view?.run {
enableSwipe(true)
showRefresh(true)
showProgress(false)
showContent(true)
updateData(it.data.sortedBy { item -> item.number })
}
}
}
Status.SUCCESS -> {
Timber.i("Loading completed lessons lessons result: Success")
view?.apply {
@ -140,7 +150,7 @@ class CompletedLessonsPresenter @Inject constructor(
}
}.afterLoading {
view?.run {
hideRefresh()
showRefresh(false)
showProgress(false)
enableSwipe(true)
}
@ -158,7 +168,9 @@ class CompletedLessonsPresenter @Inject constructor(
}
}
private fun reloadView() {
private fun reloadView(date: LocalDate) {
currentDate = date
Timber.i("Reload completed lessons view with the date ${currentDate.toFormattedString()}")
view?.apply {
showProgress(true)

View File

@ -16,7 +16,7 @@ interface CompletedLessonsView : BaseView {
fun updateNavigationDay(date: String)
fun hideRefresh()
fun showRefresh(show: Boolean)
fun showEmpty(show: Boolean)

View File

@ -84,7 +84,7 @@ fun <T> flowWithResourceIn(block: suspend () -> Flow<Resource<T>>) = flow {
block()
.catch { emit(Resource.error(it)) }
.collect {
if (it.status != Status.LOADING) { // LOADING is already emitted
if (it.status != Status.LOADING || (it.status == Status.LOADING && it.data != null)) { // LOADING without data is already emitted
emit(it)
}
}

View File

@ -0,0 +1,50 @@
package io.github.wulkanowy.utils
import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import io.github.wulkanowy.R
import io.github.wulkanowy.data.db.SharedPrefProvider
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.LocalDate
import java.time.LocalDateTime
import javax.inject.Inject
fun getRefreshKey(name: String, semester: Semester, start: LocalDate, end: LocalDate): String {
return "${name}_${semester.studentId}_${semester.semesterId}_${start.monday}_${end.sunday}"
}
fun getRefreshKey(name: String, semester: Semester): String {
return "${name}_${semester.studentId}_${semester.semesterId}"
}
fun getRefreshKey(name: String, student: Student): String {
return "${name}_${student.userLoginId}"
}
fun getRefreshKey(name: String, student: Student, folder: MessageFolder): String {
return "${name}_${student.id}_${folder.id}"
}
class AutoRefreshHelper @Inject constructor(
@ApplicationContext private val context: Context,
private val sharedPref: SharedPrefProvider
) {
fun isShouldBeRefreshed(key: String): Boolean {
val timestamp = sharedPref.getLong(key, 0).toLocalDateTime()
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)
Timber.d("Check if $key need to be refreshed: $shouldBeRefreshed (last refresh: $timestamp, interval: $servicesInterval min)")
return shouldBeRefreshed
}
fun updateLastRefreshTimestamp(key: String) {
sharedPref.putLong(key, LocalDateTime.now().toTimestamp())
}
}

View File

@ -5,11 +5,13 @@ import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.getSemesterEntity
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.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
@ -30,6 +32,9 @@ class AttendanceRepositoryTest {
@MockK
private lateinit var attendanceDb: AttendanceDao
@MockK(relaxUnitFun = true)
private lateinit var refreshHelper: AutoRefreshHelper
private val semester = getSemesterEntity()
private val student = getStudentEntity()
@ -48,8 +53,9 @@ class AttendanceRepositoryTest {
@Before
fun setUp() {
MockKAnnotations.init(this)
every { refreshHelper.isShouldBeRefreshed(any()) } returns false
attendanceRepository = AttendanceRepository(attendanceDb, sdk)
attendanceRepository = AttendanceRepository(attendanceDb, sdk, refreshHelper)
}
@Test

View File

@ -5,11 +5,13 @@ import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.getSemesterEntity
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.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
@ -30,6 +32,9 @@ class CompletedLessonsRepositoryTest {
@MockK
private lateinit var completedLessonDb: CompletedLessonsDao
@MockK(relaxUnitFun = true)
private lateinit var refreshHelper: AutoRefreshHelper
private val semester = getSemesterEntity()
private val student = getStudentEntity()
@ -48,8 +53,9 @@ class CompletedLessonsRepositoryTest {
@Before
fun initApi() {
MockKAnnotations.init(this)
every { refreshHelper.isShouldBeRefreshed(any()) } returns false
completedLessonRepository = CompletedLessonsRepository(completedLessonDb, sdk)
completedLessonRepository = CompletedLessonsRepository(completedLessonDb, sdk, refreshHelper)
}
@Test

View File

@ -5,11 +5,13 @@ import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.getSemesterEntity
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.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
@ -29,6 +31,9 @@ class ExamRemoteTest {
@MockK
private lateinit var examDb: ExamDao
@MockK(relaxUnitFun = true)
private lateinit var refreshHelper: AutoRefreshHelper
private val semester = getSemesterEntity()
private val student = getStudentEntity()
@ -49,8 +54,9 @@ class ExamRemoteTest {
@Before
fun setUp() {
MockKAnnotations.init(this)
every { refreshHelper.isShouldBeRefreshed(any()) } returns false
examRepository = ExamRepository(examDb, sdk)
examRepository = ExamRepository(examDb, sdk, refreshHelper)
}
@Test

View File

@ -6,11 +6,13 @@ import io.github.wulkanowy.data.mappers.mapToEntities
import io.github.wulkanowy.getSemesterEntity
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.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
@ -36,6 +38,9 @@ class GradeRepositoryTest {
@MockK
private lateinit var gradeSummaryDb: GradeSummaryDao
@MockK(relaxUnitFun = true)
private lateinit var refreshHelper: AutoRefreshHelper
private val semester = getSemesterEntity()
private val student = getStudentEntity()
@ -45,8 +50,9 @@ class GradeRepositoryTest {
@Before
fun initApi() {
MockKAnnotations.init(this)
every { refreshHelper.isShouldBeRefreshed(any()) } returns false
gradeRepository = GradeRepository(gradeDb, gradeSummaryDb, sdk)
gradeRepository = GradeRepository(gradeDb, gradeSummaryDb, sdk, refreshHelper)
coEvery { gradeDb.deleteAll(any()) } just Runs
coEvery { gradeDb.insertAll(any()) } returns listOf()

View File

@ -9,11 +9,13 @@ import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.GradeStatisticsItem
import io.github.wulkanowy.sdk.pojo.GradeStatisticsSubject
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.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
@ -37,6 +39,9 @@ class GradeStatisticsRepositoryTest {
@MockK
private lateinit var gradeSemesterStatisticsDb: GradeSemesterStatisticsDao
@MockK(relaxUnitFun = true)
private lateinit var refreshHelper: AutoRefreshHelper
private val semester = getSemesterEntity()
private val student = getStudentEntity()
@ -51,8 +56,9 @@ class GradeStatisticsRepositoryTest {
@Before
fun setUp() {
MockKAnnotations.init(this)
every { refreshHelper.isShouldBeRefreshed(any()) } returns false
gradeStatisticsRepository = GradeStatisticsRepository(gradePartialStatisticsDb, gradePointsStatisticsDb, gradeSemesterStatisticsDb, sdk)
gradeStatisticsRepository = GradeStatisticsRepository(gradePartialStatisticsDb, gradePointsStatisticsDb, gradeSemesterStatisticsDb, sdk, refreshHelper)
}
@Test

View File

@ -8,11 +8,13 @@ import io.github.wulkanowy.data.db.entities.MessageWithAttachment
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.MessageDetails
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.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
@ -36,6 +38,9 @@ class MessageRepositoryTest {
@MockK
private lateinit var messageAttachmentDao: MessageAttachmentDao
@MockK(relaxUnitFun = true)
private lateinit var refreshHelper: AutoRefreshHelper
private val student = getStudentEntity()
private lateinit var messageRepository: MessageRepository
@ -43,8 +48,9 @@ class MessageRepositoryTest {
@Before
fun setUp() {
MockKAnnotations.init(this)
every { refreshHelper.isShouldBeRefreshed(any()) } returns false
messageRepository = MessageRepository(messageDb, messageAttachmentDao, sdk)
messageRepository = MessageRepository(messageDb, messageAttachmentDao, sdk, refreshHelper)
}
@Test(expected = NoSuchElementException::class)

View File

@ -6,11 +6,13 @@ import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.sdk.pojo.Device
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.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
@ -29,6 +31,9 @@ class MobileDeviceRepositoryTest {
@MockK
private lateinit var mobileDeviceDb: MobileDeviceDao
@MockK(relaxUnitFun = true)
private lateinit var refreshHelper: AutoRefreshHelper
private val semester = getSemesterEntity()
private val student = getStudentEntity()
@ -43,8 +48,9 @@ class MobileDeviceRepositoryTest {
@Before
fun initTest() {
MockKAnnotations.init(this)
every { refreshHelper.isShouldBeRefreshed(any()) } returns false
mobileDeviceRepository = MobileDeviceRepository(mobileDeviceDb, sdk)
mobileDeviceRepository = MobileDeviceRepository(mobileDeviceDb, sdk, refreshHelper)
}
@Test

View File

@ -7,11 +7,13 @@ import io.github.wulkanowy.getSemesterEntity
import io.github.wulkanowy.getStudentEntity
import io.github.wulkanowy.sdk.Sdk
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
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.impl.annotations.MockK
import io.mockk.impl.annotations.SpyK
import io.mockk.just
@ -39,6 +41,9 @@ class TimetableRepositoryTest {
@MockK
private lateinit var timetableAdditionalDao: TimetableAdditionalDao
@MockK(relaxUnitFun = true)
private lateinit var refreshHelper: AutoRefreshHelper
private val student = getStudentEntity()
private val semester = getSemesterEntity()
@ -52,7 +57,9 @@ class TimetableRepositoryTest {
@Before
fun initApi() {
MockKAnnotations.init(this)
timetableRepository = TimetableRepository(timetableDb, timetableAdditionalDao, sdk, timetableNotificationSchedulerHelper)
every { refreshHelper.isShouldBeRefreshed(any()) } returns false
timetableRepository = TimetableRepository(timetableDb, timetableAdditionalDao, sdk, timetableNotificationSchedulerHelper, refreshHelper)
}
@Test