diff --git a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt index f049f828..87b3362d 100644 --- a/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt +++ b/app/src/main/java/io/github/wulkanowy/ui/modules/timetable/TimetableAdapter.kt @@ -7,6 +7,7 @@ import android.view.View.VISIBLE import android.view.ViewGroup import android.widget.TextView import androidx.core.view.ViewCompat +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import io.github.wulkanowy.R import io.github.wulkanowy.data.db.entities.Timetable @@ -26,31 +27,58 @@ import kotlin.concurrent.timer class TimetableAdapter @Inject constructor() : RecyclerView.Adapter() { - private enum class ViewType(val id: Int) { - ITEM_NORMAL(1), - ITEM_SMALL(2) + private enum class ViewType { + ITEM_NORMAL, + ITEM_SMALL } - var items = mutableListOf() - set(value) { - field = value - resetTimers() - } - 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() + private val timers = mutableMapOf() - fun resetTimers() { - Timber.d("Timetable timers (${timers.size}) reset") + private val items = mutableListOf() + + fun submitList( + newTimetable: List, + 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) { - forEach { (_, timer) -> timer.cancel() } + forEach { (_, timer) -> + timer?.cancel() + timer?.purge() + } clear() } } @@ -58,16 +86,20 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter ViewType.ITEM_SMALL.id - else -> ViewType.ITEM_NORMAL.id + !items[position].isStudentPlan && showWholeClassPlan == "small" -> ViewType.ITEM_SMALL.ordinal + else -> ViewType.ITEM_NORMAL.ordinal } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val inflater = LayoutInflater.from(parent.context) return when (viewType) { - ViewType.ITEM_NORMAL.id -> ItemViewHolder(ItemTimetableBinding.inflate(inflater, parent, false)) - ViewType.ITEM_SMALL.id -> SmallItemViewHolder(ItemTimetableSmallBinding.inflate(inflater, parent, false)) + ViewType.ITEM_NORMAL.ordinal -> ItemViewHolder( + ItemTimetableBinding.inflate(inflater, parent, false) + ) + ViewType.ITEM_SMALL.ordinal -> SmallItemViewHolder( + ItemTimetableSmallBinding.inflate(inflater, parent, false) + ) else -> throw IllegalStateException() } } @@ -111,6 +143,12 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter i < position && !item.isStudentPlan }.size)?.let { - if (!it.canceled && it.isStudentPlan) it.end - else null - } + 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) { @@ -148,11 +188,18 @@ class TimetableAdapter @Inject constructor() : RecyclerView.Adapter, + private val newList: List, + 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 + } } 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 2bc7aa59..a374e166 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 @@ -49,7 +49,7 @@ class TimetableFragment : BaseFragment(R.layout.fragme 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 @@ -109,20 +109,16 @@ class TimetableFragment : BaseFragment(R.layout.fragme showGroupsInPlanType: Boolean, showTimetableTimers: Boolean ) { - with(timetableAdapter) { - items = data.toMutableList() - showTimers = showTimetableTimers + timetableAdapter.submitList( + newTimetable = data.toMutableList(), + showGroupsInPlan = showGroupsInPlanType, + showTimers = showTimetableTimers, showWholeClassPlan = showWholeClassPlanType - showGroupsInPlan = showGroupsInPlanType - notifyDataSetChanged() - } + ) } override fun clearData() { - with(timetableAdapter) { - items = mutableListOf() - notifyDataSetChanged() - } + timetableAdapter.submitList(listOf()) } override fun updateNavigationDay(date: String) { @@ -226,7 +222,7 @@ class TimetableFragment : BaseFragment(R.layout.fragme } override fun onDestroyView() { - timetableAdapter.resetTimers() + timetableAdapter.clearTimers() presenter.onDetachView() super.onDestroyView() }