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.Semester
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.GradeStatisticsSemester as SdkGradeStatisticsSemester
import io.github.wulkanowy.sdk.pojo.GradeStatisticsSubject as SdkGradeStatisticsSubject
@JvmName("mapToEntitiesSubject")
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 {
GradeStatisticsItem(
type = ViewType.PARTIAL,
type = GradeStatisticsItem.DataType.PARTIAL,
average = it.classAverage,
partial = it,
points = null,
@ -61,7 +60,7 @@ fun List<GradePartialStatistics>.mapPartialToStatisticItems() = filterNot { it.c
fun List<GradeSemesterStatistics>.mapSemesterToStatisticItems() = filterNot { it.amounts.isEmpty() }.map {
GradeStatisticsItem(
type = ViewType.SEMESTER,
type = GradeStatisticsItem.DataType.SEMESTER,
partial = null,
points = null,
average = "",
@ -71,7 +70,7 @@ fun List<GradeSemesterStatistics>.mapSemesterToStatisticItems() = filterNot { it
fun List<GradePointsStatistics>.mapPointsToStatisticsItems() = map {
GradeStatisticsItem(
type = ViewType.POINTS,
type = GradeStatisticsItem.DataType.POINTS,
partial = null,
semester = null,
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.GradePointsStatistics
import io.github.wulkanowy.data.db.entities.GradeSemesterStatistics
import io.github.wulkanowy.ui.modules.grade.statistics.ViewType
data class GradeStatisticsItem(
val type: ViewType,
val type: DataType,
val average: String,
@ -16,4 +15,11 @@ data class GradeStatisticsItem(
val semester: GradeSemesterStatistics?,
val points: GradePointsStatistics?
)
) {
enum class DataType {
SEMESTER,
PARTIAL,
POINTS,
}
}

View File

@ -39,7 +39,7 @@ class GradeFragment : BaseFragment<FragmentGradeBinding>(R.layout.fragment_grade
override val titleStringId get() = R.string.grade_title
override var subtitleString = " "
override var subtitleString = ""
override val currentPageIndex get() = binding.gradeViewPager.currentItem

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.pojos.GradeStatisticsItem
import io.github.wulkanowy.databinding.ItemGradeStatisticsBarBinding
import io.github.wulkanowy.databinding.ItemGradeStatisticsHeaderBinding
import io.github.wulkanowy.databinding.ItemGradeStatisticsPieBinding
import io.github.wulkanowy.utils.getThemeAttrColor
import javax.inject.Inject
@ -29,12 +30,16 @@ import javax.inject.Inject
class GradeStatisticsAdapter @Inject constructor() :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var currentDataType = GradeStatisticsItem.DataType.PARTIAL
var items = emptyList<GradeStatisticsItem>()
var theme: String = "vulcan"
var showAllSubjectsOnList: Boolean = false
var onDataTypeChangeListener: () -> Unit = {}
private val vulcanGradeColors = listOf(
6 to R.color.grade_vulcan_six,
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+"
)
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 {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
ViewType.PARTIAL.id -> PartialViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false))
ViewType.SEMESTER.id -> SemesterViewHolder(ItemGradeStatisticsPieBinding.inflate(inflater, parent, false))
ViewType.POINTS.id -> PointsViewHolder(ItemGradeStatisticsBarBinding.inflate(inflater, parent, false))
ViewType.PARTIAL.id -> PartialViewHolder(
ItemGradeStatisticsPieBinding.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()
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val index = position - 1
when (holder) {
is PartialViewHolder -> bindPartialChart(holder.binding, items[position].partial!!)
is SemesterViewHolder -> bindSemesterChart(holder.binding, items[position].semester!!)
is PointsViewHolder -> bindBarChart(holder.binding, items[position].points!!)
is PartialViewHolder -> bindPartialChart(holder.binding, items[index].partial!!)
is SemesterViewHolder -> bindSemesterChart(holder.binding, items[index].semester!!)
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)
}
private fun bindSemesterChart(binding: ItemGradeStatisticsPieBinding, semester: GradeSemesterStatistics) {
private fun bindSemesterChart(
binding: ItemGradeStatisticsPieBinding,
semester: GradeSemesterStatistics
) {
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) {
text = subject
visibility = if (items.size == 1 || !showAllSubjectsOnList) GONE else VISIBLE
@ -114,7 +172,8 @@ class GradeStatisticsAdapter @Inject constructor() :
valueTextSize = 12f
sliceSpace = 1f
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, _) ->
gradeColors.single { color -> color.first == grade }.second
}.toIntArray(), binding.root.context)
@ -126,7 +185,11 @@ class GradeStatisticsAdapter @Inject constructor() :
data = PieData(dataset).apply {
setValueFormatter(object : ValueFormatter() {
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 }
.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
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))
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) {
text = points.subject
visibility = if (items.size == 1) GONE else VISIBLE
}
val dataset = BarDataSet(listOf(
BarEntry(1f, points.others.toFloat()),
BarEntry(2f, points.student.toFloat())
), binding.root.context.getString(R.string.grade_statistics_legend))
val dataset = BarDataSet(
listOf(
BarEntry(1f, points.others.toFloat()),
BarEntry(2f, points.student.toFloat())
), binding.root.context.getString(R.string.grade_statistics_legend)
)
with(dataset) {
valueTextSize = 12f
@ -189,7 +260,8 @@ class GradeStatisticsAdapter @Inject constructor() :
form = Legend.LegendForm.SQUARE
},
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]
form = Legend.LegendForm.SQUARE
}
@ -226,4 +298,7 @@ class GradeStatisticsAdapter @Inject constructor() :
private class PointsViewHolder(val binding: ItemGradeStatisticsBarBinding) :
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 currentType
get() = when (binding.gradeStatisticsTypeSwitch.checkedRadioButtonId) {
R.id.gradeStatisticsTypeSemester -> ViewType.SEMESTER
R.id.gradeStatisticsTypePartial -> ViewType.PARTIAL
else -> ViewType.POINTS
}
override val currentType get() = statisticsAdapter.currentDataType
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentGradeStatisticsBinding.bind(view)
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() {
statisticsAdapter.onDataTypeChangeListener = presenter::onTypeChange
with(binding.gradeStatisticsRecycler) {
layoutManager = LinearLayoutManager(requireContext())
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)
with(binding.gradeStatisticsSubjects) {
@ -71,7 +72,9 @@ class GradeStatisticsFragment :
gradeStatisticsSwipe.setOnRefreshListener(presenter::onSwipeRefresh)
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() }
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) {
this.showAllSubjectsOnList = showAllSubjectsOnStatisticsList
this.theme = theme
this.items = items
showAllSubjectsOnList = showAllSubjectsOnStatisticsList
theme = newTheme
items = newItems
notifyDataSetChanged()
}
}
@ -103,11 +110,7 @@ class GradeStatisticsFragment :
}
override fun resetView() {
binding.gradeStatisticsScroll.scrollTo(0, 0)
}
override fun showContent(show: Boolean) {
binding.gradeStatisticsRecycler.visibility = if (show) View.VISIBLE else View.GONE
binding.gradeStatisticsRecycler.scrollToPosition(0)
}
override fun showEmpty(show: Boolean) {
@ -154,11 +157,6 @@ class GradeStatisticsFragment :
(parentFragment as? GradeFragment)?.onChildRefresh()
}
override fun onResume() {
super.onResume()
binding.gradeStatisticsTypeSwitch.setOnCheckedChangeListener { _, _ -> presenter.onTypeChange() }
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putSerializable(SAVED_CHART_TYPE, presenter.currentType)

View File

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

View File

@ -7,13 +7,17 @@ interface GradeStatisticsView : BaseView {
val isViewEmpty: Boolean
val currentType: ViewType
val currentType: GradeStatisticsItem.DataType
fun initView()
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)
@ -25,8 +29,6 @@ interface GradeStatisticsView : BaseView {
fun resetView()
fun showContent(show: Boolean)
fun showEmpty(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) {
SEMESTER(1),
PARTIAL(2),
POINTS(3)
POINTS(3),
HEADER(4)
}

View File

@ -3,8 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.modules.grade.statistics.GradeStatisticsFragment">
android:orientation="vertical">
<io.github.wulkanowy.ui.widgets.MaterialLinearLayout
android:id="@+id/gradeStatisticsSubjectsContainer"
@ -12,7 +11,8 @@
android:layout_height="wrap_content"
android:background="?android:windowBackground"
android:padding="5dp"
android:visibility="gone"
android:visibility="visible"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="UnusedAttribute"
tools:listitem="@layout/item_attendance_summary"
tools:visibility="visible">
@ -34,153 +34,101 @@
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/gradeStatisticsSwipe"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="match_parent">
<androidx.core.widget.NestedScrollView
android:id="@+id/gradeStatisticsScroll"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
android:layout_height="match_parent">
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/gradeStatisticsProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"
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
android:id="@+id/gradeStatisticsEmpty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables">
<HorizontalScrollView
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_main_grade"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
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
android:layout_width="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
android:id="@+id/gradeStatisticsProgress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"
tools:visibility="gone" />
<LinearLayout
android:id="@+id/gradeStatisticsEmpty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_main_grade"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="@string/grade_no_items"
android:textSize="20sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/gradeStatisticsError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/gradeStatisticsErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/gradeStatisticsErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/gradeStatisticsErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
android:layout_marginTop="20dp"
android:gravity="center"
android:text="@string/grade_no_items"
android:textSize="20sp" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
<LinearLayout
android:id="@+id/gradeStatisticsError"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="invisible"
tools:ignore="UseCompoundDrawables"
tools:visibility="invisible">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:srcCompat="@drawable/ic_error"
app:tint="?colorOnBackground"
tools:ignore="contentDescription" />
<TextView
android:id="@+id/gradeStatisticsErrorMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center"
android:padding="8dp"
android:text="@string/error_unknown"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center"
android:orientation="horizontal">
<com.google.android.material.button.MaterialButton
android:id="@+id/gradeStatisticsErrorDetails"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="@string/all_details" />
<com.google.android.material.button.MaterialButton
android:id="@+id/gradeStatisticsErrorRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/all_retry" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</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>