[UI/Attendance] Fix counting issues. Add attendance details dialog.

This commit is contained in:
Kuba Szczodrzyński
2020-05-09 19:18:18 +02:00
parent 65e0e10db6
commit 771712da99
14 changed files with 381 additions and 63 deletions

View File

@ -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,

View File

@ -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 }
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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)
}}
}

View File

@ -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())

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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,

View File

@ -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)
}
/* _ _ _____ _____ _ __ _