Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

courses: add pagination (fixes #3470) #3471

Closed
wants to merge 10 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,16 @@ import org.ole.planet.myplanet.callback.OnRatingChangeListener
import org.ole.planet.myplanet.databinding.RowCourseBinding
import org.ole.planet.myplanet.model.RealmMyCourse
import org.ole.planet.myplanet.model.RealmTag
import org.ole.planet.myplanet.utilities.JsonUtils.getInt
import org.ole.planet.myplanet.utilities.Markdown.setMarkdownText
import org.ole.planet.myplanet.utilities.TimeUtils.formatDate
import org.ole.planet.myplanet.utilities.Utilities
import java.util.Collections
import java.util.regex.Pattern
import kotlin.math.*

class AdapterCourses(private val context: Context, private var courseList: List<RealmMyCourse?>, private val map: HashMap<String?, JsonObject>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private lateinit var rowCourseBinding: RowCourseBinding
private val selectedItems: MutableList<RealmMyCourse?>
private val selectedItems: MutableList<RealmMyCourse?> = ArrayList()
private var listener: OnCourseItemSelected? = null
private var homeItemClickListener: OnHomeItemClickListener? = null
private var progressMap: HashMap<String?, JsonObject>? = null
Expand All @@ -47,8 +47,22 @@ class AdapterCourses(private val context: Context, private var courseList: List<
private var isTitleAscending = true
private var areAllSelected = true

private var _itemsPerPage: Int = 10
var itemsPerPage: Int
get() = _itemsPerPage
set(value) {
_itemsPerPage = value
notifyDataSetChanged()
}

private var _currentPage: Int = 1
var currentPage: Int
get() = _currentPage
set(value) {
_currentPage = value
}

init {
selectedItems = ArrayList()
if (context is OnHomeItemClickListener) {
homeItemClickListener = context
}
Expand All @@ -67,6 +81,10 @@ class AdapterCourses(private val context: Context, private var courseList: List<
return courseList
}

fun getTotalCourseCount(): Int {
return courseList.size
}

fun setCourseList(courseList: List<RealmMyCourse?>) {
this.courseList = courseList
sortCourseList()
Expand All @@ -75,11 +93,11 @@ class AdapterCourses(private val context: Context, private var courseList: List<
}

private fun sortCourseListByTitle() {
Collections.sort(courseList) { course1: RealmMyCourse?, course2: RealmMyCourse? ->
Collections.sort(courseList) { course1, course2 ->
if (isTitleAscending) {
return@sort course1!!.courseTitle!!.compareTo(course2!!.courseTitle!!, ignoreCase = true)
course1!!.courseTitle!!.compareTo(course2!!.courseTitle!!, ignoreCase = true)
} else {
return@sort course2!!.courseTitle!!.compareTo(course1!!.courseTitle!!, ignoreCase = true)
course2!!.courseTitle!!.compareTo(course1!!.courseTitle!!, ignoreCase = true)
}
}
}
Expand Down Expand Up @@ -120,9 +138,10 @@ class AdapterCourses(private val context: Context, private var courseList: List<
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val paginatedCourseList = getPaginatedCourseList()
if (holder is ViewHoldercourse) {
holder.bind(position)
val course = courseList[position]
val course = paginatedCourseList[position]
if (course != null) {
holder.rowCourseBinding.title.text = course.courseTitle
holder.rowCourseBinding.description.text = course.description
Expand Down Expand Up @@ -164,21 +183,20 @@ class AdapterCourses(private val context: Context, private var courseList: List<
}

fun areAllSelected(): Boolean {
areAllSelected = selectedItems.size == courseList.size
val paginatedCourseList = getPaginatedCourseList()
areAllSelected = paginatedCourseList.all { selectedItems.contains(it) }
return areAllSelected
}

fun selectAllItems(selectAll: Boolean) {
val paginatedCourseList = getPaginatedCourseList()
if (selectAll) {
selectedItems.clear()
selectedItems.addAll(courseList)
selectedItems.addAll(paginatedCourseList)
} else {
selectedItems.clear()
selectedItems.removeAll(paginatedCourseList)
}
notifyDataSetChanged()
if (listener != null) {
listener!!.onSelectedListChange(selectedItems)
}
listener?.onSelectedListChange(selectedItems)
}

private fun displayTagCloud(flexboxDrawable: FlexboxLayout, position: Int) {
Expand Down Expand Up @@ -207,8 +225,8 @@ class AdapterCourses(private val context: Context, private var courseList: List<
private fun showProgressAndRating(position: Int, holder: RecyclerView.ViewHolder) {
val viewHolder = holder as ViewHoldercourse
showProgress(position)
if (map.containsKey(courseList[position]!!.courseId)) {
val `object` = map[courseList[position]!!.courseId]
if (map.containsKey(courseList[position]?.courseId)) {
val `object` = map[courseList[position]?.courseId]
showRating(`object`, viewHolder.rowCourseBinding.average, viewHolder.rowCourseBinding.timesRated, viewHolder.rowCourseBinding.ratingBar)
} else {
viewHolder.rowCourseBinding.ratingBar.rating = 0f
Expand All @@ -220,9 +238,10 @@ class AdapterCourses(private val context: Context, private var courseList: List<
val ob = progressMap!![courseList[position]?.courseId]
rowCourseBinding.courseProgress.max = getInt("max", ob)
rowCourseBinding.courseProgress.progress = getInt("current", ob)
if (getInt("current", ob) < getInt("max", ob))
if (getInt("current", ob) == getInt("max", ob)) {
rowCourseBinding.courseProgress.secondaryProgress = getInt("current", ob) + 1
rowCourseBinding.courseProgress.visibility = View.VISIBLE
rowCourseBinding.courseProgress.visibility = View.VISIBLE
}
} else {
rowCourseBinding.courseProgress.visibility = View.GONE
}
Expand All @@ -239,12 +258,29 @@ class AdapterCourses(private val context: Context, private var courseList: List<
}
}

private fun getInt(str: String, `object`: JsonObject?): Int {
return `object`?.get(str)?.asInt ?: 0
}

private fun getPaginatedCourseList(): List<RealmMyCourse?> {
return if (itemsPerPage == Int.MAX_VALUE) {
courseList
} else {
val startIndex = (currentPage - 1) * itemsPerPage
val endIndex = min(startIndex + itemsPerPage, courseList.size)
courseList.subList(startIndex, endIndex)
}
}

override fun getItemCount(): Int {
return courseList.size
return if (itemsPerPage == Int.MAX_VALUE) {
courseList.size
} else {
getPaginatedCourseList().size
}
}

internal inner class ViewHoldercourse(val rowCourseBinding: RowCourseBinding) :
RecyclerView.ViewHolder(rowCourseBinding.root) {
inner class ViewHoldercourse(var rowCourseBinding: RowCourseBinding) : RecyclerView.ViewHolder(rowCourseBinding.root) {
private var adapterPosition = 0

init {
Expand Down Expand Up @@ -274,12 +310,24 @@ class AdapterCourses(private val context: Context, private var courseList: List<
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
}

fun bind(position: Int) {
adapterPosition = position
}
}

fun clearSelection() {
selectedItems.clear()
notifyDataSetChanged()
}

fun getTotalPages(): Int {
return if (courseList.isNotEmpty() && itemsPerPage != Int.MAX_VALUE) {
ceil(courseList.size.toDouble() / itemsPerPage).toInt()
} else {
1
}
}

companion object {
@JvmStatic
fun showRating(`object`: JsonObject?, average: TextView?, ratingCount: TextView?, ratingBar: AppCompatRatingBar?) {
Expand All @@ -298,7 +346,7 @@ class AdapterCourses(private val context: Context, private var courseList: List<
}

fun prependBaseUrlToImages(markdownContent: String?, baseUrl: String): String {
val pattern = "!\\[.*?\\]\\((.*?)\\)"
val pattern = "!\\[.*?]\\((.*?)\\)"
val imagePattern = Pattern.compile(pattern)
val matcher = markdownContent?.let { imagePattern.matcher(it) }
val result = StringBuffer()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.ole.planet.myplanet.ui.courses

import android.app.AlertDialog
import android.content.DialogInterface
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
Expand All @@ -10,8 +11,10 @@ import android.widget.AdapterView
import android.widget.Button
import android.widget.CheckBox
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.Spinner
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import com.google.gson.Gson
Expand All @@ -28,6 +31,7 @@ import org.ole.planet.myplanet.model.RealmTag
import org.ole.planet.myplanet.model.RealmTag.Companion.getTagsArray
import org.ole.planet.myplanet.ui.resources.CollectionsFragment
import org.ole.planet.myplanet.utilities.KeyboardUtils.setupUI
import org.ole.planet.myplanet.utilities.Utilities.getItemsPerPageValue
import java.util.Calendar
import java.util.UUID

Expand All @@ -44,6 +48,12 @@ class CoursesFragment : BaseRecyclerFragment<RealmMyCourse?>(), OnCourseItemSele
lateinit var spnSubject: Spinner
lateinit var searchTags: MutableList<RealmTag>
private lateinit var confirmation: AlertDialog
private lateinit var btnPrevious: TextView
private lateinit var btnNext: TextView
private lateinit var spnItemsPerPage: Spinner
private lateinit var ltPagination: LinearLayout
private var drawableNext: Drawable? = null
private var drawablePrevious: Drawable? = null
override fun getLayout(): Int {
return R.layout.fragment_my_course
}
Expand Down Expand Up @@ -90,6 +100,31 @@ class CoursesFragment : BaseRecyclerFragment<RealmMyCourse?>(), OnCourseItemSele
.setNegativeButton(R.string.no, null).show()
}

drawableNext = ContextCompat.getDrawable(requireContext(), R.drawable.ic_right_arrow)
drawableNext?.setTint(ContextCompat.getColor(requireContext(), R.color.md_black_1000))
btnNext.setCompoundDrawablesWithIntrinsicBounds(null, null, drawableNext, null)

drawablePrevious = ContextCompat.getDrawable(requireContext(), R.drawable.ic_left_arrow)
drawablePrevious?.setTint(ContextCompat.getColor(requireContext(), R.color.md_black_1000))
btnPrevious.setCompoundDrawablesWithIntrinsicBounds(drawablePrevious, null, null, null)

spnItemsPerPage.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
val selectedValue = parent.getItemAtPosition(position).toString()
val itemsPerPage = getItemsPerPageValue(selectedValue)
adapterCourses.itemsPerPage = itemsPerPage
adapterCourses.currentPage = 1
adapterCourses.clearSelection()
adapterCourses.notifyDataSetChanged()
updateButtonVisibility()
selectAll.isChecked = false
selectAll.text = getString(R.string.select_all)
recyclerView.scrollToPosition(0)
}

override fun onNothingSelected(parent: AdapterView<*>) {}
}

requireView().findViewById<View>(R.id.btn_collections).setOnClickListener {
val f = CollectionsFragment.getInstance(searchTags, "courses")
f.setListener(this)
Expand All @@ -100,8 +135,67 @@ class CoursesFragment : BaseRecyclerFragment<RealmMyCourse?>(), OnCourseItemSele
showNoData(tvMessage, adapterCourses.itemCount, "courses")
setupUI(requireView().findViewById(R.id.my_course_parent_layout), requireActivity())
changeButtonStatus()
if (!isMyCourseLib) tvFragmentInfo.setText(R.string.our_courses)
if (!isMyCourseLib) {
tvFragmentInfo.setText(R.string.our_courses)
}
additionalSetup()

btnNext.setOnClickListener {
if (adapterCourses.currentPage * adapterCourses.itemsPerPage < adapterCourses.getTotalCourseCount()) {
adapterCourses.currentPage++
adapterCourses.clearSelection()
adapterCourses.notifyDataSetChanged()
updateButtonVisibility()
selectAll.isChecked = false
selectAll.text = getString(R.string.select_all)
recyclerView.scrollToPosition(0)
}
}

btnPrevious.setOnClickListener {
if (adapterCourses.currentPage > 1) {
adapterCourses.currentPage--
adapterCourses.clearSelection()
adapterCourses.notifyDataSetChanged()
updateButtonVisibility()
selectAll.isChecked = false
selectAll.text = getString(R.string.select_all)
recyclerView.scrollToPosition(0)
}
}
updateButtonVisibility()
}

private fun updateButtonVisibility() {
if (adapterCourses.itemsPerPage == Int.MAX_VALUE) {
btnNext.visibility = View.GONE
btnPrevious.visibility = View.GONE
} else {
if (adapterCourses.currentPage < adapterCourses.getTotalPages()) {
btnNext.isEnabled = true
btnNext.setTextColor(ContextCompat.getColor(requireContext(), R.color.md_black_1000))
drawableNext?.setTint(ContextCompat.getColor(requireContext(), R.color.md_black_1000))
} else {
btnNext.isEnabled = false
btnNext.setTextColor(ContextCompat.getColor(requireContext(), R.color.md_grey_500))
drawableNext?.setTint(ContextCompat.getColor(requireContext(), R.color.md_grey_500))
}
if (adapterCourses.currentPage > 1) {
btnPrevious.isEnabled = true
btnPrevious.setTextColor(ContextCompat.getColor(requireContext(), R.color.md_black_1000))
drawablePrevious?.setTint(ContextCompat.getColor(requireContext(), R.color.md_black_1000))
} else {
btnPrevious.isEnabled = false
btnPrevious.setTextColor(ContextCompat.getColor(requireContext(), R.color.md_grey_500))
drawablePrevious?.setTint(ContextCompat.getColor(requireContext(), R.color.md_grey_500))
}
}

if (adapterCourses.itemCount == 0) {
ltPagination.visibility = View.GONE
tvDelete?.visibility = View.GONE
btnRemove.visibility = View.GONE
}
}

private fun additionalSetup() {
Expand All @@ -123,7 +217,7 @@ class CoursesFragment : BaseRecyclerFragment<RealmMyCourse?>(), OnCourseItemSele
confirmation.show()
addToMyList()
selectedItems?.clear()
tvAddToLib.isEnabled = false // selectedItems will always have a size of 0
tvAddToLib.isEnabled = false
checkList()
}
}
Expand All @@ -138,16 +232,21 @@ class CoursesFragment : BaseRecyclerFragment<RealmMyCourse?>(), OnCourseItemSele
spnGrade.onItemSelectedListener = itemSelectedListener
spnSubject.onItemSelectedListener = itemSelectedListener
selectAll = requireView().findViewById(R.id.selectAll)
btnNext = requireView().findViewById(R.id.next)
btnPrevious = requireView().findViewById(R.id.previous)
spnItemsPerPage = requireView().findViewById(R.id.spn_items_per_page)
ltPagination = requireView().findViewById(R.id.ltPagination)

checkList()

selectAll.setOnClickListener {
val allSelected = selectedItems?.size == adapterCourses.getCourseList().size
val allSelected = adapterCourses.areAllSelected()
adapterCourses.selectAllItems(!allSelected)
if (allSelected) {
selectAll.isChecked = false
selectAll.text = getString(R.string.select_all)
selectAll.isChecked = !allSelected
selectAll.text = if (allSelected) {
getString(R.string.select_all)
} else {
selectAll.isChecked = true
selectAll.text = getString(R.string.unselect_all)
getString(R.string.unselect_all)
}
}
}
Expand Down Expand Up @@ -239,7 +338,7 @@ class CoursesFragment : BaseRecyclerFragment<RealmMyCourse?>(), OnCourseItemSele
val li: MutableList<RealmTag> = ArrayList()
li.add(tag)
searchTags = li
tvSelected.text = R.string.selected.toString() + tag.name
tvSelected.text = "${R.string.selected}${tag.name}"
adapterCourses.setCourseList(filterCourseByTag(etSearch.text.toString(), li))
showNoData(tvMessage, adapterCourses.itemCount, "courses")
}
Expand Down
Loading