mirror of
https://github.com/wulkanowy/wulkanowy.git
synced 2025-01-31 21:02:45 +01:00
Fix buggy timers in timetable (#1428)
Co-authored-by: Rafał Borcz <RafalBO99@outlook.com>
This commit is contained in:
parent
d3b3939d26
commit
eb94e06d54
@ -7,6 +7,7 @@ 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.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
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
|
||||||
@ -26,31 +27,58 @@ import kotlin.concurrent.timer
|
|||||||
|
|
||||||
class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
private enum class ViewType(val id: Int) {
|
private enum class ViewType {
|
||||||
ITEM_NORMAL(1),
|
ITEM_NORMAL,
|
||||||
ITEM_SMALL(2)
|
ITEM_SMALL
|
||||||
}
|
}
|
||||||
|
|
||||||
var items = mutableListOf<Timetable>()
|
|
||||||
set(value) {
|
|
||||||
field = value
|
|
||||||
resetTimers()
|
|
||||||
}
|
|
||||||
|
|
||||||
var onClickListener: (Timetable) -> Unit = {}
|
var onClickListener: (Timetable) -> Unit = {}
|
||||||
|
|
||||||
var showWholeClassPlan: String = "no"
|
private var showWholeClassPlan: String = "no"
|
||||||
|
|
||||||
var showGroupsInPlan: Boolean = false
|
private var showGroupsInPlan: Boolean = false
|
||||||
|
|
||||||
var showTimers: Boolean = false
|
private var showTimers: Boolean = false
|
||||||
|
|
||||||
private val timers = mutableMapOf<Int, Timer>()
|
private val timers = mutableMapOf<Int, Timer?>()
|
||||||
|
|
||||||
fun resetTimers() {
|
private val items = mutableListOf<Timetable>()
|
||||||
Timber.d("Timetable timers (${timers.size}) reset")
|
|
||||||
|
fun submitList(
|
||||||
|
newTimetable: List<Timetable>,
|
||||||
|
showWholeClassPlan: String = 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) {
|
with(timers) {
|
||||||
forEach { (_, timer) -> timer.cancel() }
|
forEach { (_, timer) ->
|
||||||
|
timer?.cancel()
|
||||||
|
timer?.purge()
|
||||||
|
}
|
||||||
clear()
|
clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,16 +86,20 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
override fun getItemCount() = items.size
|
override fun getItemCount() = items.size
|
||||||
|
|
||||||
override fun getItemViewType(position: Int) = when {
|
override fun getItemViewType(position: Int) = when {
|
||||||
!items[position].isStudentPlan && showWholeClassPlan == "small" -> ViewType.ITEM_SMALL.id
|
!items[position].isStudentPlan && showWholeClassPlan == "small" -> ViewType.ITEM_SMALL.ordinal
|
||||||
else -> ViewType.ITEM_NORMAL.id
|
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 (viewType) {
|
||||||
ViewType.ITEM_NORMAL.id -> ItemViewHolder(ItemTimetableBinding.inflate(inflater, parent, false))
|
ViewType.ITEM_NORMAL.ordinal -> ItemViewHolder(
|
||||||
ViewType.ITEM_SMALL.id -> SmallItemViewHolder(ItemTimetableSmallBinding.inflate(inflater, parent, false))
|
ItemTimetableBinding.inflate(inflater, parent, false)
|
||||||
|
)
|
||||||
|
ViewType.ITEM_SMALL.ordinal -> SmallItemViewHolder(
|
||||||
|
ItemTimetableSmallBinding.inflate(inflater, parent, false)
|
||||||
|
)
|
||||||
else -> throw IllegalStateException()
|
else -> throw IllegalStateException()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,6 +143,12 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
bindNormalDescription(binding, lesson)
|
bindNormalDescription(binding, lesson)
|
||||||
bindNormalColors(binding, lesson)
|
bindNormalColors(binding, lesson)
|
||||||
|
|
||||||
|
timers[position]?.let {
|
||||||
|
it.cancel()
|
||||||
|
it.purge()
|
||||||
|
}
|
||||||
|
timers[position] = null
|
||||||
|
|
||||||
if (lesson.isStudentPlan && showTimers) {
|
if (lesson.isStudentPlan && showTimers) {
|
||||||
timers[position] = timer(period = 1000) {
|
timers[position] = timer(period = 1000) {
|
||||||
if (ViewCompat.isAttachedToWindow(root)) {
|
if (ViewCompat.isAttachedToWindow(root)) {
|
||||||
@ -128,10 +166,12 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getPreviousLesson(position: Int): LocalDateTime? {
|
private fun getPreviousLesson(position: Int): LocalDateTime? {
|
||||||
return items.filter { it.isStudentPlan }.getOrNull(position - 1 - items.filterIndexed { i, item -> i < position && !item.isStudentPlan }.size)?.let {
|
return items.filter { it.isStudentPlan }
|
||||||
if (!it.canceled && it.isStudentPlan) it.end
|
.getOrNull(position - 1 - items.filterIndexed { i, item -> i < position && !item.isStudentPlan }.size)
|
||||||
else null
|
?.let {
|
||||||
}
|
if (!it.canceled && it.isStudentPlan) it.end
|
||||||
|
else null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTimeLeft(binding: ItemTimetableBinding, lesson: Timetable, position: Int) {
|
private fun updateTimeLeft(binding: ItemTimetableBinding, lesson: Timetable, position: Int) {
|
||||||
@ -148,11 +188,18 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
timetableItemTimeLeft.visibility = GONE
|
timetableItemTimeLeft.visibility = GONE
|
||||||
with(timetableItemTimeUntil) {
|
with(timetableItemTimeUntil) {
|
||||||
visibility = VISIBLE
|
visibility = VISIBLE
|
||||||
text = context.getString(R.string.timetable_time_until,
|
text = context.getString(
|
||||||
|
R.string.timetable_time_until,
|
||||||
if (until.seconds <= 60) {
|
if (until.seconds <= 60) {
|
||||||
context.getString(R.string.timetable_seconds, until.seconds.toString(10))
|
context.getString(
|
||||||
|
R.string.timetable_seconds,
|
||||||
|
until.seconds.toString(10)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
context.getString(R.string.timetable_minutes, until.toMinutes().toString(10))
|
context.getString(
|
||||||
|
R.string.timetable_minutes,
|
||||||
|
until.toMinutes().toString(10)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -166,9 +213,15 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
text = context.getString(
|
text = context.getString(
|
||||||
R.string.timetable_time_left,
|
R.string.timetable_time_left,
|
||||||
if (left.seconds < 60) {
|
if (left.seconds < 60) {
|
||||||
context.getString(R.string.timetable_seconds, left.seconds.toString(10))
|
context.getString(
|
||||||
|
R.string.timetable_seconds,
|
||||||
|
left.seconds.toString(10)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
context.getString(R.string.timetable_minutes, left.toMinutes().toString(10))
|
context.getString(
|
||||||
|
R.string.timetable_minutes,
|
||||||
|
left.toMinutes().toString(10)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -189,8 +242,9 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun bindSubjectStyle(subjectView: TextView, lesson: Timetable) {
|
private fun bindSubjectStyle(subjectView: TextView, lesson: Timetable) {
|
||||||
subjectView.paintFlags = if (lesson.canceled) subjectView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
subjectView.paintFlags =
|
||||||
else subjectView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
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) {
|
||||||
@ -202,10 +256,12 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
timetableSmallItemRoom.visibility = GONE
|
timetableSmallItemRoom.visibility = GONE
|
||||||
timetableSmallItemTeacher.visibility = GONE
|
timetableSmallItemTeacher.visibility = GONE
|
||||||
|
|
||||||
timetableSmallItemDescription.setTextColor(root.context.getThemeAttrColor(
|
timetableSmallItemDescription.setTextColor(
|
||||||
if (lesson.canceled) R.attr.colorPrimary
|
root.context.getThemeAttrColor(
|
||||||
else R.attr.colorTimetableChange
|
if (lesson.canceled) R.attr.colorPrimary
|
||||||
))
|
else R.attr.colorTimetableChange
|
||||||
|
)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
timetableSmallItemDescription.visibility = GONE
|
timetableSmallItemDescription.visibility = GONE
|
||||||
timetableSmallItemRoom.visibility = VISIBLE
|
timetableSmallItemRoom.visibility = VISIBLE
|
||||||
@ -224,14 +280,17 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
timetableItemGroup.visibility = GONE
|
timetableItemGroup.visibility = GONE
|
||||||
timetableItemTeacher.visibility = GONE
|
timetableItemTeacher.visibility = GONE
|
||||||
|
|
||||||
timetableItemDescription.setTextColor(root.context.getThemeAttrColor(
|
timetableItemDescription.setTextColor(
|
||||||
if (lesson.canceled) R.attr.colorPrimary
|
root.context.getThemeAttrColor(
|
||||||
else R.attr.colorTimetableChange
|
if (lesson.canceled) R.attr.colorPrimary
|
||||||
))
|
else R.attr.colorTimetableChange
|
||||||
|
)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
timetableItemDescription.visibility = GONE
|
timetableItemDescription.visibility = GONE
|
||||||
timetableItemRoom.visibility = VISIBLE
|
timetableItemRoom.visibility = VISIBLE
|
||||||
timetableItemGroup.visibility = if (showGroupsInPlan && lesson.group.isNotBlank()) VISIBLE else GONE
|
timetableItemGroup.visibility =
|
||||||
|
if (showGroupsInPlan && lesson.group.isNotBlank()) VISIBLE else GONE
|
||||||
timetableItemTeacher.visibility = VISIBLE
|
timetableItemTeacher.visibility = VISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,7 +299,10 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
private fun bindSmallColors(binding: ItemTimetableSmallBinding, lesson: Timetable) {
|
private fun bindSmallColors(binding: ItemTimetableSmallBinding, lesson: Timetable) {
|
||||||
with(binding) {
|
with(binding) {
|
||||||
if (lesson.canceled) {
|
if (lesson.canceled) {
|
||||||
updateNumberAndSubjectCanceledColor(timetableSmallItemNumber, timetableSmallItemSubject)
|
updateNumberAndSubjectCanceledColor(
|
||||||
|
timetableSmallItemNumber,
|
||||||
|
timetableSmallItemSubject
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
updateNumberColor(timetableSmallItemNumber, lesson)
|
updateNumberColor(timetableSmallItemNumber, lesson)
|
||||||
updateSubjectColor(timetableSmallItemSubject, lesson)
|
updateSubjectColor(timetableSmallItemSubject, lesson)
|
||||||
@ -269,31 +331,39 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateNumberColor(numberView: TextView, lesson: Timetable) {
|
private fun updateNumberColor(numberView: TextView, lesson: Timetable) {
|
||||||
numberView.setTextColor(numberView.context.getThemeAttrColor(
|
numberView.setTextColor(
|
||||||
if (lesson.changes || lesson.info.isNotBlank()) R.attr.colorTimetableChange
|
numberView.context.getThemeAttrColor(
|
||||||
else android.R.attr.textColorPrimary
|
if (lesson.changes || lesson.info.isNotBlank()) R.attr.colorTimetableChange
|
||||||
))
|
else android.R.attr.textColorPrimary
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateSubjectColor(subjectView: TextView, lesson: Timetable) {
|
private fun updateSubjectColor(subjectView: TextView, lesson: Timetable) {
|
||||||
subjectView.setTextColor(subjectView.context.getThemeAttrColor(
|
subjectView.setTextColor(
|
||||||
if (lesson.subjectOld.isNotBlank() && lesson.subjectOld != lesson.subject) R.attr.colorTimetableChange
|
subjectView.context.getThemeAttrColor(
|
||||||
else android.R.attr.textColorPrimary
|
if (lesson.subjectOld.isNotBlank() && lesson.subjectOld != lesson.subject) R.attr.colorTimetableChange
|
||||||
))
|
else android.R.attr.textColorPrimary
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateRoomColor(roomView: TextView, lesson: Timetable) {
|
private fun updateRoomColor(roomView: TextView, lesson: Timetable) {
|
||||||
roomView.setTextColor(roomView.context.getThemeAttrColor(
|
roomView.setTextColor(
|
||||||
if (lesson.roomOld.isNotBlank() && lesson.roomOld != lesson.room) R.attr.colorTimetableChange
|
roomView.context.getThemeAttrColor(
|
||||||
else android.R.attr.textColorSecondary
|
if (lesson.roomOld.isNotBlank() && lesson.roomOld != lesson.room) R.attr.colorTimetableChange
|
||||||
))
|
else android.R.attr.textColorSecondary
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateTeacherColor(teacherTextView: TextView, lesson: Timetable) {
|
private fun updateTeacherColor(teacherTextView: TextView, lesson: Timetable) {
|
||||||
teacherTextView.setTextColor(teacherTextView.context.getThemeAttrColor(
|
teacherTextView.setTextColor(
|
||||||
if (lesson.teacherOld.isNotBlank() && lesson.teacherOld != lesson.teacher) R.attr.colorTimetableChange
|
teacherTextView.context.getThemeAttrColor(
|
||||||
else android.R.attr.textColorSecondary
|
if (lesson.teacherOld.isNotBlank() && lesson.teacherOld != lesson.teacher) R.attr.colorTimetableChange
|
||||||
))
|
else android.R.attr.textColorSecondary
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ItemViewHolder(val binding: ItemTimetableBinding) :
|
private class ItemViewHolder(val binding: ItemTimetableBinding) :
|
||||||
@ -301,4 +371,21 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter<RecyclerView
|
|||||||
|
|
||||||
private class SmallItemViewHolder(val binding: ItemTimetableSmallBinding) :
|
private class SmallItemViewHolder(val binding: ItemTimetableSmallBinding) :
|
||||||
RecyclerView.ViewHolder(binding.root)
|
RecyclerView.ViewHolder(binding.root)
|
||||||
|
|
||||||
|
class TimetableAdapterDiffCallback(
|
||||||
|
private val oldList: List<Timetable>,
|
||||||
|
private val newList: List<Timetable>,
|
||||||
|
private val isFlagsDifferent: Boolean
|
||||||
|
) : DiffUtil.Callback() {
|
||||||
|
|
||||||
|
override fun getOldListSize() = oldList.size
|
||||||
|
|
||||||
|
override fun getNewListSize() = newList.size
|
||||||
|
|
||||||
|
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||||
|
oldList[oldItemPosition].id == newList[newItemPosition].id
|
||||||
|
|
||||||
|
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int) =
|
||||||
|
oldList[oldItemPosition] == newList[newItemPosition] && !isFlagsDifferent
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
|||||||
|
|
||||||
override val titleStringId get() = R.string.timetable_title
|
override val titleStringId get() = R.string.timetable_title
|
||||||
|
|
||||||
override val isViewEmpty get() = timetableAdapter.items.isEmpty()
|
override val isViewEmpty get() = timetableAdapter.itemCount > 0
|
||||||
|
|
||||||
override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize
|
override val currentStackSize get() = (activity as? MainActivity)?.currentStackSize
|
||||||
|
|
||||||
@ -109,20 +109,16 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
|||||||
showGroupsInPlanType: Boolean,
|
showGroupsInPlanType: Boolean,
|
||||||
showTimetableTimers: Boolean
|
showTimetableTimers: Boolean
|
||||||
) {
|
) {
|
||||||
with(timetableAdapter) {
|
timetableAdapter.submitList(
|
||||||
items = data.toMutableList()
|
newTimetable = data.toMutableList(),
|
||||||
showTimers = showTimetableTimers
|
showGroupsInPlan = showGroupsInPlanType,
|
||||||
|
showTimers = showTimetableTimers,
|
||||||
showWholeClassPlan = showWholeClassPlanType
|
showWholeClassPlan = showWholeClassPlanType
|
||||||
showGroupsInPlan = showGroupsInPlanType
|
)
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun clearData() {
|
override fun clearData() {
|
||||||
with(timetableAdapter) {
|
timetableAdapter.submitList(listOf())
|
||||||
items = mutableListOf()
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateNavigationDay(date: String) {
|
override fun updateNavigationDay(date: String) {
|
||||||
@ -226,7 +222,7 @@ class TimetableFragment : BaseFragment<FragmentTimetableBinding>(R.layout.fragme
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
timetableAdapter.resetTimers()
|
timetableAdapter.clearTimers()
|
||||||
presenter.onDetachView()
|
presenter.onDetachView()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user