mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-01-18 13:16:45 -06:00
Timetable timer refactor (#1785)
This commit is contained in:
parent
c3abe50ed4
commit
57ea6379ab
12
.editorconfig
Normal file
12
.editorconfig
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[*]
|
||||||
|
charset=utf-8
|
||||||
|
end_of_line=lf
|
||||||
|
insert_final_newline=true
|
||||||
|
indent_style=space
|
||||||
|
indent_size=4
|
||||||
|
|
||||||
|
[*.json]
|
||||||
|
indent_size=2
|
||||||
|
|
||||||
|
[*.{kt,kts}]
|
||||||
|
disabled_rules=import-ordering,no-wildcard-imports
|
@ -1,116 +1,69 @@
|
|||||||
package io.github.wulkanowy.ui.modules.timetable
|
package io.github.wulkanowy.ui.modules.timetable
|
||||||
|
|
||||||
import android.graphics.Paint
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View.GONE
|
import android.view.View.GONE
|
||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Timetable
|
import io.github.wulkanowy.data.db.entities.Timetable
|
||||||
import io.github.wulkanowy.data.enums.TimetableMode
|
|
||||||
import io.github.wulkanowy.databinding.ItemTimetableBinding
|
import io.github.wulkanowy.databinding.ItemTimetableBinding
|
||||||
import io.github.wulkanowy.databinding.ItemTimetableSmallBinding
|
import io.github.wulkanowy.databinding.ItemTimetableSmallBinding
|
||||||
import io.github.wulkanowy.utils.*
|
import io.github.wulkanowy.utils.getThemeAttrColor
|
||||||
import timber.log.Timber
|
import io.github.wulkanowy.utils.toFormattedString
|
||||||
import java.time.Instant
|
|
||||||
import java.util.*
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.concurrent.timer
|
|
||||||
|
|
||||||
class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
class TimetableAdapter @Inject constructor() :
|
||||||
|
ListAdapter<TimetableItem, RecyclerView.ViewHolder>(differ) {
|
||||||
|
|
||||||
private enum class ViewType {
|
override fun getItemViewType(position: Int): Int = getItem(position).type.ordinal
|
||||||
ITEM_NORMAL,
|
|
||||||
ITEM_SMALL
|
|
||||||
}
|
|
||||||
|
|
||||||
var onClickListener: (Timetable) -> Unit = {}
|
|
||||||
|
|
||||||
private var showWholeClassPlan = TimetableMode.ONLY_CURRENT_GROUP
|
|
||||||
|
|
||||||
private var showGroupsInPlan: Boolean = false
|
|
||||||
|
|
||||||
private var showTimers: Boolean = false
|
|
||||||
|
|
||||||
private val timers = mutableMapOf<Int, Timer?>()
|
|
||||||
|
|
||||||
private val items = mutableListOf<Timetable>()
|
|
||||||
|
|
||||||
fun submitList(
|
|
||||||
newTimetable: List<Timetable>,
|
|
||||||
showWholeClassPlan: TimetableMode = this.showWholeClassPlan,
|
|
||||||
showGroupsInPlan: Boolean = this.showGroupsInPlan,
|
|
||||||
showTimers: Boolean = this.showTimers
|
|
||||||
) {
|
|
||||||
val isFlagsDifferent = this.showWholeClassPlan != showWholeClassPlan
|
|
||||||
|| this.showGroupsInPlan != showGroupsInPlan
|
|
||||||
|| this.showTimers != showTimers
|
|
||||||
|
|
||||||
val diffResult = DiffUtil.calculateDiff(
|
|
||||||
TimetableAdapterDiffCallback(
|
|
||||||
oldList = items.toMutableList(),
|
|
||||||
newList = newTimetable,
|
|
||||||
isFlagsDifferent = isFlagsDifferent
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
this.showGroupsInPlan = showGroupsInPlan
|
|
||||||
this.showTimers = showTimers
|
|
||||||
this.showWholeClassPlan = showWholeClassPlan
|
|
||||||
|
|
||||||
items.clear()
|
|
||||||
items.addAll(newTimetable)
|
|
||||||
|
|
||||||
diffResult.dispatchUpdatesTo(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clearTimers() {
|
|
||||||
Timber.d("Timetable timers (${timers.size}) cleared")
|
|
||||||
with(timers) {
|
|
||||||
forEach { (_, timer) ->
|
|
||||||
timer?.cancel()
|
|
||||||
timer?.purge()
|
|
||||||
}
|
|
||||||
clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount() = items.size
|
|
||||||
|
|
||||||
override fun getItemViewType(position: Int) = when {
|
|
||||||
!items[position].isStudentPlan && showWholeClassPlan == TimetableMode.SMALL_OTHER_GROUP -> ViewType.ITEM_SMALL.ordinal
|
|
||||||
else -> ViewType.ITEM_NORMAL.ordinal
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
val inflater = LayoutInflater.from(parent.context)
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
|
|
||||||
return when (viewType) {
|
return when (TimetableItemType.values()[viewType]) {
|
||||||
ViewType.ITEM_NORMAL.ordinal -> ItemViewHolder(
|
TimetableItemType.SMALL -> SmallViewHolder(
|
||||||
ItemTimetableBinding.inflate(inflater, parent, false)
|
|
||||||
)
|
|
||||||
ViewType.ITEM_SMALL.ordinal -> SmallItemViewHolder(
|
|
||||||
ItemTimetableSmallBinding.inflate(inflater, parent, false)
|
ItemTimetableSmallBinding.inflate(inflater, parent, false)
|
||||||
)
|
)
|
||||||
else -> throw IllegalStateException()
|
TimetableItemType.NORMAL -> NormalViewHolder(
|
||||||
|
ItemTimetableBinding.inflate(inflater, parent, false)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(
|
||||||
|
holder: RecyclerView.ViewHolder,
|
||||||
|
position: Int,
|
||||||
|
payloads: MutableList<Any>
|
||||||
|
) {
|
||||||
|
if (payloads.isEmpty()) return super.onBindViewHolder(holder, position, payloads)
|
||||||
|
|
||||||
|
if (holder is NormalViewHolder) updateTimeLeft(
|
||||||
|
binding = holder.binding,
|
||||||
|
timeLeft = (getItem(position) as TimetableItem.Normal).timeLeft,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
val lesson = items[position]
|
|
||||||
|
|
||||||
when (holder) {
|
when (holder) {
|
||||||
is ItemViewHolder -> bindNormalView(holder.binding, lesson, position)
|
is SmallViewHolder -> bindSmallView(
|
||||||
is SmallItemViewHolder -> bindSmallView(holder.binding, lesson)
|
binding = holder.binding,
|
||||||
|
item = getItem(position) as TimetableItem.Small,
|
||||||
|
)
|
||||||
|
is NormalViewHolder -> bindNormalView(
|
||||||
|
binding = holder.binding,
|
||||||
|
item = getItem(position) as TimetableItem.Normal,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindSmallView(binding: ItemTimetableSmallBinding, lesson: Timetable) {
|
private fun bindSmallView(binding: ItemTimetableSmallBinding, item: TimetableItem.Small) {
|
||||||
|
val lesson = item.lesson
|
||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
timetableSmallItemNumber.text = lesson.number.toString()
|
timetableSmallItemNumber.text = lesson.number.toString()
|
||||||
timetableSmallItemSubject.text = lesson.subject
|
timetableSmallItemSubject.text = lesson.subject
|
||||||
@ -122,11 +75,13 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
bindSmallDescription(binding, lesson)
|
bindSmallDescription(binding, lesson)
|
||||||
bindSmallColors(binding, lesson)
|
bindSmallColors(binding, lesson)
|
||||||
|
|
||||||
root.setOnClickListener { onClickListener(lesson) }
|
root.setOnClickListener { item.onClick(lesson) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindNormalView(binding: ItemTimetableBinding, lesson: Timetable, position: Int) {
|
private fun bindNormalView(binding: ItemTimetableBinding, item: TimetableItem.Normal) {
|
||||||
|
val lesson = item.lesson
|
||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
timetableItemNumber.text = lesson.number.toString()
|
timetableItemNumber.text = lesson.number.toString()
|
||||||
timetableItemSubject.text = lesson.subject
|
timetableItemSubject.text = lesson.subject
|
||||||
@ -137,51 +92,19 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
timetableItemTimeFinish.text = lesson.end.toFormattedString("HH:mm")
|
timetableItemTimeFinish.text = lesson.end.toFormattedString("HH:mm")
|
||||||
|
|
||||||
bindSubjectStyle(timetableItemSubject, lesson)
|
bindSubjectStyle(timetableItemSubject, lesson)
|
||||||
bindNormalDescription(binding, lesson)
|
bindNormalDescription(binding, item)
|
||||||
bindNormalColors(binding, lesson)
|
bindNormalColors(binding, lesson)
|
||||||
|
updateTimeLeft(binding, item.timeLeft)
|
||||||
|
|
||||||
timers[position]?.let {
|
root.setOnClickListener { item.onClick(lesson) }
|
||||||
it.cancel()
|
|
||||||
it.purge()
|
|
||||||
}
|
|
||||||
timers[position] = null
|
|
||||||
|
|
||||||
if (lesson.isStudentPlan && showTimers) {
|
|
||||||
timers[position] = timer(period = 1000) {
|
|
||||||
Handler(Looper.getMainLooper()).post {
|
|
||||||
updateTimeLeft(binding, lesson, position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// reset item on set changed
|
|
||||||
timetableItemTimeUntil.visibility = GONE
|
|
||||||
timetableItemTimeLeft.visibility = GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
root.setOnClickListener { onClickListener(lesson) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPreviousLesson(position: Int): Instant? {
|
private fun updateTimeLeft(binding: ItemTimetableBinding, timeLeft: TimeLeft?) {
|
||||||
return items.filter { it.isStudentPlan }
|
|
||||||
.getOrNull(position - 1 - items.filterIndexed { i, item -> i < position && !item.isStudentPlan }.size)
|
|
||||||
?.let {
|
|
||||||
if (!it.canceled && it.isStudentPlan) it.end
|
|
||||||
else null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateTimeLeft(binding: ItemTimetableBinding, lesson: Timetable, position: Int) {
|
|
||||||
val isShowTimeUntil = lesson.isShowTimeUntil(getPreviousLesson(position))
|
|
||||||
val until = lesson.until.plusMinutes(1)
|
|
||||||
val left = lesson.left?.plusMinutes(1)
|
|
||||||
val isJustFinished = lesson.isJustFinished
|
|
||||||
|
|
||||||
with(binding) {
|
with(binding) {
|
||||||
when {
|
when {
|
||||||
// before lesson
|
// before lesson
|
||||||
isShowTimeUntil -> {
|
timeLeft?.until != null -> {
|
||||||
Timber.d("Show time until lesson: $position")
|
|
||||||
timetableItemTimeLeft.visibility = GONE
|
timetableItemTimeLeft.visibility = GONE
|
||||||
with(timetableItemTimeUntil) {
|
with(timetableItemTimeUntil) {
|
||||||
visibility = VISIBLE
|
visibility = VISIBLE
|
||||||
@ -189,14 +112,13 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
R.string.timetable_time_until,
|
R.string.timetable_time_until,
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.timetable_minutes,
|
R.string.timetable_minutes,
|
||||||
until.toMinutes().toString(10)
|
timeLeft.until.toMinutes().toString(10)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// after lesson start
|
// after lesson start
|
||||||
left != null -> {
|
timeLeft?.left != null -> {
|
||||||
Timber.d("Show time left lesson: $position")
|
|
||||||
timetableItemTimeUntil.visibility = GONE
|
timetableItemTimeUntil.visibility = GONE
|
||||||
with(timetableItemTimeLeft) {
|
with(timetableItemTimeLeft) {
|
||||||
visibility = VISIBLE
|
visibility = VISIBLE
|
||||||
@ -204,14 +126,13 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
R.string.timetable_time_left,
|
R.string.timetable_time_left,
|
||||||
context.getString(
|
context.getString(
|
||||||
R.string.timetable_minutes,
|
R.string.timetable_minutes,
|
||||||
left.toMinutes().toString()
|
timeLeft.left.toMinutes().toString()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// right after lesson finish
|
// right after lesson finish
|
||||||
isJustFinished -> {
|
timeLeft?.isJustFinished == true -> {
|
||||||
Timber.d("Show just finished lesson: $position")
|
|
||||||
timetableItemTimeUntil.visibility = GONE
|
timetableItemTimeUntil.visibility = GONE
|
||||||
timetableItemTimeLeft.visibility = VISIBLE
|
timetableItemTimeLeft.visibility = VISIBLE
|
||||||
timetableItemTimeLeft.text = root.context.getString(R.string.timetable_finished)
|
timetableItemTimeLeft.text = root.context.getString(R.string.timetable_finished)
|
||||||
@ -225,9 +146,7 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun bindSubjectStyle(subjectView: TextView, lesson: Timetable) {
|
private fun bindSubjectStyle(subjectView: TextView, lesson: Timetable) {
|
||||||
subjectView.paintFlags =
|
subjectView.paint.isStrikeThruText = lesson.canceled
|
||||||
if (lesson.canceled) subjectView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
|
||||||
else subjectView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindSmallDescription(binding: ItemTimetableSmallBinding, lesson: Timetable) {
|
private fun bindSmallDescription(binding: ItemTimetableSmallBinding, lesson: Timetable) {
|
||||||
@ -253,7 +172,8 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindNormalDescription(binding: ItemTimetableBinding, lesson: Timetable) {
|
private fun bindNormalDescription(binding: ItemTimetableBinding, item: TimetableItem.Normal) {
|
||||||
|
val lesson = item.lesson
|
||||||
with(binding) {
|
with(binding) {
|
||||||
if (lesson.info.isNotBlank() && !lesson.changes) {
|
if (lesson.info.isNotBlank() && !lesson.changes) {
|
||||||
timetableItemDescription.visibility = VISIBLE
|
timetableItemDescription.visibility = VISIBLE
|
||||||
@ -272,8 +192,7 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
} else {
|
} else {
|
||||||
timetableItemDescription.visibility = GONE
|
timetableItemDescription.visibility = GONE
|
||||||
timetableItemRoom.visibility = VISIBLE
|
timetableItemRoom.visibility = VISIBLE
|
||||||
timetableItemGroup.visibility =
|
timetableItemGroup.isVisible = item.showGroupsInPlan && lesson.group.isNotBlank()
|
||||||
if (showGroupsInPlan && lesson.group.isNotBlank()) VISIBLE else GONE
|
|
||||||
timetableItemTeacher.visibility = VISIBLE
|
timetableItemTeacher.visibility = VISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -349,26 +268,35 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ItemViewHolder(val binding: ItemTimetableBinding) :
|
private class NormalViewHolder(val binding: ItemTimetableBinding) :
|
||||||
RecyclerView.ViewHolder(binding.root)
|
RecyclerView.ViewHolder(binding.root)
|
||||||
|
|
||||||
private class SmallItemViewHolder(val binding: ItemTimetableSmallBinding) :
|
private class SmallViewHolder(val binding: ItemTimetableSmallBinding) :
|
||||||
RecyclerView.ViewHolder(binding.root)
|
RecyclerView.ViewHolder(binding.root)
|
||||||
|
|
||||||
class TimetableAdapterDiffCallback(
|
companion object {
|
||||||
private val oldList: List<Timetable>,
|
private val differ = object : DiffUtil.ItemCallback<TimetableItem>() {
|
||||||
private val newList: List<Timetable>,
|
override fun areItemsTheSame(oldItem: TimetableItem, newItem: TimetableItem): Boolean =
|
||||||
private val isFlagsDifferent: Boolean
|
when {
|
||||||
) : DiffUtil.Callback() {
|
oldItem is TimetableItem.Small && newItem is TimetableItem.Small -> {
|
||||||
|
oldItem.lesson.start == newItem.lesson.start
|
||||||
|
}
|
||||||
|
oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal -> {
|
||||||
|
oldItem.lesson.start == newItem.lesson.start
|
||||||
|
}
|
||||||
|
else -> oldItem == newItem
|
||||||
|
}
|
||||||
|
|
||||||
override fun getOldListSize() = oldList.size
|
override fun areContentsTheSame(oldItem: TimetableItem, newItem: TimetableItem) =
|
||||||
|
oldItem == newItem
|
||||||
|
|
||||||
override fun getNewListSize() = newList.size
|
override fun getChangePayload(oldItem: TimetableItem, newItem: TimetableItem): Any? {
|
||||||
|
return if (oldItem is TimetableItem.Normal && newItem is TimetableItem.Normal) {
|
||||||
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
if (oldItem.lesson == newItem.lesson && oldItem.timeLeft != newItem.timeLeft) {
|
||||||
oldList[oldItemPosition].id == newList[newItemPosition].id
|
"time_left"
|
||||||
|
} else super.getChangePayload(oldItem, newItem)
|
||||||
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
} else super.getChangePayload(oldItem, newItem)
|
||||||
oldList[oldItemPosition] == newList[newItemPosition] && !isFlagsDifferent
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import io.github.wulkanowy.R
|
import io.github.wulkanowy.R
|
||||||
import io.github.wulkanowy.data.db.entities.Timetable
|
import io.github.wulkanowy.data.db.entities.Timetable
|
||||||
import io.github.wulkanowy.data.enums.TimetableMode
|
|
||||||
import io.github.wulkanowy.databinding.FragmentTimetableBinding
|
import io.github.wulkanowy.databinding.FragmentTimetableBinding
|
||||||
import io.github.wulkanowy.ui.base.BaseFragment
|
import io.github.wulkanowy.ui.base.BaseFragment
|
||||||
import io.github.wulkanowy.ui.modules.main.MainActivity
|
import io.github.wulkanowy.ui.modules.main.MainActivity
|
||||||
@ -20,11 +19,7 @@ import io.github.wulkanowy.ui.modules.main.MainView
|
|||||||
import io.github.wulkanowy.ui.modules.timetable.additional.AdditionalLessonsFragment
|
import io.github.wulkanowy.ui.modules.timetable.additional.AdditionalLessonsFragment
|
||||||
import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment
|
import io.github.wulkanowy.ui.modules.timetable.completed.CompletedLessonsFragment
|
||||||
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
import io.github.wulkanowy.ui.widgets.DividerItemDecoration
|
||||||
import io.github.wulkanowy.utils.dpToPx
|
import io.github.wulkanowy.utils.*
|
||||||
import io.github.wulkanowy.utils.firstSchoolDayInSchoolYear
|
|
||||||
import io.github.wulkanowy.utils.getThemeAttrColor
|
|
||||||
import io.github.wulkanowy.utils.lastSchoolDayInSchoolYear
|
|
||||||
import io.github.wulkanowy.utils.openMaterialDatePicker
|
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -73,8 +68,6 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun initView() {
|
override fun initView() {
|
||||||
timetableAdapter.onClickListener = presenter::onTimetableItemSelected
|
|
||||||
|
|
||||||
with(binding.timetableRecycler) {
|
with(binding.timetableRecycler) {
|
||||||
layoutManager = LinearLayoutManager(context)
|
layoutManager = LinearLayoutManager(context)
|
||||||
adapter = timetableAdapter
|
adapter = timetableAdapter
|
||||||
@ -110,18 +103,8 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateData(
|
override fun updateData(data: List<TimetableItem>) {
|
||||||
data: List<Timetable>,
|
timetableAdapter.submitList(data)
|
||||||
showWholeClassPlanType: TimetableMode,
|
|
||||||
showGroupsInPlanType: Boolean,
|
|
||||||
showTimetableTimers: Boolean
|
|
||||||
) {
|
|
||||||
timetableAdapter.submitList(
|
|
||||||
newTimetable = data.toMutableList(),
|
|
||||||
showGroupsInPlan = showGroupsInPlanType,
|
|
||||||
showTimers = showTimetableTimers,
|
|
||||||
showWholeClassPlan = showWholeClassPlanType
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearData() {
|
override fun clearData() {
|
||||||
@ -214,7 +197,6 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
timetableAdapter.clearTimers()
|
|
||||||
presenter.onDetachView()
|
presenter.onDetachView()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package io.github.wulkanowy.ui.modules.timetable
|
||||||
|
|
||||||
|
import io.github.wulkanowy.data.db.entities.Timetable
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
sealed class TimetableItem(val type: TimetableItemType) {
|
||||||
|
|
||||||
|
data class Small(
|
||||||
|
val lesson: Timetable,
|
||||||
|
val onClick: (Timetable) -> Unit,
|
||||||
|
) : TimetableItem(TimetableItemType.SMALL)
|
||||||
|
|
||||||
|
data class Normal(
|
||||||
|
val lesson: Timetable,
|
||||||
|
val showGroupsInPlan: Boolean,
|
||||||
|
val timeLeft: TimeLeft?,
|
||||||
|
val onClick: (Timetable) -> Unit,
|
||||||
|
) : TimetableItem(TimetableItemType.NORMAL)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class TimeLeft(
|
||||||
|
val until: Duration?,
|
||||||
|
val left: Duration?,
|
||||||
|
val isJustFinished: Boolean,
|
||||||
|
)
|
||||||
|
|
||||||
|
enum class TimetableItemType {
|
||||||
|
SMALL,
|
||||||
|
NORMAL,
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
package io.github.wulkanowy.ui.modules.timetable
|
package io.github.wulkanowy.ui.modules.timetable
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import io.github.wulkanowy.data.Status
|
import io.github.wulkanowy.data.Status
|
||||||
import io.github.wulkanowy.data.db.entities.Timetable
|
import io.github.wulkanowy.data.db.entities.Timetable
|
||||||
import io.github.wulkanowy.data.enums.TimetableMode
|
import io.github.wulkanowy.data.enums.TimetableMode
|
||||||
@ -10,25 +9,17 @@ import io.github.wulkanowy.data.repositories.StudentRepository
|
|||||||
import io.github.wulkanowy.data.repositories.TimetableRepository
|
import io.github.wulkanowy.data.repositories.TimetableRepository
|
||||||
import io.github.wulkanowy.ui.base.BasePresenter
|
import io.github.wulkanowy.ui.base.BasePresenter
|
||||||
import io.github.wulkanowy.ui.base.ErrorHandler
|
import io.github.wulkanowy.ui.base.ErrorHandler
|
||||||
import io.github.wulkanowy.utils.AnalyticsHelper
|
import io.github.wulkanowy.utils.*
|
||||||
import io.github.wulkanowy.utils.afterLoading
|
|
||||||
import io.github.wulkanowy.utils.capitalise
|
|
||||||
import io.github.wulkanowy.utils.flowWithResourceIn
|
|
||||||
import io.github.wulkanowy.utils.getLastSchoolDayIfHoliday
|
|
||||||
import io.github.wulkanowy.utils.isHolidays
|
|
||||||
import io.github.wulkanowy.utils.nextOrSameSchoolDay
|
|
||||||
import io.github.wulkanowy.utils.nextSchoolDay
|
|
||||||
import io.github.wulkanowy.utils.previousSchoolDay
|
|
||||||
import io.github.wulkanowy.utils.toFormattedString
|
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDate.now
|
import java.time.LocalDate.*
|
||||||
import java.time.LocalDate.of
|
import java.util.*
|
||||||
import java.time.LocalDate.ofEpochDay
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.concurrent.timer
|
||||||
|
|
||||||
class TimetablePresenter @Inject constructor(
|
class TimetablePresenter @Inject constructor(
|
||||||
errorHandler: ErrorHandler,
|
errorHandler: ErrorHandler,
|
||||||
@ -46,6 +37,8 @@ class TimetablePresenter @Inject constructor(
|
|||||||
|
|
||||||
private lateinit var lastError: Throwable
|
private lateinit var lastError: Throwable
|
||||||
|
|
||||||
|
private var tickTimer: Timer? = null
|
||||||
|
|
||||||
fun onAttachView(view: TimetableView, date: Long?) {
|
fun onAttachView(view: TimetableView, date: Long?) {
|
||||||
super.onAttachView(view)
|
super.onAttachView(view)
|
||||||
view.initView()
|
view.initView()
|
||||||
@ -106,11 +99,6 @@ class TimetablePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onTimetableItemSelected(lesson: Timetable) {
|
|
||||||
Timber.i("Select timetable item ${lesson.id}")
|
|
||||||
view?.showTimetableDialog(lesson)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onAdditionalLessonsSwitchSelected(): Boolean {
|
fun onAdditionalLessonsSwitchSelected(): Boolean {
|
||||||
view?.openAdditionalLessonsView()
|
view?.openAdditionalLessonsView()
|
||||||
return true
|
return true
|
||||||
@ -148,12 +136,12 @@ class TimetablePresenter @Inject constructor(
|
|||||||
Status.LOADING -> {
|
Status.LOADING -> {
|
||||||
if (!it.data?.lessons.isNullOrEmpty()) {
|
if (!it.data?.lessons.isNullOrEmpty()) {
|
||||||
view?.run {
|
view?.run {
|
||||||
|
updateData(it.data!!.lessons)
|
||||||
enableSwipe(true)
|
enableSwipe(true)
|
||||||
showRefresh(true)
|
showRefresh(true)
|
||||||
showErrorView(false)
|
showErrorView(false)
|
||||||
showProgress(false)
|
showProgress(false)
|
||||||
showContent(true)
|
showContent(true)
|
||||||
updateData(it.data!!.lessons)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,17 +177,62 @@ class TimetablePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateData(lessons: List<Timetable>) {
|
private fun updateData(lessons: List<Timetable>) {
|
||||||
view?.updateData(
|
tickTimer?.cancel()
|
||||||
showWholeClassPlanType = prefRepository.showWholeClassPlan,
|
|
||||||
showGroupsInPlanType = prefRepository.showGroupsInPlan,
|
if (!prefRepository.showTimetableTimers) {
|
||||||
showTimetableTimers = prefRepository.showTimetableTimers,
|
view?.updateData(createItems(lessons))
|
||||||
data = createItems(lessons)
|
} else {
|
||||||
|
tickTimer = timer(period = 2_000) {
|
||||||
|
view?.updateData(createItems(lessons))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createItems(items: List<Timetable>): List<TimetableItem> {
|
||||||
|
val filteredItems = items
|
||||||
|
.filter {
|
||||||
|
if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) {
|
||||||
|
it.isStudentPlan
|
||||||
|
} else true
|
||||||
|
}.sortedWith(
|
||||||
|
compareBy({ item -> item.number }, { item -> !item.isStudentPlan })
|
||||||
|
)
|
||||||
|
|
||||||
|
return filteredItems.mapIndexed { i, it ->
|
||||||
|
if (it.isStudentPlan) TimetableItem.Normal(
|
||||||
|
lesson = it,
|
||||||
|
showGroupsInPlan = prefRepository.showGroupsInPlan,
|
||||||
|
timeLeft = filteredItems.getTimeLeftForLesson(it, i),
|
||||||
|
onClick = ::onTimetableItemSelected
|
||||||
|
) else TimetableItem.Small(
|
||||||
|
lesson = it,
|
||||||
|
onClick = ::onTimetableItemSelected
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun List<Timetable>.getTimeLeftForLesson(lesson: Timetable, index: Int): TimeLeft {
|
||||||
|
val isShowTimeUntil = lesson.isShowTimeUntil(getPreviousLesson(index))
|
||||||
|
return TimeLeft(
|
||||||
|
until = lesson.until.plusMinutes(1).takeIf { isShowTimeUntil },
|
||||||
|
left = lesson.left?.plusMinutes(1),
|
||||||
|
isJustFinished = lesson.isJustFinished,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createItems(items: List<Timetable>) = items.filter { item ->
|
private fun List<Timetable>.getPreviousLesson(position: Int): Instant? {
|
||||||
if (prefRepository.showWholeClassPlan == TimetableMode.ONLY_CURRENT_GROUP) item.isStudentPlan else true
|
return filter { it.isStudentPlan }
|
||||||
}.sortedWith(compareBy({ item -> item.number }, { item -> !item.isStudentPlan }))
|
.getOrNull(position - 1 - filterIndexed { i, item -> i < position && !item.isStudentPlan }.size)
|
||||||
|
?.let {
|
||||||
|
if (!it.canceled && it.isStudentPlan) it.end
|
||||||
|
else null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onTimetableItemSelected(lesson: Timetable) {
|
||||||
|
Timber.i("Select timetable item ${lesson.id}")
|
||||||
|
view?.showTimetableDialog(lesson)
|
||||||
|
}
|
||||||
|
|
||||||
private fun showErrorViewOnError(message: String, error: Throwable) {
|
private fun showErrorViewOnError(message: String, error: Throwable) {
|
||||||
view?.run {
|
view?.run {
|
||||||
@ -227,7 +260,6 @@ class TimetablePresenter @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
|
||||||
private fun reloadNavigation() {
|
private fun reloadNavigation() {
|
||||||
view?.apply {
|
view?.apply {
|
||||||
showPreButton(!currentDate.minusDays(1).isHolidays)
|
showPreButton(!currentDate.minusDays(1).isHolidays)
|
||||||
@ -235,4 +267,10 @@ class TimetablePresenter @Inject constructor(
|
|||||||
updateNavigationDay(currentDate.toFormattedString("EEEE, dd.MM").capitalise())
|
updateNavigationDay(currentDate.toFormattedString("EEEE, dd.MM").capitalise())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDetachView() {
|
||||||
|
tickTimer?.cancel()
|
||||||
|
tickTimer = null
|
||||||
|
super.onDetachView()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package io.github.wulkanowy.ui.modules.timetable
|
package io.github.wulkanowy.ui.modules.timetable
|
||||||
|
|
||||||
import io.github.wulkanowy.data.db.entities.Timetable
|
import io.github.wulkanowy.data.db.entities.Timetable
|
||||||
import io.github.wulkanowy.data.enums.TimetableMode
|
|
||||||
import io.github.wulkanowy.ui.base.BaseView
|
import io.github.wulkanowy.ui.base.BaseView
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
@ -13,12 +12,7 @@ interface TimetableView : BaseView {
|
|||||||
|
|
||||||
fun initView()
|
fun initView()
|
||||||
|
|
||||||
fun updateData(
|
fun updateData(data: List<TimetableItem>)
|
||||||
data: List<Timetable>,
|
|
||||||
showWholeClassPlanType: TimetableMode,
|
|
||||||
showGroupsInPlanType: Boolean,
|
|
||||||
showTimetableTimers: Boolean
|
|
||||||
)
|
|
||||||
|
|
||||||
fun updateNavigationDay(date: String)
|
fun updateNavigationDay(date: String)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user