Fix OOM in grade statistics (#1192)

This commit is contained in:
Rafał Borcz 2021-03-06 16:50:06 +01:00 committed by GitHub
parent 9139febbdf
commit 1afa7ecf3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 347 additions and 223 deletions

View File

@ -5,10 +5,9 @@ import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
import io.github.wulkanowy.data.db.entities.Semester import io.github.wulkanowy.data.db.entities.Semester
import io.github.wulkanowy.data.pojos.GradeStatisticsItem import io.github.wulkanowy.data.pojos.GradeStatisticsItem
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
import io.github.wulkanowy.sdk.pojo.GradeStatisticsSubject as SdkGradeStatisticsSubject
import io.github.wulkanowy.sdk.pojo.GradeStatisticsSemester as SdkGradeStatisticsSemester
import io.github.wulkanowy.sdk.pojo.GradePointsStatistics as SdkGradePointsStatistics import io.github.wulkanowy.sdk.pojo.GradePointsStatistics as SdkGradePointsStatistics
import io.github.wulkanowy.sdk.pojo.GradeStatisticsSemester as SdkGradeStatisticsSemester
import io.github.wulkanowy.sdk.pojo.GradeStatisticsSubject as SdkGradeStatisticsSubject
@JvmName("mapToEntitiesSubject") @JvmName("mapToEntitiesSubject")
fun List<SdkGradeStatisticsSubject>.mapToEntities(semester: Semester) = map { fun List<SdkGradeStatisticsSubject>.mapToEntities(semester: Semester) = map {
@ -51,7 +50,7 @@ fun List<SdkGradePointsStatistics>.mapToEntities(semester: Semester) = map {
fun List<GradePartialStatistics>.mapPartialToStatisticItems() = filterNot { it.classAmounts.isEmpty() }.map { fun List<GradePartialStatistics>.mapPartialToStatisticItems() = filterNot { it.classAmounts.isEmpty() }.map {
GradeStatisticsItem( GradeStatisticsItem(
type = ViewType.PARTIAL, type = GradeStatisticsItem.DataType.PARTIAL,
average = it.classAverage, average = it.classAverage,
partial = it, partial = it,
points = null, points = null,
@ -61,7 +60,7 @@ fun List<GradePartialStatistics>.mapPartialToStatisticItems() = filterNot { it.c
fun List<GradeSemesterStatistics>.mapSemesterToStatisticItems() = filterNot { it.amounts.isEmpty() }.map { fun List<GradeSemesterStatistics>.mapSemesterToStatisticItems() = filterNot { it.amounts.isEmpty() }.map {
GradeStatisticsItem( GradeStatisticsItem(
type = ViewType.SEMESTER, type = GradeStatisticsItem.DataType.SEMESTER,
partial = null, partial = null,
points = null, points = null,
average = "", average = "",
@ -71,7 +70,7 @@ fun List<GradeSemesterStatistics>.mapSemesterToStatisticItems() = filterNot { it
fun List<GradePointsStatistics>.mapPointsToStatisticsItems() = map { fun List<GradePointsStatistics>.mapPointsToStatisticsItems() = map {
GradeStatisticsItem( GradeStatisticsItem(
type = ViewType.POINTS, type = GradeStatisticsItem.DataType.POINTS,
partial = null, partial = null,
semester = null, semester = null,
average = "", average = "",

View File

@ -3,11 +3,10 @@ package io.github.wulkanowy.data.pojos
import io.github.wulkanowy.data.db.entities.GradePartialStatistics import io.github.wulkanowy.data.db.entities.GradePartialStatistics
import io.github.wulkanowy.data.db.entities.GradePointsStatistics import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
data class GradeStatisticsItem( data class GradeStatisticsItem(
val type: ViewType, val type: DataType,
val average: String, val average: String,
@ -16,4 +15,11 @@ data class GradeStatisticsItem(
val semester: GradeSemesterStatistics?, val semester: GradeSemesterStatistics?,
val points: GradePointsStatistics? val points: GradePointsStatistics?
)
) {
enum class DataType {
SEMESTER,
PARTIAL,
POINTS,
}
}

View File

@ -22,6 +22,7 @@ import io.github.wulkanowy.data.db.entities.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
import io.github.wulkanowy.data.pojos.GradeStatisticsItem import io.github.wulkanowy.data.pojos.GradeStatisticsItem
import io.github.wulkanowy.databinding.ItemGradeStatisticsBarBinding import io.github.wulkanowy.databinding.ItemGradeStatisticsBarBinding
import io.github.wulkanowy.databinding.ItemGradeStatisticsHeaderBinding
import io.github.wulkanowy.databinding.ItemGradeStatisticsPieBinding import io.github.wulkanowy.databinding.ItemGradeStatisticsPieBinding
import io.github.wulkanowy.utils.getThemeAttrColor import io.github.wulkanowy.utils.getThemeAttrColor
import javax.inject.Inject import javax.inject.Inject
@ -29,12 +30,16 @@ import javax.inject.Inject
class GradeStatisticsAdapter @Inject constructor() : class GradeStatisticsAdapter @Inject constructor() :
RecyclerView.Adapter<RecyclerView.ViewHolder>() { RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var currentDataType = GradeStatisticsItem.DataType.PARTIAL
var items = emptyList<GradeStatisticsItem>() var items = emptyList<GradeStatisticsItem>()
var theme: String = "vulcan" var theme: String = "vulcan"
var showAllSubjectsOnList: Boolean = false var showAllSubjectsOnList: Boolean = false
var onDataTypeChangeListener: () -> Unit = {}
private val vulcanGradeColors = listOf( private val vulcanGradeColors = listOf(
6 to R.color.grade_vulcan_six, 6 to R.color.grade_vulcan_six,
5 to R.color.grade_vulcan_five, 5 to R.color.grade_vulcan_five,
@ -62,37 +67,90 @@ class GradeStatisticsAdapter @Inject constructor() :
"6, 6-", "5, 5-, 5+", "4, 4-, 4+", "3, 3-, 3+", "2, 2-, 2+", "1, 1+" "6, 6-", "5, 5-, 5+", "4, 4-, 4+", "3, 3-, 3+", "2, 2-, 2+", "1, 1+"
) )
override fun getItemCount() = if (showAllSubjectsOnList) items.size else (if (items.isEmpty()) 0 else 1) override fun getItemCount() =
(if (showAllSubjectsOnList) items.size else (if (items.isEmpty()) 0 else 1)) + 1
override fun getItemViewType(position: Int) = items[position].type.id override fun getItemViewType(position: Int) =
if (position == 0) {
ViewType.HEADER.id
} else {
when (items[position - 1].type) {
GradeStatisticsItem.DataType.PARTIAL -> ViewType.PARTIAL.id
GradeStatisticsItem.DataType.POINTS -> ViewType.POINTS.id
GradeStatisticsItem.DataType.SEMESTER -> ViewType.SEMESTER.id
}
}
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.PARTIAL.id -> PartialViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false)) ViewType.PARTIAL.id -> PartialViewHolder(
ViewType.SEMESTER.id -> SemesterViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false)) ItemGradeStatisticsPieBinding.inflate(inflater, parent, false)
ViewType.POINTS.id -> PointsViewHolder(ItemGradeStatisticsBarBinding.inflate(inflater, parent, false)) )
ViewType.SEMESTER.id -> SemesterViewHolder(
ItemGradeStatisticsPieBinding.inflate(inflater, parent, false)
)
ViewType.POINTS.id -> PointsViewHolder(
ItemGradeStatisticsBarBinding.inflate(inflater, parent, false)
)
ViewType.HEADER.id -> HeaderViewHolder(
ItemGradeStatisticsHeaderBinding.inflate(inflater, parent, false)
)
else -> throw IllegalStateException() else -> throw IllegalStateException()
} }
} }
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val index = position - 1
when (holder) { when (holder) {
is PartialViewHolder -> bindPartialChart(holder.binding, items[position].partial!!) is PartialViewHolder -> bindPartialChart(holder.binding, items[index].partial!!)
is SemesterViewHolder -> bindSemesterChart(holder.binding, items[position].semester!!) is SemesterViewHolder -> bindSemesterChart(holder.binding, items[index].semester!!)
is PointsViewHolder -> bindBarChart(holder.binding, items[position].points!!) is PointsViewHolder -> bindBarChart(holder.binding, items[index].points!!)
is HeaderViewHolder -> bindHeader(holder.binding)
} }
} }
private fun bindPartialChart(binding: ItemGradeStatisticsPieBinding, partials: GradePartialStatistics) { private fun bindHeader(binding: ItemGradeStatisticsHeaderBinding) {
binding.gradeStatisticsTypeSwitch.check(
when (currentDataType) {
GradeStatisticsItem.DataType.PARTIAL -> R.id.gradeStatisticsTypePartial
GradeStatisticsItem.DataType.SEMESTER -> R.id.gradeStatisticsTypeSemester
GradeStatisticsItem.DataType.POINTS -> R.id.gradeStatisticsTypePoints
}
)
binding.gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, checkedId ->
currentDataType = when (checkedId) {
R.id.gradeStatisticsTypePartial -> GradeStatisticsItem.DataType.PARTIAL
R.id.gradeStatisticsTypeSemester -> GradeStatisticsItem.DataType.SEMESTER
R.id.gradeStatisticsTypePoints -> GradeStatisticsItem.DataType.POINTS
else -> GradeStatisticsItem.DataType.PARTIAL
}
onDataTypeChangeListener()
}
}
private fun bindPartialChart(
binding: ItemGradeStatisticsPieBinding,
partials: GradePartialStatistics
) {
bindPieChart(binding, partials.subject, partials.classAverage, partials.classAmounts) bindPieChart(binding, partials.subject, partials.classAverage, partials.classAmounts)
} }
private fun bindSemesterChart(binding: ItemGradeStatisticsPieBinding, semester: GradeSemesterStatistics) { private fun bindSemesterChart(
binding: ItemGradeStatisticsPieBinding,
semester: GradeSemesterStatistics
) {
bindPieChart(binding, semester.subject, semester.average, semester.amounts) bindPieChart(binding, semester.subject, semester.average, semester.amounts)
} }
private fun bindPieChart(binding: ItemGradeStatisticsPieBinding, subject: String, average: String, amounts: List<Int>) { private fun bindPieChart(
binding: ItemGradeStatisticsPieBinding,
subject: String,
average: String,
amounts: List<Int>
) {
with(binding.gradeStatisticsPieTitle) { with(binding.gradeStatisticsPieTitle) {
text = subject text = subject
visibility = if (items.size == 1 || !showAllSubjectsOnList) GONE else VISIBLE visibility = if (items.size == 1 || !showAllSubjectsOnList) GONE else VISIBLE
@ -114,7 +172,8 @@ class GradeStatisticsAdapter @Inject constructor() :
valueTextSize = 12f valueTextSize = 12f
sliceSpace = 1f sliceSpace = 1f
valueTextColor = Color.WHITE valueTextColor = Color.WHITE
val grades = amounts.mapIndexed { grade, amount -> (grade + 1) to amount }.filterNot { it.second == 0 } val grades = amounts.mapIndexed { grade, amount -> (grade + 1) to amount }
.filterNot { it.second == 0 }
setColors(grades.reversed().map { (grade, _) -> setColors(grades.reversed().map { (grade, _) ->
gradeColors.single { color -> color.first == grade }.second gradeColors.single { color -> color.first == grade }.second
}.toIntArray(), binding.root.context) }.toIntArray(), binding.root.context)
@ -126,7 +185,11 @@ class GradeStatisticsAdapter @Inject constructor() :
data = PieData(dataset).apply { data = PieData(dataset).apply {
setValueFormatter(object : ValueFormatter() { setValueFormatter(object : ValueFormatter() {
override fun getPieLabel(value: Float, pieEntry: PieEntry): String { override fun getPieLabel(value: Float, pieEntry: PieEntry): String {
return resources.getQuantityString(R.plurals.grade_number_item, value.toInt(), value.toInt()) return resources.getQuantityString(
R.plurals.grade_number_item,
value.toInt(),
value.toInt()
)
} }
}) })
} }
@ -143,11 +206,14 @@ class GradeStatisticsAdapter @Inject constructor() :
val numberOfGradesString = amounts.fold(0) { acc, it -> acc + it } val numberOfGradesString = amounts.fold(0) { acc, it -> acc + it }
.let { resources.getQuantityString(R.plurals.grade_number_item, it, it) } .let { resources.getQuantityString(R.plurals.grade_number_item, it, it) }
val averageString = binding.root.context.getString(R.string.grade_statistics_average, average) val averageString =
binding.root.context.getString(R.string.grade_statistics_average, average)
minAngleForSlices = 25f minAngleForSlices = 25f
description.isEnabled = false description.isEnabled = false
centerText = numberOfGradesString + ("\n\n" + averageString).takeIf { average.isNotBlank() }.orEmpty() centerText =
numberOfGradesString + ("\n\n" + averageString).takeIf { average.isNotBlank() }
.orEmpty()
setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground)) setHoleColor(context.getThemeAttrColor(android.R.attr.windowBackground))
setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary)) setCenterTextColor(context.getThemeAttrColor(android.R.attr.textColorPrimary))
@ -155,16 +221,21 @@ class GradeStatisticsAdapter @Inject constructor() :
} }
} }
private fun bindBarChart(binding: ItemGradeStatisticsBarBinding, points: GradePointsStatistics) { private fun bindBarChart(
binding: ItemGradeStatisticsBarBinding,
points: GradePointsStatistics
) {
with(binding.gradeStatisticsBarTitle) { with(binding.gradeStatisticsBarTitle) {
text = points.subject text = points.subject
visibility = if (items.size == 1) GONE else VISIBLE visibility = if (items.size == 1) GONE else VISIBLE
} }
val dataset = BarDataSet(listOf( val dataset = BarDataSet(
listOf(
BarEntry(1f, points.others.toFloat()), BarEntry(1f, points.others.toFloat()),
BarEntry(2f, points.student.toFloat()) BarEntry(2f, points.student.toFloat())
), binding.root.context.getString(R.string.grade_statistics_legend)) ), binding.root.context.getString(R.string.grade_statistics_legend)
)
with(dataset) { with(dataset) {
valueTextSize = 12f valueTextSize = 12f
@ -189,7 +260,8 @@ class GradeStatisticsAdapter @Inject constructor() :
form = Legend.LegendForm.SQUARE form = Legend.LegendForm.SQUARE
}, },
LegendEntry().apply { LegendEntry().apply {
label = binding.root.context.getString(R.string.grade_statistics_average_student) label =
binding.root.context.getString(R.string.grade_statistics_average_student)
formColor = gradePointsColors[1] formColor = gradePointsColors[1]
form = Legend.LegendForm.SQUARE form = Legend.LegendForm.SQUARE
} }
@ -226,4 +298,7 @@ class GradeStatisticsAdapter @Inject constructor() :
private class PointsViewHolder(val binding: ItemGradeStatisticsBarBinding) : private class PointsViewHolder(val binding: ItemGradeStatisticsBarBinding) :
RecyclerView.ViewHolder(binding.root) RecyclerView.ViewHolder(binding.root)
private class HeaderViewHolder(val binding: ItemGradeStatisticsHeaderBinding) :
RecyclerView.ViewHolder(binding.root)
} }

View File

@ -38,27 +38,28 @@ class GradeStatisticsFragment :
override val isViewEmpty get() = statisticsAdapter.items.isEmpty() override val isViewEmpty get() = statisticsAdapter.items.isEmpty()
override val currentType override val currentType get() = statisticsAdapter.currentDataType
get() = when (binding.gradeStatisticsTypeSwitch.checkedRadioButtonId) {
R.id.gradeStatisticsTypeSemester -> ViewType.SEMESTER
R.id.gradeStatisticsTypePartial -> ViewType.PARTIAL
else -> ViewType.POINTS
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding = FragmentGradeStatisticsBinding.bind(view) binding = FragmentGradeStatisticsBinding.bind(view)
messageContainer = binding.gradeStatisticsSwipe messageContainer = binding.gradeStatisticsSwipe
presenter.onAttachView(this, savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? ViewType) presenter.onAttachView(
this,
savedInstanceState?.getSerializable(SAVED_CHART_TYPE) as? GradeStatisticsItem.DataType
)
} }
override fun initView() { override fun initView() {
statisticsAdapter.onDataTypeChangeListener = presenter::onTypeChange
with(binding.gradeStatisticsRecycler) { with(binding.gradeStatisticsRecycler) {
layoutManager = LinearLayoutManager(requireContext()) layoutManager = LinearLayoutManager(requireContext())
adapter = statisticsAdapter adapter = statisticsAdapter
} }
subjectsAdapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf()) subjectsAdapter =
ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, mutableListOf())
subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject) subjectsAdapter.setDropDownViewResource(R.layout.item_attendance_summary_subject)
with(binding.gradeStatisticsSubjects) { with(binding.gradeStatisticsSubjects) {
@ -71,7 +72,9 @@ class GradeStatisticsFragment :
gradeStatisticsSwipe.setOnRefreshListener(presenter::onSwipeRefresh) gradeStatisticsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
gradeStatisticsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary)) gradeStatisticsSwipe.setColorSchemeColors(requireContext().getThemeAttrColor(R.attr.colorPrimary))
gradeStatisticsSwipe.setProgressBackgroundColorSchemeColor(requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)) gradeStatisticsSwipe.setProgressBackgroundColorSchemeColor(
requireContext().getThemeAttrColor(R.attr.colorSwipeRefresh)
)
gradeStatisticsErrorRetry.setOnClickListener { presenter.onRetry() } gradeStatisticsErrorRetry.setOnClickListener { presenter.onRetry() }
gradeStatisticsErrorDetails.setOnClickListener { presenter.onDetailsClick() } gradeStatisticsErrorDetails.setOnClickListener { presenter.onDetailsClick() }
} }
@ -85,11 +88,15 @@ class GradeStatisticsFragment :
} }
} }
override fun updateData(items: List<GradeStatisticsItem>, theme: String, showAllSubjectsOnStatisticsList: Boolean) { override fun updateData(
newItems: List<GradeStatisticsItem>,
newTheme: String,
showAllSubjectsOnStatisticsList: Boolean
) {
with(statisticsAdapter) { with(statisticsAdapter) {
this.showAllSubjectsOnList = showAllSubjectsOnStatisticsList showAllSubjectsOnList = showAllSubjectsOnStatisticsList
this.theme = theme theme = newTheme
this.items = items items = newItems
notifyDataSetChanged() notifyDataSetChanged()
} }
} }
@ -103,11 +110,7 @@ class GradeStatisticsFragment :
} }
override fun resetView() { override fun resetView() {
binding.gradeStatisticsScroll.scrollTo(0, 0) binding.gradeStatisticsRecycler.scrollToPosition(0)
}
override fun showContent(show: Boolean) {
binding.gradeStatisticsRecycler.visibility = if (show) View.VISIBLE else View.GONE
} }
override fun showEmpty(show: Boolean) { override fun showEmpty(show: Boolean) {
@ -154,11 +157,6 @@ class GradeStatisticsFragment :
(parentFragment as? GradeFragment)?.onChildRefresh() (parentFragment as? GradeFragment)?.onChildRefresh()
} }
override fun onResume() {
super.onResume()
binding.gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, _ -> presenter.onTypeChange() }
}
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState) super.onSaveInstanceState(outState)
outState.putSerializable(SAVED_CHART_TYPE, presenter.currentType) outState.putSerializable(SAVED_CHART_TYPE, presenter.currentType)

View File

@ -35,12 +35,12 @@ class GradeStatisticsPresenter @Inject constructor(
private lateinit var lastError: Throwable private lateinit var lastError: Throwable
var currentType: ViewType = ViewType.PARTIAL var currentType: GradeStatisticsItem.DataType = GradeStatisticsItem.DataType.PARTIAL
private set private set
fun onAttachView(view: GradeStatisticsView, type: ViewType?) { fun onAttachView(view: GradeStatisticsView, type: GradeStatisticsItem.DataType?) {
super.onAttachView(view) super.onAttachView(view)
currentType = type ?: ViewType.PARTIAL currentType = type ?: GradeStatisticsItem.DataType.PARTIAL
view.initView() view.initView()
errorHandler.showErrorMessage = ::showErrorViewOnError errorHandler.showErrorMessage = ::showErrorViewOnError
} }
@ -59,11 +59,11 @@ class GradeStatisticsPresenter @Inject constructor(
} }
fun onParentViewChangeSemester() { fun onParentViewChangeSemester() {
clearDataInView()
view?.run { view?.run {
showProgress(true) showProgress(true)
enableSwipe(false) enableSwipe(false)
showRefresh(false) showRefresh(false)
showContent(false)
showErrorView(false) showErrorView(false)
showEmpty(false) showEmpty(false)
clearView() clearView()
@ -90,8 +90,8 @@ class GradeStatisticsPresenter @Inject constructor(
fun onSubjectSelected(name: String?) { fun onSubjectSelected(name: String?) {
Timber.i("Select grade stats subject $name") Timber.i("Select grade stats subject $name")
clearDataInView()
view?.run { view?.run {
showContent(false)
showProgress(true) showProgress(true)
enableSwipe(false) enableSwipe(false)
showEmpty(false) showEmpty(false)
@ -104,11 +104,11 @@ class GradeStatisticsPresenter @Inject constructor(
} }
fun onTypeChange() { fun onTypeChange() {
val type = view?.currentType ?: ViewType.POINTS val type = view?.currentType ?: GradeStatisticsItem.DataType.POINTS
Timber.i("Select grade stats semester: $type") Timber.i("Select grade stats semester: $type")
cancelJobs("load") cancelJobs("load")
clearDataInView()
view?.run { view?.run {
showContent(false)
showProgress(true) showProgress(true)
enableSwipe(false) enableSwipe(false)
showEmpty(false) showEmpty(false)
@ -143,10 +143,16 @@ class GradeStatisticsPresenter @Inject constructor(
}.launch("subjects") }.launch("subjects")
} }
private fun loadDataByType(semesterId: Int, subjectName: String, type: ViewType, forceRefresh: Boolean = false) { private fun loadDataByType(
semesterId: Int,
subjectName: String,
type: GradeStatisticsItem.DataType,
forceRefresh: Boolean = false
) {
Timber.i("Loading grade stats data started") Timber.i("Loading grade stats data started")
currentSubjectName = if (preferencesRepository.showAllSubjectsOnStatisticsList) "Wszystkie" else subjectName currentSubjectName =
if (preferencesRepository.showAllSubjectsOnStatisticsList) "Wszystkie" else subjectName
currentType = type currentType = type
flowWithResourceIn { flowWithResourceIn {
@ -156,9 +162,30 @@ class GradeStatisticsPresenter @Inject constructor(
with(gradeStatisticsRepository) { with(gradeStatisticsRepository) {
when (type) { when (type) {
ViewType.PARTIAL -> getGradesPartialStatistics(student, semester, currentSubjectName, forceRefresh) GradeStatisticsItem.DataType.PARTIAL -> {
ViewType.SEMESTER -> getGradesSemesterStatistics(student, semester, currentSubjectName, forceRefresh) getGradesPartialStatistics(
ViewType.POINTS -> getGradesPointsStatistics(student, semester, currentSubjectName, forceRefresh) student = student,
semester = semester,
subjectName = currentSubjectName,
forceRefresh = forceRefresh
)
}
GradeStatisticsItem.DataType.SEMESTER -> {
getGradesSemesterStatistics(
student = student,
semester = semester,
subjectName = currentSubjectName,
forceRefresh = forceRefresh
)
}
GradeStatisticsItem.DataType.POINTS -> {
getGradesPointsStatistics(
student = student,
semester = semester,
subjectName = currentSubjectName,
forceRefresh = forceRefresh
)
}
} }
} }
}.onEach { }.onEach {
@ -168,12 +195,15 @@ class GradeStatisticsPresenter @Inject constructor(
if (!isNoContent) { if (!isNoContent) {
view?.run { view?.run {
showEmpty(isNoContent) showEmpty(isNoContent)
showContent(!isNoContent)
showErrorView(false) showErrorView(false)
enableSwipe(true) enableSwipe(true)
showRefresh(true) showRefresh(true)
showProgress(false) showProgress(false)
updateData(it.data!!, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList) updateData(
if (isNoContent) emptyList() else it.data!!,
preferencesRepository.gradeColorTheme,
preferencesRepository.showAllSubjectsOnStatisticsList
)
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
} }
} }
@ -183,9 +213,12 @@ class GradeStatisticsPresenter @Inject constructor(
view?.run { view?.run {
val isNoContent = checkIsNoContent(it.data!!, type) val isNoContent = checkIsNoContent(it.data!!, type)
showEmpty(isNoContent) showEmpty(isNoContent)
showContent(!isNoContent)
showErrorView(false) showErrorView(false)
updateData(it.data, preferencesRepository.gradeColorTheme, preferencesRepository.showAllSubjectsOnStatisticsList) updateData(
if (isNoContent) emptyList() else it.data,
preferencesRepository.gradeColorTheme,
preferencesRepository.showAllSubjectsOnStatisticsList
)
showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList) showSubjects(!preferencesRepository.showAllSubjectsOnStatisticsList)
} }
analytics.logEvent( analytics.logEvent(
@ -209,14 +242,29 @@ class GradeStatisticsPresenter @Inject constructor(
}.launch("load") }.launch("load")
} }
private fun checkIsNoContent(items: List<GradeStatisticsItem>, type: ViewType): Boolean { private fun checkIsNoContent(
items: List<GradeStatisticsItem>,
type: GradeStatisticsItem.DataType
): Boolean {
return items.isEmpty() || when (type) { return items.isEmpty() || when (type) {
ViewType.SEMESTER -> items.firstOrNull()?.semester?.amounts.orEmpty().sum() == 0 GradeStatisticsItem.DataType.SEMESTER -> {
ViewType.PARTIAL -> items.firstOrNull()?.partial?.classAmounts.orEmpty().sum() == 0 items.firstOrNull()?.semester?.amounts.orEmpty().sum() == 0
ViewType.POINTS -> items.firstOrNull()?.points?.let { points ->
points.student == .0 && points.others == .0
} ?: false
} }
GradeStatisticsItem.DataType.PARTIAL -> {
items.firstOrNull()?.partial?.classAmounts.orEmpty().sum() == 0
}
GradeStatisticsItem.DataType.POINTS -> {
items.firstOrNull()?.points?.let { points -> points.student == .0 && points.others == .0 } ?: false
}
}
}
private fun clearDataInView() {
view?.updateData(
emptyList(),
preferencesRepository.gradeColorTheme,
preferencesRepository.showAllSubjectsOnStatisticsList
)
} }
private fun showErrorViewOnError(message: String, error: Throwable) { private fun showErrorViewOnError(message: String, error: Throwable) {

View File

@ -7,13 +7,17 @@ interface GradeStatisticsView : BaseView {
val isViewEmpty: Boolean val isViewEmpty: Boolean
val currentType: ViewType val currentType: GradeStatisticsItem.DataType
fun initView() fun initView()
fun updateSubjects(data: ArrayList<String>) fun updateSubjects(data: ArrayList<String>)
fun updateData(items: List<GradeStatisticsItem>, theme: String, showAllSubjectsOnStatisticsList: Boolean) fun updateData(
newItems: List<GradeStatisticsItem>,
newTheme: String,
showAllSubjectsOnStatisticsList: Boolean
)
fun showSubjects(show: Boolean) fun showSubjects(show: Boolean)
@ -25,8 +29,6 @@ interface GradeStatisticsView : BaseView {
fun resetView() fun resetView()
fun showContent(show: Boolean)
fun showEmpty(show: Boolean) fun showEmpty(show: Boolean)
fun showErrorView(show: Boolean) fun showErrorView(show: Boolean)

View File

@ -3,5 +3,6 @@ package io.github.wulkanowy.ui.modules.grade.statistics
enum class ViewType(val id: Int) { enum class ViewType(val id: Int) {
SEMESTER(1), SEMESTER(1),
PARTIAL(2), PARTIAL(2),
POINTS(3) POINTS(3),
HEADER(4)
} }

View File

@ -3,8 +3,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical">
tools:context=".ui.modules.grade.statistics.GradeStatisticsFragment">
<io.github.wulkanowy.ui.widgets.MaterialLinearLayout <io.github.wulkanowy.ui.widgets.MaterialLinearLayout
android:id="@+id/gradeStatisticsSubjectsContainer" android:id="@+id/gradeStatisticsSubjectsContainer"
@ -12,7 +11,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?android:windowBackground" android:background="?android:windowBackground"
android:padding="5dp" android:padding="5dp"
android:visibility="gone" android:visibility="visible"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="UnusedAttribute" tools:ignore="UnusedAttribute"
tools:listitem="@layout/item_attendance_summary" tools:listitem="@layout/item_attendance_summary"
tools:visibility="visible"> tools:visibility="visible">
@ -34,67 +34,12 @@
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/gradeStatisticsSwipe" android:id="@+id/gradeStatisticsSwipe"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:id="@+id/gradeStatisticsScroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<RadioGroup
android:id="@+id/gradeStatisticsTypeSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingTop="5dp"
android:paddingEnd="16dp">
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/gradeStatisticsTypePartial"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:tag="partial"
android:text="@string/grade_statistics_partial" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/gradeStatisticsTypeSemester"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="annual"
android:text="@string/grade_statistics_semester" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/gradeStatisticsTypePoints"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="points"
android:text="@string/grade_statistics_points" />
</RadioGroup>
</HorizontalScrollView>
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/gradeStatisticsRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_grade_statistics_pie" />
<com.google.android.material.progressindicator.CircularProgressIndicator <com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/gradeStatisticsProgress" android:id="@+id/gradeStatisticsProgress"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -103,6 +48,12 @@
android:indeterminate="true" android:indeterminate="true"
tools:visibility="gone" /> tools:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/gradeStatisticsRecycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_grade_statistics_pie" />
<LinearLayout <LinearLayout
android:id="@+id/gradeStatisticsEmpty" android:id="@+id/gradeStatisticsEmpty"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -128,7 +79,6 @@
android:textSize="20sp" /> android:textSize="20sp" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/gradeStatisticsError" android:id="@+id/gradeStatisticsError"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -180,7 +130,5 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout> </LinearLayout>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<RadioGroup
android:id="@+id/gradeStatisticsTypeSwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingTop="5dp"
android:paddingEnd="16dp">
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/gradeStatisticsTypePartial"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:tag="partial"
android:text="@string/grade_statistics_partial" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/gradeStatisticsTypeSemester"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="annual"
android:text="@string/grade_statistics_semester" />
<com.google.android.material.radiobutton.MaterialRadioButton
android:id="@+id/gradeStatisticsTypePoints"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:tag="points"
android:text="@string/grade_statistics_points" />
</RadioGroup>
</HorizontalScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>