forked from github/wulkanowy-mirror
Display day header from website in timetable (#1269)
This commit is contained in:
parent
7bc5219d81
commit
aeb3b2a030
@ -160,7 +160,7 @@ ext {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "io.github.wulkanowy:sdk:1.1.3"
|
||||
implementation "io.github.wulkanowy:sdk:877b9135"
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
|
||||
|
||||
|
2204
app/schemas/io.github.wulkanowy.data.db.AppDatabase/37.json
Normal file
2204
app/schemas/io.github.wulkanowy.data.db.AppDatabase/37.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -181,4 +181,8 @@ internal class RepositoryModule {
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideStudentInfoDao(database: AppDatabase) = database.studentInfoDao
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideTimetableHeaderDao(database: AppDatabase) = database.timetableHeaderDao
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import io.github.wulkanowy.data.db.dao.SubjectDao
|
||||
import io.github.wulkanowy.data.db.dao.TeacherDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
|
||||
import io.github.wulkanowy.data.db.entities.Attendance
|
||||
import io.github.wulkanowy.data.db.entities.AttendanceSummary
|
||||
import io.github.wulkanowy.data.db.entities.CompletedLesson
|
||||
@ -58,6 +59,7 @@ import io.github.wulkanowy.data.db.entities.Subject
|
||||
import io.github.wulkanowy.data.db.entities.Teacher
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||
import io.github.wulkanowy.data.db.entities.TimetableHeader
|
||||
import io.github.wulkanowy.data.db.migrations.Migration10
|
||||
import io.github.wulkanowy.data.db.migrations.Migration11
|
||||
import io.github.wulkanowy.data.db.migrations.Migration12
|
||||
@ -87,6 +89,7 @@ import io.github.wulkanowy.data.db.migrations.Migration33
|
||||
import io.github.wulkanowy.data.db.migrations.Migration34
|
||||
import io.github.wulkanowy.data.db.migrations.Migration35
|
||||
import io.github.wulkanowy.data.db.migrations.Migration36
|
||||
import io.github.wulkanowy.data.db.migrations.Migration37
|
||||
import io.github.wulkanowy.data.db.migrations.Migration4
|
||||
import io.github.wulkanowy.data.db.migrations.Migration5
|
||||
import io.github.wulkanowy.data.db.migrations.Migration6
|
||||
@ -125,6 +128,7 @@ import javax.inject.Singleton
|
||||
Conference::class,
|
||||
TimetableAdditional::class,
|
||||
StudentInfo::class,
|
||||
TimetableHeader::class,
|
||||
],
|
||||
version = AppDatabase.VERSION_SCHEMA,
|
||||
exportSchema = true
|
||||
@ -133,7 +137,7 @@ import javax.inject.Singleton
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
|
||||
companion object {
|
||||
const val VERSION_SCHEMA = 36
|
||||
const val VERSION_SCHEMA = 37
|
||||
|
||||
fun getMigrations(sharedPrefProvider: SharedPrefProvider, appInfo: AppInfo) = arrayOf(
|
||||
Migration2(),
|
||||
@ -170,7 +174,8 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
Migration33(),
|
||||
Migration34(),
|
||||
Migration35(appInfo),
|
||||
Migration36()
|
||||
Migration36(),
|
||||
Migration37(),
|
||||
)
|
||||
|
||||
fun newInstance(
|
||||
@ -236,4 +241,6 @@ abstract class AppDatabase : RoomDatabase() {
|
||||
abstract val timetableAdditionalDao: TimetableAdditionalDao
|
||||
|
||||
abstract val studentInfoDao: StudentInfoDao
|
||||
|
||||
abstract val timetableHeaderDao: TimetableHeaderDao
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package io.github.wulkanowy.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import io.github.wulkanowy.data.db.entities.TimetableHeader
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Dao
|
||||
@Singleton
|
||||
interface TimetableHeaderDao : BaseDao<TimetableHeader> {
|
||||
|
||||
@Query("SELECT * FROM TimetableHeaders 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<TimetableHeader>>
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package io.github.wulkanowy.data.db.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.io.Serializable
|
||||
import java.time.LocalDate
|
||||
|
||||
@Entity(tableName = "TimetableHeaders")
|
||||
data class TimetableHeader(
|
||||
|
||||
@ColumnInfo(name = "student_id")
|
||||
val studentId: Int,
|
||||
|
||||
@ColumnInfo(name = "diary_id")
|
||||
val diaryId: Int,
|
||||
|
||||
val date: LocalDate,
|
||||
|
||||
val content: String,
|
||||
) : Serializable {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Long = 0
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package io.github.wulkanowy.data.db.migrations
|
||||
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
class Migration37 : Migration(36, 37) {
|
||||
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS TimetableHeaders (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
student_id INTEGER NOT NULL,
|
||||
diary_id INTEGER NOT NULL,
|
||||
date INTEGER NOT NULL,
|
||||
content TEXT NOT NULL
|
||||
)
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
@ -3,9 +3,19 @@ package io.github.wulkanowy.data.mappers
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||
import io.github.wulkanowy.data.db.entities.TimetableHeader
|
||||
import io.github.wulkanowy.data.pojos.TimetableFull
|
||||
import io.github.wulkanowy.sdk.pojo.TimetableFull as SdkTimetableFull
|
||||
import io.github.wulkanowy.sdk.pojo.TimetableDayHeader as SdkTimetableHeader
|
||||
import io.github.wulkanowy.sdk.pojo.Timetable as SdkTimetable
|
||||
import io.github.wulkanowy.sdk.pojo.TimetableAdditional as SdkTimetableAdditional
|
||||
|
||||
fun SdkTimetableFull.mapToEntities(semester: Semester) = TimetableFull(
|
||||
lessons = lessons.mapToEntities(semester),
|
||||
additional = additional.mapToEntities(semester),
|
||||
headers = headers.mapToEntities(semester)
|
||||
)
|
||||
|
||||
fun List<SdkTimetable>.mapToEntities(semester: Semester) = map {
|
||||
Timetable(
|
||||
studentId = semester.studentId,
|
||||
@ -39,3 +49,13 @@ fun List<SdkTimetableAdditional>.mapToEntities(semester: Semester) = map {
|
||||
end = it.end
|
||||
)
|
||||
}
|
||||
|
||||
@JvmName("mapToEntitiesTimetableHeaders")
|
||||
fun List<SdkTimetableHeader>.mapToEntities(semester: Semester) = map {
|
||||
TimetableHeader(
|
||||
studentId = semester.studentId,
|
||||
diaryId = semester.diaryId,
|
||||
date = it.date,
|
||||
content = it.content
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package io.github.wulkanowy.data.pojos
|
||||
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||
import io.github.wulkanowy.data.db.entities.TimetableHeader
|
||||
|
||||
data class TimetableFull(
|
||||
val lessons: List<Timetable>,
|
||||
val additional: List<TimetableAdditional>,
|
||||
val headers: List<TimetableHeader>,
|
||||
)
|
@ -2,11 +2,14 @@ package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
|
||||
import io.github.wulkanowy.data.db.entities.Semester
|
||||
import io.github.wulkanowy.data.db.entities.Student
|
||||
import io.github.wulkanowy.data.db.entities.Timetable
|
||||
import io.github.wulkanowy.data.db.entities.TimetableAdditional
|
||||
import io.github.wulkanowy.data.db.entities.TimetableHeader
|
||||
import io.github.wulkanowy.data.mappers.mapToEntities
|
||||
import io.github.wulkanowy.data.pojos.TimetableFull
|
||||
import io.github.wulkanowy.sdk.Sdk
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
@ -16,8 +19,8 @@ import io.github.wulkanowy.utils.monday
|
||||
import io.github.wulkanowy.utils.networkBoundResource
|
||||
import io.github.wulkanowy.utils.sunday
|
||||
import io.github.wulkanowy.utils.uniqueSubtract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import java.time.LocalDate
|
||||
import javax.inject.Inject
|
||||
@ -27,6 +30,7 @@ import javax.inject.Singleton
|
||||
class TimetableRepository @Inject constructor(
|
||||
private val timetableDb: TimetableDao,
|
||||
private val timetableAdditionalDb: TimetableAdditionalDao,
|
||||
private val timetableHeaderDb: TimetableHeaderDao,
|
||||
private val sdk: Sdk,
|
||||
private val schedulerHelper: TimetableNotificationSchedulerHelper,
|
||||
private val refreshHelper: AutoRefreshHelper,
|
||||
@ -36,53 +40,111 @@ class TimetableRepository @Inject constructor(
|
||||
|
||||
private val cacheKey = "timetable"
|
||||
|
||||
fun getTimetable(student: Student, semester: Semester, start: LocalDate, end: LocalDate, forceRefresh: Boolean, refreshAdditional: Boolean = false) = networkBoundResource(
|
||||
fun getTimetable(
|
||||
student: Student, semester: Semester, start: LocalDate, end: LocalDate,
|
||||
forceRefresh: Boolean, refreshAdditional: Boolean = false
|
||||
) = networkBoundResource(
|
||||
mutex = saveFetchResultMutex,
|
||||
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 }
|
||||
.combine(timetableAdditionalDb.loadAll(semester.diaryId, semester.studentId, start.monday, end.sunday)) { timetable, additional ->
|
||||
timetable to additional
|
||||
}
|
||||
},
|
||||
fetch = {
|
||||
sdk.init(student).switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getTimetable(start.monday, end.sunday)
|
||||
.let { (normal, additional) -> normal.mapToEntities(semester) to additional.mapToEntities(semester) }
|
||||
shouldFetch = { (timetable, additional, headers) ->
|
||||
val refreshKey = getRefreshKey(cacheKey, semester, start, end)
|
||||
val isShouldRefresh = refreshHelper.isShouldBeRefreshed(refreshKey)
|
||||
val isRefreshAdditional = additional.isEmpty() && refreshAdditional
|
||||
|
||||
val isNoData = timetable.isEmpty() || isRefreshAdditional || headers.isEmpty()
|
||||
|
||||
isNoData || forceRefresh || isShouldRefresh
|
||||
},
|
||||
saveFetchResult = { (oldTimetable, oldAdditional), (newTimetable, newAdditional) ->
|
||||
refreshTimetable(student, oldTimetable, newTimetable)
|
||||
refreshAdditional(oldAdditional, newAdditional)
|
||||
query = { getFullTimetableFromDatabase(student, semester, start, end) },
|
||||
fetch = {
|
||||
val timetableFull = sdk.init(student)
|
||||
.switchDiary(semester.diaryId, semester.schoolYear)
|
||||
.getTimetableFull(start.monday, end.sunday)
|
||||
|
||||
timetableFull.mapToEntities(semester)
|
||||
},
|
||||
saveFetchResult = { timetableOld, timetableNew ->
|
||||
refreshTimetable(student, timetableOld.lessons, timetableNew.lessons)
|
||||
refreshAdditional(timetableOld.additional, timetableNew.additional)
|
||||
refreshDayHeaders(timetableOld.headers, timetableNew.headers)
|
||||
|
||||
refreshHelper.updateLastRefreshTimestamp(getRefreshKey(cacheKey, semester, start, end))
|
||||
},
|
||||
filterResult = { (timetable, additional) ->
|
||||
timetable.filter { item ->
|
||||
item.date in start..end
|
||||
} to additional.filter { item ->
|
||||
item.date in start..end
|
||||
}
|
||||
filterResult = { (timetable, additional, headers) ->
|
||||
TimetableFull(
|
||||
lessons = timetable.filter { it.date in start..end },
|
||||
additional = additional.filter { it.date in start..end },
|
||||
headers = headers.filter { it.date in start..end }
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
private suspend fun refreshTimetable(student: Student, old: List<Timetable>, new: List<Timetable>) {
|
||||
timetableDb.deleteAll(old.uniqueSubtract(new).also { schedulerHelper.cancelScheduled(it) })
|
||||
timetableDb.insertAll(new.uniqueSubtract(old).also { schedulerHelper.scheduleNotifications(it, student) }.map { item ->
|
||||
item.also { new ->
|
||||
old.singleOrNull { new.start == it.start }?.let { old ->
|
||||
return@map new.copy(
|
||||
room = if (new.room.isEmpty()) old.room else new.room,
|
||||
teacher = if (new.teacher.isEmpty() && !new.changes && !old.changes) old.teacher else new.teacher
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
private fun getFullTimetableFromDatabase(
|
||||
student: Student, semester: Semester,
|
||||
start: LocalDate, end: LocalDate
|
||||
): Flow<TimetableFull> {
|
||||
val timetableFlow = timetableDb.loadAll(
|
||||
diaryId = semester.diaryId,
|
||||
studentId = semester.studentId,
|
||||
from = start.monday,
|
||||
end = end.sunday
|
||||
)
|
||||
val headersFlow = timetableHeaderDb.loadAll(
|
||||
diaryId = semester.diaryId,
|
||||
studentId = semester.studentId,
|
||||
from = start.monday,
|
||||
end = end.sunday
|
||||
)
|
||||
val additionalFlow = timetableAdditionalDb.loadAll(
|
||||
diaryId = semester.diaryId,
|
||||
studentId = semester.studentId,
|
||||
from = start.monday,
|
||||
end = end.sunday
|
||||
)
|
||||
return combine(timetableFlow, headersFlow, additionalFlow) { lessons, headers, additional ->
|
||||
schedulerHelper.scheduleNotifications(lessons, student)
|
||||
|
||||
TimetableFull(
|
||||
lessons = lessons,
|
||||
headers = headers,
|
||||
additional = additional
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun refreshAdditional(old: List<TimetableAdditional>, new: List<TimetableAdditional>) {
|
||||
timetableAdditionalDb.deleteAll(old.uniqueSubtract(new))
|
||||
timetableAdditionalDb.insertAll(new.uniqueSubtract(old))
|
||||
private suspend fun refreshTimetable(
|
||||
student: Student,
|
||||
lessonsOld: List<Timetable>, lessonsNew: List<Timetable>
|
||||
) {
|
||||
val lessonsToRemove = lessonsOld uniqueSubtract lessonsNew
|
||||
val lessonsToAdd = (lessonsNew uniqueSubtract lessonsOld).map { new ->
|
||||
val matchingOld = lessonsOld.singleOrNull { new.start == it.start }
|
||||
if (matchingOld != null) {
|
||||
val useOldTeacher = new.teacher.isEmpty() && !new.changes && !matchingOld.changes
|
||||
new.copy(
|
||||
room = if (new.room.isEmpty()) matchingOld.room else new.room,
|
||||
teacher = if (useOldTeacher) matchingOld.teacher
|
||||
else new.teacher
|
||||
)
|
||||
} else new
|
||||
}
|
||||
|
||||
timetableDb.deleteAll(lessonsToRemove)
|
||||
timetableDb.insertAll(lessonsToAdd)
|
||||
|
||||
schedulerHelper.cancelScheduled(lessonsToRemove, student)
|
||||
schedulerHelper.scheduleNotifications(lessonsToAdd, student)
|
||||
}
|
||||
|
||||
private suspend fun refreshAdditional(
|
||||
old: List<TimetableAdditional>,
|
||||
new: List<TimetableAdditional>
|
||||
) {
|
||||
timetableAdditionalDb.deleteAll(old uniqueSubtract new)
|
||||
timetableAdditionalDb.insertAll(new uniqueSubtract old)
|
||||
}
|
||||
|
||||
private suspend fun refreshDayHeaders(old: List<TimetableHeader>, new: List<TimetableHeader>) {
|
||||
timetableHeaderDb.deleteAll(old uniqueSubtract new)
|
||||
timetableHeaderDb.insertAll(new uniqueSubtract old)
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,8 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
||||
lesson: Timetable
|
||||
) = day.getOrNull(index - 1)?.end ?: lesson.start.minusMinutes(30)
|
||||
|
||||
suspend fun cancelScheduled(lessons: List<Timetable>, studentId: Int = 1) {
|
||||
suspend fun cancelScheduled(lessons: List<Timetable>, student: Student) {
|
||||
val studentId = student.studentId
|
||||
withContext(dispatchersProvider.backgroundThread) {
|
||||
lessons.sortedBy { it.start }.forEachIndexed { index, lesson ->
|
||||
val upcomingTime = getUpcomingLessonTime(index, lessons, lesson)
|
||||
@ -78,7 +79,7 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
||||
|
||||
suspend fun scheduleNotifications(lessons: List<Timetable>, student: Student) {
|
||||
if (!preferencesRepository.isUpcomingLessonsNotificationsEnable) {
|
||||
return cancelScheduled(lessons, student.studentId)
|
||||
return cancelScheduled(lessons, student)
|
||||
}
|
||||
|
||||
withContext(dispatchersProvider.backgroundThread) {
|
||||
@ -89,7 +90,7 @@ class TimetableNotificationSchedulerHelper @Inject constructor(
|
||||
val canceled = day.filter { it.canceled }
|
||||
val active = day.filter { !it.canceled }
|
||||
|
||||
cancelScheduled(canceled)
|
||||
cancelScheduled(canceled, student)
|
||||
active.forEachIndexed { index, lesson ->
|
||||
val intent = createIntent(student, lesson, active.getOrNull(index + 1))
|
||||
|
||||
|
@ -7,6 +7,7 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
@ -71,7 +72,9 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
||||
with(binding) {
|
||||
timetableSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
|
||||
timetableSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
|
||||
timetableSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh))
|
||||
timetableSwipe.setProgressBackgroundColorSchemeColor(
|
||||
requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)
|
||||
)
|
||||
timetableErrorRetry.setOnClickListener { presenter.onRetry() }
|
||||
timetableErrorDetails.setOnClickListener { presenter.onDetailsClick() }
|
||||
|
||||
@ -95,7 +98,12 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateData(data: List<Timetable>, showWholeClassPlanType: String, showGroupsInPlanType: Boolean, showTimetableTimers: Boolean) {
|
||||
override fun updateData(
|
||||
data: List<Timetable>,
|
||||
showWholeClassPlanType: String,
|
||||
showGroupsInPlanType: Boolean,
|
||||
showTimetableTimers: Boolean
|
||||
) {
|
||||
with(timetableAdapter) {
|
||||
items = data.toMutableList()
|
||||
showTimers = showTimetableTimers
|
||||
@ -136,6 +144,13 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
||||
binding.timetableEmpty.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
|
||||
override fun setDayHeaderMessage(message: String?) {
|
||||
binding.timetableEmptyMessage.visibility = if (message.isNullOrEmpty()) GONE else VISIBLE
|
||||
binding.timetableEmptyMessage.text = HtmlCompat.fromHtml(
|
||||
message.orEmpty(), HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
)
|
||||
}
|
||||
|
||||
override fun showErrorView(show: Boolean) {
|
||||
binding.timetableError.visibility = if (show) VISIBLE else GONE
|
||||
}
|
||||
@ -172,8 +187,10 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
||||
val dateSetListener = DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth ->
|
||||
presenter.onDateSet(year, month + 1, dayOfMonth)
|
||||
}
|
||||
val datePickerDialog = DatePickerDialog.newInstance(dateSetListener,
|
||||
currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth)
|
||||
val datePickerDialog = DatePickerDialog.newInstance(
|
||||
dateSetListener,
|
||||
currentDate.year, currentDate.monthValue - 1, currentDate.dayOfMonth
|
||||
)
|
||||
|
||||
with(datePickerDialog) {
|
||||
setDateRangeLimiter(SchooldaysRangeLimiter())
|
||||
|
@ -138,32 +138,37 @@ class TimetablePresenter @Inject constructor(
|
||||
flowWithResourceIn {
|
||||
val student = studentRepository.getCurrentStudent()
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
timetableRepository.getTimetable(student, semester, currentDate, currentDate, forceRefresh)
|
||||
timetableRepository.getTimetable(
|
||||
student, semester, currentDate, currentDate, forceRefresh
|
||||
)
|
||||
}.onEach {
|
||||
when (it.status) {
|
||||
Status.LOADING -> {
|
||||
if (!it.data?.first.isNullOrEmpty()) {
|
||||
if (!it.data?.lessons.isNullOrEmpty()) {
|
||||
view?.run {
|
||||
enableSwipe(true)
|
||||
showRefresh(true)
|
||||
showProgress(false)
|
||||
showContent(true)
|
||||
updateData(it.data!!.first)
|
||||
updateData(it.data!!.lessons)
|
||||
}
|
||||
}
|
||||
}
|
||||
Status.SUCCESS -> {
|
||||
Timber.i("Loading timetable result: Success")
|
||||
view?.apply {
|
||||
updateData(it.data!!.first)
|
||||
showEmpty(it.data.first.isEmpty())
|
||||
updateData(it.data!!.lessons)
|
||||
showEmpty(it.data.lessons.isEmpty())
|
||||
setDayHeaderMessage(it.data.headers.singleOrNull { header ->
|
||||
header.date == currentDate
|
||||
}?.content)
|
||||
showErrorView(false)
|
||||
showContent(it.data.first.isNotEmpty())
|
||||
showContent(it.data.lessons.isNotEmpty())
|
||||
}
|
||||
analytics.logEvent(
|
||||
"load_data",
|
||||
"type" to "timetable",
|
||||
"items" to it.data!!.first.size
|
||||
"items" to it.data!!.lessons.size
|
||||
)
|
||||
}
|
||||
Status.ERROR -> {
|
||||
|
@ -24,6 +24,8 @@ interface TimetableView : BaseView {
|
||||
|
||||
fun showEmpty(show: Boolean)
|
||||
|
||||
fun setDayHeaderMessage(message: String?)
|
||||
|
||||
fun showErrorView(show: Boolean)
|
||||
|
||||
fun setErrorDetails(message: String)
|
||||
|
@ -110,15 +110,15 @@ class AdditionalLessonsPresenter @Inject constructor(
|
||||
Status.SUCCESS -> {
|
||||
Timber.i("Loading additional lessons lessons result: Success")
|
||||
view?.apply {
|
||||
updateData(it.data!!.second.sortedBy { item -> item.date })
|
||||
showEmpty(it.data.second.isEmpty())
|
||||
updateData(it.data!!.additional.sortedBy { item -> item.date })
|
||||
showEmpty(it.data.additional.isEmpty())
|
||||
showErrorView(false)
|
||||
showContent(it.data.second.isNotEmpty())
|
||||
showContent(it.data.additional.isNotEmpty())
|
||||
}
|
||||
analytics.logEvent(
|
||||
"load_data",
|
||||
"type" to "additional_lessons",
|
||||
"items" to it.data!!.second.size
|
||||
"items" to it.data!!.additional.size
|
||||
)
|
||||
}
|
||||
Status.ERROR -> {
|
||||
|
@ -107,7 +107,7 @@ class TimetableWidgetFactory(
|
||||
|
||||
val semester = semesterRepository.getCurrentSemester(student)
|
||||
timetableRepository.getTimetable(student, semester, date, date, false)
|
||||
.toFirstResult().data?.first.orEmpty()
|
||||
.toFirstResult().data?.lessons.orEmpty()
|
||||
.sortedWith(compareBy({ it.number }, { !it.isStudentPlan }))
|
||||
.filter { if (prefRepository.showWholeClassPlan == "no") it.isStudentPlan else true }
|
||||
}
|
||||
|
@ -15,7 +15,8 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true" />
|
||||
android:indeterminate="true"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/timetableSwipe"
|
||||
@ -26,7 +27,8 @@
|
||||
android:id="@+id/timetableRecycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:listitem="@layout/item_timetable" />
|
||||
tools:listitem="@layout/item_timetable"
|
||||
tools:visibility="visible" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<LinearLayout
|
||||
@ -53,6 +55,18 @@
|
||||
android:gravity="center"
|
||||
android:text="@string/timetable_no_items"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timetableEmptyMessage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:gravity="center"
|
||||
android:lineSpacingMultiplier="1.2"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="16sp"
|
||||
tools:maxLines="4"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
|
@ -121,7 +121,7 @@
|
||||
app:layout_constraintStart_toEndOf="@+id/timetableItemTeacher"
|
||||
app:layout_constraintTop_toTopOf="@+id/timetableItemTimeFinish"
|
||||
tools:text="Lekcja odwołana: uczniowie zwolnieni do domu"
|
||||
tools:visibility="visible" />
|
||||
tools:visibility="gone" />
|
||||
|
||||
<androidx.constraintlayout.widget.Barrier
|
||||
android:id="@+id/timetableItemTimeBarrier"
|
||||
|
@ -2,10 +2,12 @@ package io.github.wulkanowy.data.repositories
|
||||
|
||||
import io.github.wulkanowy.data.db.dao.TimetableAdditionalDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableDao
|
||||
import io.github.wulkanowy.data.db.dao.TimetableHeaderDao
|
||||
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.sdk.pojo.TimetableFull
|
||||
import io.github.wulkanowy.services.alarm.TimetableNotificationSchedulerHelper
|
||||
import io.github.wulkanowy.utils.AutoRefreshHelper
|
||||
import io.github.wulkanowy.utils.toFirstResult
|
||||
@ -41,6 +43,9 @@ class TimetableRepositoryTest {
|
||||
@MockK
|
||||
private lateinit var timetableAdditionalDao: TimetableAdditionalDao
|
||||
|
||||
@MockK
|
||||
private lateinit var timetableHeaderDao: TimetableHeaderDao
|
||||
|
||||
@MockK(relaxUnitFun = true)
|
||||
private lateinit var refreshHelper: AutoRefreshHelper
|
||||
|
||||
@ -59,7 +64,7 @@ class TimetableRepositoryTest {
|
||||
MockKAnnotations.init(this)
|
||||
every { refreshHelper.isShouldBeRefreshed(any()) } returns false
|
||||
|
||||
timetableRepository = TimetableRepository(timetableDb, timetableAdditionalDao, sdk, timetableNotificationSchedulerHelper, refreshHelper)
|
||||
timetableRepository = TimetableRepository(timetableDb, timetableAdditionalDao, timetableHeaderDao, sdk, timetableNotificationSchedulerHelper, refreshHelper)
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -71,7 +76,7 @@ class TimetableRepositoryTest {
|
||||
createTimetableRemote(of(2021, 1, 4, 9, 40), 3, "", "W-F"),
|
||||
createTimetableRemote(of(2021, 1, 4, 10, 30), 4, "", "W-F")
|
||||
)
|
||||
coEvery { sdk.getTimetable(any(), any()) } returns (remoteList to emptyList())
|
||||
coEvery { sdk.getTimetableFull(any(), any()) } returns TimetableFull(emptyList(), remoteList, emptyList())
|
||||
|
||||
val localList = listOf(
|
||||
createTimetableRemote(of(2021, 1, 4, 8, 0), 1, "123", "Przyroda"),
|
||||
@ -87,13 +92,17 @@ class TimetableRepositoryTest {
|
||||
coEvery { timetableAdditionalDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
|
||||
coEvery { timetableAdditionalDao.deleteAll(emptyList()) } just Runs
|
||||
|
||||
coEvery { timetableHeaderDao.loadAll(1, 1, startDate, endDate) } returns flowOf(listOf())
|
||||
coEvery { timetableHeaderDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
|
||||
coEvery { timetableHeaderDao.deleteAll(emptyList()) } just Runs
|
||||
|
||||
// execute
|
||||
val res = runBlocking {
|
||||
timetableRepository.getTimetable(student, semester, startDate, endDate, true).toFirstResult()
|
||||
}
|
||||
|
||||
// verify
|
||||
assertEquals(4, res.data?.first.orEmpty().size)
|
||||
assertEquals(4, res.data?.lessons.orEmpty().size)
|
||||
coVerify {
|
||||
timetableDb.insertAll(withArg {
|
||||
assertEquals(4, it.size)
|
||||
@ -124,7 +133,7 @@ class TimetableRepositoryTest {
|
||||
createTimetableRemote(of(2021, 1, 6, 9, 40), 3, "125", "Matematyka", "Paweł Środowski", false),
|
||||
createTimetableRemote(of(2021, 1, 6, 10, 40), 4, "126", "Matematyka", "Paweł Czwartkowski", true)
|
||||
)
|
||||
coEvery { sdk.getTimetable(startDate, endDate) } returns (remoteList to emptyList())
|
||||
coEvery { sdk.getTimetableFull(startDate, endDate) } returns TimetableFull(emptyList(), remoteList, emptyList())
|
||||
|
||||
val localList = listOf(
|
||||
createTimetableRemote(of(2021, 1, 4, 8, 0), 1, "123", "Matematyka", "Paweł Poniedziałkowski", false),
|
||||
@ -150,12 +159,16 @@ class TimetableRepositoryTest {
|
||||
coEvery { timetableAdditionalDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
|
||||
coEvery { timetableAdditionalDao.deleteAll(emptyList()) } just Runs
|
||||
|
||||
coEvery { timetableHeaderDao.loadAll(1, 1, startDate, endDate) } returns flowOf(listOf())
|
||||
coEvery { timetableHeaderDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
|
||||
coEvery { timetableHeaderDao.deleteAll(emptyList()) } just Runs
|
||||
|
||||
// execute
|
||||
val res = runBlocking { timetableRepository.getTimetable(student, semester, startDate, endDate, true).toFirstResult() }
|
||||
|
||||
// verify
|
||||
assertEquals(null, res.error)
|
||||
assertEquals(12, res.data!!.first.size)
|
||||
assertEquals(12, res.data!!.lessons.size)
|
||||
|
||||
coVerify {
|
||||
timetableDb.insertAll(withArg {
|
||||
@ -187,7 +200,7 @@ class TimetableRepositoryTest {
|
||||
)
|
||||
|
||||
// prepare
|
||||
coEvery { sdk.getTimetable(startDate, endDate) } returns (remoteList to emptyList())
|
||||
coEvery { sdk.getTimetableFull(startDate, endDate) } returns TimetableFull(emptyList(), remoteList, emptyList())
|
||||
coEvery { timetableDb.loadAll(1, 1, startDate, endDate) } returnsMany listOf(
|
||||
flowOf(remoteList.mapToEntities(semester)),
|
||||
flowOf(remoteList.mapToEntities(semester))
|
||||
@ -199,13 +212,17 @@ class TimetableRepositoryTest {
|
||||
coEvery { timetableAdditionalDao.deleteAll(emptyList()) } just Runs
|
||||
coEvery { timetableAdditionalDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
|
||||
|
||||
coEvery { timetableHeaderDao.loadAll(1, 1, startDate, endDate) } returns flowOf(listOf())
|
||||
coEvery { timetableHeaderDao.insertAll(emptyList()) } returns listOf(1, 2, 3)
|
||||
coEvery { timetableHeaderDao.deleteAll(emptyList()) } just Runs
|
||||
|
||||
// execute
|
||||
val res = runBlocking { timetableRepository.getTimetable(student, semester, startDate, endDate, true).toFirstResult() }
|
||||
|
||||
// verify
|
||||
assertEquals(null, res.error)
|
||||
assertEquals(2, res.data?.first?.size)
|
||||
coVerify { sdk.getTimetable(startDate, endDate) }
|
||||
assertEquals(2, res.data?.lessons?.size)
|
||||
coVerify { sdk.getTimetableFull(startDate, endDate) }
|
||||
coVerify { timetableDb.loadAll(1, 1, startDate, endDate) }
|
||||
coVerify { timetableDb.insertAll(match { it.isEmpty() }) }
|
||||
coVerify { timetableDb.deleteAll(match { it.isEmpty() }) }
|
||||
|
Loading…
x
Reference in New Issue
Block a user