mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-06-13 14:10:46 +02:00
[UI/Attendance] Fix counting issues. Add attendance details dialog.
This commit is contained in:
@ -151,11 +151,17 @@ class MobidziennikWebAttendance(override val data: DataMobidziennik,
|
||||
}
|
||||
}
|
||||
val typeName = types?.get(typeSymbol) ?: ""
|
||||
val typeColor = when (typeSymbol) {
|
||||
"e" -> 0xff673ab7
|
||||
"en" -> 0xffec407a
|
||||
"ep" -> 0xff4caf50
|
||||
else -> null
|
||||
}?.toInt()
|
||||
|
||||
val typeShort = when (baseType) {
|
||||
TYPE_UNKNOWN -> typeSymbol
|
||||
else -> data.app.attendanceManager.getTypeShort(baseType)
|
||||
}
|
||||
val typeShort = if (isCounted)
|
||||
data.app.attendanceManager.getTypeShort(baseType)
|
||||
else
|
||||
typeSymbol
|
||||
|
||||
val semester = data.profile?.dateToSemester(lessonDate) ?: 1
|
||||
|
||||
@ -168,7 +174,7 @@ class MobidziennikWebAttendance(override val data: DataMobidziennik,
|
||||
typeName = typeName,
|
||||
typeShort = typeShort,
|
||||
typeSymbol = typeSymbol,
|
||||
typeColor = null,
|
||||
typeColor = typeColor,
|
||||
date = lessonDate,
|
||||
startTime = startTime,
|
||||
semester = semester,
|
||||
|
@ -73,6 +73,6 @@ open class Attendance(
|
||||
|
||||
@delegate:Ignore
|
||||
val typeObject by lazy {
|
||||
AttendanceType(profileId, baseType.toLong(), baseType, typeName, typeShort, typeSymbol, typeColor)
|
||||
AttendanceType(profileId, baseType.toLong(), baseType, typeName, typeShort, typeSymbol, typeColor).also { it.isCounted = isCounted }
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
package pl.szczodrzynski.edziennik.data.db.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
|
||||
@Entity(tableName = "attendanceTypes",
|
||||
primaryKeys = ["profileId", "id"])
|
||||
@ -23,6 +24,9 @@ data class AttendanceType (
|
||||
val typeColor: Int?
|
||||
) : Comparable<AttendanceType> {
|
||||
|
||||
@Ignore
|
||||
var isCounted: Boolean = true
|
||||
|
||||
// attendance bar order:
|
||||
// day_free, present, present_custom, unknown, belated_excused, belated, released, absent_excused, absent,
|
||||
override fun compareTo(other: AttendanceType): Int {
|
||||
|
@ -12,6 +12,7 @@ import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import pl.szczodrzynski.edziennik.dp
|
||||
import pl.szczodrzynski.edziennik.utils.Colors
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/* https://github.com/JakubekWeg/Mobishit/blob/master/app/src/main/java/jakubweg/mobishit/view/AttendanceBarView.kt */
|
||||
class AttendanceBar : View {
|
||||
@ -34,7 +35,7 @@ class AttendanceBar : View {
|
||||
mCornerRadius = 4.dp.toFloat()
|
||||
|
||||
if (isInEditMode)
|
||||
setAttendanceData(mapOf(
|
||||
setAttendanceData(listOf(
|
||||
0xff43a047.toInt() to 23,
|
||||
0xff009688.toInt() to 187,
|
||||
0xff3f51b5.toInt() to 46,
|
||||
@ -49,8 +50,8 @@ class AttendanceBar : View {
|
||||
// color, count
|
||||
private class AttendanceItem(var color: Int, var count: Int)
|
||||
|
||||
fun setAttendanceData(list: Map<Int, Int>) {
|
||||
attendancesList = list.map { AttendanceItem(it.key, it.value) }
|
||||
fun setAttendanceData(list: List<Pair<Int, Int>>) {
|
||||
attendancesList = list.map { AttendanceItem(it.first, it.second) }
|
||||
setWillNotDraw(false)
|
||||
invalidate()
|
||||
}
|
||||
@ -91,11 +92,12 @@ class AttendanceBar : View {
|
||||
mainPaint.color = e.color
|
||||
canvas.drawRect(left, top, left + width, bottom, mainPaint)
|
||||
|
||||
val percentage = (100f * e.count / sum).roundToInt().toString() + "%"
|
||||
val textBounds = Rect()
|
||||
textPaint.getTextBounds(e.count.toString(), 0, e.count.toString().length, textBounds)
|
||||
textPaint.getTextBounds(percentage, 0, percentage.length, textBounds)
|
||||
if (width > textBounds.width() + 8.dp && height > textBounds.height() + 2.dp) {
|
||||
textPaint.color = Colors.legibleTextColor(e.color)
|
||||
canvas.drawText(e.count.toString(), left + width / 2, bottom - height / 2 + textBounds.height()/2, textPaint)
|
||||
canvas.drawText(percentage, left + width / 2, bottom - height / 2 + textBounds.height()/2, textPaint)
|
||||
}
|
||||
|
||||
left += width
|
||||
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) Kuba Szczodrzyński 2020-5-9.
|
||||
*/
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.attendance
|
||||
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
|
||||
import pl.szczodrzynski.edziennik.databinding.AttendanceDetailsDialogBinding
|
||||
import pl.szczodrzynski.edziennik.setTintColor
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class AttendanceDetailsDialog(
|
||||
val activity: AppCompatActivity,
|
||||
val attendance: AttendanceFull,
|
||||
val onShowListener: ((tag: String) -> Unit)? = null,
|
||||
val onDismissListener: ((tag: String) -> Unit)? = null
|
||||
) : CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "AttendanceDetailsDialog"
|
||||
}
|
||||
|
||||
private lateinit var app: App
|
||||
private lateinit var b: AttendanceDetailsDialogBinding
|
||||
private lateinit var dialog: AlertDialog
|
||||
|
||||
private val job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
get() = job + Dispatchers.Main
|
||||
|
||||
// local variables go here
|
||||
|
||||
init { run {
|
||||
if (activity.isFinishing)
|
||||
return@run
|
||||
onShowListener?.invoke(TAG)
|
||||
app = activity.applicationContext as App
|
||||
b = AttendanceDetailsDialogBinding.inflate(activity.layoutInflater)
|
||||
dialog = MaterialAlertDialogBuilder(activity)
|
||||
.setView(b.root)
|
||||
.setPositiveButton(R.string.close, null)
|
||||
.setOnDismissListener {
|
||||
onDismissListener?.invoke(TAG)
|
||||
}
|
||||
.show()
|
||||
val manager = app.attendanceManager
|
||||
|
||||
val attendanceColor = manager.getAttendanceColor(attendance)
|
||||
b.attendance = attendance
|
||||
b.devMode = App.debugMode
|
||||
b.attendanceName.setTextColor(if (ColorUtils.calculateLuminance(attendanceColor) > 0.3) 0xaa000000.toInt() else 0xccffffff.toInt())
|
||||
b.attendanceName.background.setTintColor(attendanceColor)
|
||||
|
||||
b.attendanceIsCounted.setText(if (attendance.isCounted) R.string.yes else R.string.no)
|
||||
}}
|
||||
}
|
@ -95,7 +95,7 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
|
||||
}})
|
||||
|
||||
adapter.onAttendanceClick = {
|
||||
//GradeDetailsDialog(activity, it)
|
||||
AttendanceDetailsDialog(activity, it)
|
||||
}
|
||||
}; return true}
|
||||
|
||||
@ -174,15 +174,14 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
|
||||
items.forEach { month ->
|
||||
month.typeCountMap = month.items
|
||||
.groupBy { it.typeObject }
|
||||
.map { it.key to it.value.count { a -> a.isCounted } }
|
||||
.map { it.key to it.value.size }
|
||||
.sortedBy { it.first }
|
||||
.toMap()
|
||||
|
||||
val totalCount = month.typeCountMap.entries.sumBy {
|
||||
when (it.key.baseType) {
|
||||
Attendance.TYPE_UNKNOWN -> 0
|
||||
else -> it.value
|
||||
}
|
||||
if (!it.key.isCounted || it.key.baseType == Attendance.TYPE_UNKNOWN)
|
||||
0
|
||||
else it.value
|
||||
}
|
||||
val presenceCount = month.typeCountMap.entries.sumBy {
|
||||
when (it.key.baseType) {
|
||||
@ -190,7 +189,7 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
|
||||
Attendance.TYPE_PRESENT_CUSTOM,
|
||||
Attendance.TYPE_BELATED,
|
||||
Attendance.TYPE_BELATED_EXCUSED,
|
||||
Attendance.TYPE_RELEASED -> it.value
|
||||
Attendance.TYPE_RELEASED -> if (it.key.isCounted) it.value else 0
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
@ -213,6 +212,7 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
|
||||
type = it.key,
|
||||
items = it.value.toMutableList()
|
||||
) }
|
||||
.sortedBy { it.items.size }
|
||||
|
||||
items.forEach { type ->
|
||||
type.percentage = if (attendance.isEmpty())
|
||||
|
@ -76,7 +76,7 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
if (adapter.items.isNotNullNorEmpty() && b.list.adapter == null) {
|
||||
b.list.adapter = adapter
|
||||
b.list.apply {
|
||||
setHasFixedSize(true)
|
||||
setHasFixedSize(false)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
isNestedScrollingEnabled = false
|
||||
}
|
||||
@ -103,7 +103,7 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
}})
|
||||
|
||||
adapter.onAttendanceClick = {
|
||||
//GradeDetailsDialog(activity, it)
|
||||
AttendanceDetailsDialog(activity, it)
|
||||
}
|
||||
|
||||
b.toggleGroup.check(when (periodSelection) {
|
||||
@ -184,15 +184,14 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
items.forEach { subject ->
|
||||
subject.typeCountMap = subject.items
|
||||
.groupBy { it.typeObject }
|
||||
.map { it.key to it.value.count { a -> a.isCounted } }
|
||||
.map { it.key to it.value.size }
|
||||
.sortedBy { it.first }
|
||||
.toMap()
|
||||
|
||||
val totalCount = subject.typeCountMap.entries.sumBy {
|
||||
when (it.key.baseType) {
|
||||
Attendance.TYPE_UNKNOWN -> 0
|
||||
else -> it.value
|
||||
}
|
||||
if (!it.key.isCounted || it.key.baseType == Attendance.TYPE_UNKNOWN)
|
||||
0
|
||||
else it.value
|
||||
}
|
||||
val presenceCount = subject.typeCountMap.entries.sumBy {
|
||||
when (it.key.baseType) {
|
||||
@ -200,7 +199,7 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
Attendance.TYPE_PRESENT_CUSTOM,
|
||||
Attendance.TYPE_BELATED,
|
||||
Attendance.TYPE_BELATED_EXCUSED,
|
||||
Attendance.TYPE_RELEASED -> it.value
|
||||
Attendance.TYPE_RELEASED -> if (it.key.isCounted) it.value else 0
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
@ -218,7 +217,7 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
|
||||
val typeCountMap = attendance
|
||||
.groupBy { it.typeObject }
|
||||
.map { it.key to it.value.count { a -> a.isCounted } }
|
||||
.map { it.key to it.value.size }
|
||||
.sortedBy { it.first }
|
||||
.toMap()
|
||||
|
||||
@ -228,11 +227,11 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
presenceCountSum.toFloat() / totalCountSum.toFloat() * 100f
|
||||
|
||||
launch {
|
||||
b.attendanceBar.setAttendanceData(typeCountMap.mapKeys { manager.getAttendanceColor(it.key) })
|
||||
b.attendanceBar.setAttendanceData(typeCountMap.map { manager.getAttendanceColor(it.key) to it.value })
|
||||
b.attendanceBar.isInvisible = typeCountMap.isEmpty()
|
||||
|
||||
b.previewContainer.removeAllViews()
|
||||
val sum = typeCountMap.entries.sumBy { it.value }.toFloat()
|
||||
//val sum = typeCountMap.entries.sumBy { it.value }.toFloat()
|
||||
typeCountMap.forEach { (type, count) ->
|
||||
val layout = LinearLayout(activity)
|
||||
val attendanceObject = Attendance(
|
||||
@ -252,7 +251,8 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
)
|
||||
layout.addView(AttendanceView(activity, attendanceObject, manager))
|
||||
layout.addView(TextView(activity).also {
|
||||
it.setText(R.string.attendance_percentage_format, count/sum*100f)
|
||||
//it.setText(R.string.attendance_percentage_format, count/sum*100f)
|
||||
it.text = count.toString()
|
||||
it.setPadding(0, 0, 5.dp, 0)
|
||||
})
|
||||
layout.setPadding(0, 8.dp, 0, 8.dp)
|
||||
|
@ -49,7 +49,7 @@ class MonthViewHolder(
|
||||
|
||||
b.unread.isVisible = item.hasUnseen
|
||||
|
||||
b.attendanceBar.setAttendanceData(item.typeCountMap.mapKeys { manager.getAttendanceColor(it.key) })
|
||||
b.attendanceBar.setAttendanceData(item.typeCountMap.map { manager.getAttendanceColor(it.key) to it.value })
|
||||
|
||||
b.previewContainer.isInvisible = item.state != STATE_CLOSED
|
||||
b.summaryContainer.isInvisible = item.state == STATE_CLOSED
|
||||
@ -77,7 +77,8 @@ class MonthViewHolder(
|
||||
)
|
||||
layout.addView(AttendanceView(contextWrapper, attendance, manager))
|
||||
layout.addView(TextView(contextWrapper).also {
|
||||
it.setText(R.string.attendance_percentage_format, count/sum*100f)
|
||||
//it.setText(R.string.attendance_percentage_format, count/sum*100f)
|
||||
it.text = count.toString()
|
||||
it.setPadding(0, 0, 5.dp, 0)
|
||||
})
|
||||
layout.setPadding(0, 8.dp, 0, 0)
|
||||
|
@ -42,7 +42,7 @@ class SubjectViewHolder(
|
||||
|
||||
b.unread.isVisible = item.hasUnseen
|
||||
|
||||
b.attendanceBar.setAttendanceData(item.typeCountMap.mapKeys { manager.getAttendanceColor(it.key) })
|
||||
b.attendanceBar.setAttendanceData(item.typeCountMap.map { manager.getAttendanceColor(it.key) to it.value })
|
||||
|
||||
b.percentage.isVisible = true
|
||||
|
||||
|
@ -5,13 +5,14 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.concat
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
|
||||
import pl.szczodrzynski.edziennik.databinding.AttendanceItemTypeBinding
|
||||
import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter
|
||||
@ -43,7 +44,11 @@ class TypeViewHolder(
|
||||
|
||||
b.unread.isVisible = item.hasUnseen
|
||||
|
||||
b.previewContainer.visibility = if (item.state == AttendanceAdapter.STATE_CLOSED) View.VISIBLE else View.INVISIBLE
|
||||
b.details.text = listOf(
|
||||
app.getString(R.string.attendance_percentage_format, item.percentage),
|
||||
app.getString(R.string.attendance_type_yearly_format, item.items.size),
|
||||
app.getString(R.string.attendance_type_semester_format, item.semesterCount)
|
||||
).concat(" • ")
|
||||
|
||||
b.type.setAttendance(Attendance(
|
||||
profileId = 0,
|
||||
|
@ -51,16 +51,16 @@ class AttendanceManager(val app: App) : CoroutineScope {
|
||||
}
|
||||
}
|
||||
fun getAttendanceColor(typeObject: AttendanceType): Int {
|
||||
return (if (useSymbols) typeObject.typeColor else null) ?: when (typeObject.baseType) {
|
||||
Attendance.TYPE_PRESENT_CUSTOM -> typeObject.typeColor ?: 0xff64b5f6.toInt()
|
||||
else -> getAttendanceColor(typeObject.baseType)
|
||||
}
|
||||
return (if (useSymbols) typeObject.typeColor else null)
|
||||
?: if (typeObject.baseType == Attendance.TYPE_PRESENT_CUSTOM || !typeObject.isCounted)
|
||||
typeObject.typeColor ?: 0xff64b5f6.toInt()
|
||||
else getAttendanceColor(typeObject.baseType)
|
||||
}
|
||||
fun getAttendanceColor(attendance: Attendance): Int {
|
||||
return (if (useSymbols) attendance.typeColor else null) ?: when (attendance.baseType) {
|
||||
Attendance.TYPE_PRESENT_CUSTOM -> attendance.typeColor ?: 0xff64b5f6.toInt()
|
||||
else -> getAttendanceColor(attendance.baseType)
|
||||
}
|
||||
return (if (useSymbols) attendance.typeColor else null)
|
||||
?: if (attendance.baseType == Attendance.TYPE_PRESENT_CUSTOM || !attendance.isCounted)
|
||||
attendance.typeColor ?: 0xff64b5f6.toInt()
|
||||
else getAttendanceColor(attendance.baseType)
|
||||
}
|
||||
|
||||
/* _ _ _____ _____ _ __ _
|
||||
|
Reference in New Issue
Block a user