diff --git a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt index 611161e5..aca28732 100644 --- a/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt +++ b/app/src/androidTest/java/io/github/wulkanowy/data/db/migrations/AbstractMigrationTest.kt @@ -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() 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 diff --git a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt index 4a4aaf74..9301d5fa 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/SharedPrefProvider.kt @@ -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" diff --git a/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt b/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt index 8c03609d..097ad7c8 100644 --- a/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt +++ b/app/src/main/java/io/github/wulkanowy/data/db/dao/CompletedLessonsDao.kt @@ -12,5 +12,5 @@ import javax.inject.Singleton interface CompletedLessonsDao : BaseDao { @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> + fun loadAll(studentId: Int, diaryId: Int, from: LocalDate, end: LocalDate): Flow> } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt index 699a325d..9a6528f3 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceRepository.kt @@ -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 } } ) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt index 4aa51fba..4edb507b 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/AttendanceSummaryRepository.kt @@ -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)) } ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt index e9d6de6a..59aabdd5 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepository.kt @@ -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 } } ) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt index 3a0d944b..befcf9e6 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ConferenceRepository.kt @@ -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)) } ) } diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt index 63e11807..bd6e7d2d 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/ExamRepository.kt @@ -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 } } ) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt index d7bdc564..bab290f3 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeRepository.kt @@ -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)) } ) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt index 70c0ada6..ab65fb14 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepository.kt @@ -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) { diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt index cec0d7f6..7625dbbc 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/HomeworkRepository.kt @@ -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)) } ) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt index a203e659..ea7b2b0e 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MessageRepository.kt @@ -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)) } ) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt index bbcf7f53..d77dccca 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/MobileDeviceRepository.kt @@ -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)) } ) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt index 9009d887..85789f09 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/NoteRepository.kt @@ -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)) } ) diff --git a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt index 84eca78f..fa1898f5 100644 --- a/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt +++ b/app/src/main/java/io/github/wulkanowy/data/repositories/TimetableRepository.kt @@ -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 -> diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt index c7caef06..a106bc50 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceFragment.kt @@ -191,8 +191,8 @@ class AttendanceFragment : BaseFragment(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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt index 9c4a8807..9201d1ab 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendancePresenter.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt index d54fb8bf..0459dfcf 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/AttendanceView.kt @@ -24,7 +24,7 @@ interface AttendanceView : BaseView { fun clearData() - fun hideRefresh() + fun showRefresh(show: Boolean) fun resetView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt index 2f862237..6b971f26 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryFragment.kt @@ -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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt index 242b0938..e53cda74 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryPresenter.kt @@ -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) = 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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt index dd4053c7..66f370c5 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/attendance/summary/AttendanceSummaryView.kt @@ -9,7 +9,7 @@ interface AttendanceSummaryView : BaseView { fun initView() - fun hideRefresh() + fun showRefresh(show: Boolean) fun showContent(show: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt index 74d93897..6173c15b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceFragment.kt @@ -67,8 +67,8 @@ class ConferenceFragment : BaseFragment(R.layout.frag } } - override fun hideRefresh() { - binding.conferenceSwipe.isRefreshing = false + override fun showRefresh(show: Boolean) { + binding.conferenceSwipe.isRefreshing = show } override fun showProgress(show: Boolean) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt index 125aca2f..cc7e50db 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferencePresenter.kt @@ -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) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt index 37845a6f..f3d1b3b3 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/conference/ConferenceView.kt @@ -13,7 +13,7 @@ interface ConferenceView : BaseView { fun clearData() - fun hideRefresh() + fun showRefresh(show: Boolean) fun showEmpty(show: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt index 000da00f..9cc1aeda 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamFragment.kt @@ -65,8 +65,8 @@ class ExamFragment : BaseFragment(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>) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt index 7234b7e9..b70a648f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamPresenter.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt index 00429bae..ac1a87fe 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/exam/ExamView.kt @@ -15,7 +15,7 @@ interface ExamView : BaseView { fun clearData() - fun hideRefresh() + fun showRefresh(show: Boolean) fun resetView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt index 782006ac..9f0b237f 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/details/GradeDetailsPresenter.kt @@ -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) { + 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) { 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 f94edd8a..37f47869 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 @@ -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, 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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt index a3f33dd9..14d75d7c 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/grade/summary/GradeSummaryPresenter.kt @@ -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!!) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt index e1d7ac52..85173e91 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkFragment.kt @@ -83,8 +83,8 @@ class HomeworkFragment : BaseFragment(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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt index 266b00b7..11c54dc2 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkPresenter.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt index 2a678cd4..a1d6a04a 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/homework/HomeworkView.kt @@ -15,7 +15,7 @@ interface HomeworkView : BaseView { fun updateNavigationWeek(date: String) - fun hideRefresh() + fun showRefresh(show: Boolean) fun showEmpty(show: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt index 5a8ad211..c560a77d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/message/tab/MessageTabPresenter.kt @@ -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!! diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt index 8065e9b6..48737d7b 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceFragment.kt @@ -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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt index dce670a3..9591867d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDevicePresenter.kt @@ -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) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt index ec2d3f87..b94646a7 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/mobiledevice/MobileDeviceView.kt @@ -17,7 +17,7 @@ interface MobileDeviceView : BaseView { fun showUndo(device: MobileDevice, position: Int) - fun hideRefresh() + fun showRefresh(show: Boolean) fun showProgress(show: Boolean) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt index 079285a8..40378265 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteFragment.kt @@ -105,8 +105,8 @@ class NoteFragment : BaseFragment(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() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt index ed394533..e80f5494 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NotePresenter.kt @@ -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) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt index a7cbab8f..9fc0be94 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/note/NoteView.kt @@ -27,7 +27,7 @@ interface NoteView : BaseView { fun showContent(show: Boolean) - fun hideRefresh() + fun showRefresh(show: Boolean) fun showNoteDialog(note: Note) } diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt index a2141d86..1bb9c920 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableFragment.kt @@ -113,8 +113,8 @@ class TimetableFragment : BaseFragment(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() { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt index 3d9838a3..3cd15bcf 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetablePresenter.kt @@ -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(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) { + view?.updateData( + showWholeClassPlanType = prefRepository.showWholeClassPlan, + showGroupsInPlanType = prefRepository.showGroupsInPlan, + showTimetableTimers = prefRepository.showTimetableTimers, + data = createItems(lessons) + ) + } + + private fun createItems(items: List) = 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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt index 30135453..c6bceb9e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableView.kt @@ -18,7 +18,7 @@ interface TimetableView : BaseView { fun clearData() - fun hideRefresh() + fun showRefresh(show: Boolean) fun resetView() diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt index 5b77fc1f..828e1001 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsFragment.kt @@ -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) { diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt index 7f9799b7..04e2a5cd 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsPresenter.kt @@ -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) diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt index 7e92cc63..7a98874e 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/completed/CompletedLessonsView.kt @@ -16,7 +16,7 @@ interface CompletedLessonsView : BaseView { fun updateNavigationDay(date: String) - fun hideRefresh() + fun showRefresh(show: Boolean) fun showEmpty(show: Boolean) 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 d3bc40c0..2d074b22 100644 --- a/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt +++ b/app/src/main/java/io/github/wulkanowy/utils/FlowUtils.kt @@ -84,7 +84,7 @@ fun flowWithResourceIn(block: suspend () -> Flow>) = 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) } } diff --git a/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt new file mode 100644 index 00000000..cd59b864 --- /dev/null +++ b/app/src/main/java/io/github/wulkanowy/utils/RefreshUtils.kt @@ -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()) + } +} diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt index c6c3613e..4c6a1172 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/AttendanceRepositoryTest.kt @@ -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 diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt index 7b20e647..461e1809 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/CompletedLessonsRepositoryTest.kt @@ -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 diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt index 5966bc42..42a89707 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/ExamRemoteTest.kt @@ -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 diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt index d6516a35..002c7ad7 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeRepositoryTest.kt @@ -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() diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt index 25415562..73dd4cfa 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/GradeStatisticsRepositoryTest.kt @@ -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 diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt index 1149a2f0..2fc899b3 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MessageRepositoryTest.kt @@ -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) diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt index f4d6dd4f..4a4f2c76 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/MobileDeviceRepositoryTest.kt @@ -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 diff --git a/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt b/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt index 20826f00..1e000e84 100644 --- a/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt +++ b/app/src/test/java/io/github/wulkanowy/data/repositories/TimetableRepositoryTest.kt @@ -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