From 771712da992a971e061abae27b540c82324c99be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Sat, 9 May 2020 19:18:18 +0200 Subject: [PATCH] [UI/Attendance] Fix counting issues. Add attendance details dialog. --- .../data/web/MobidziennikWebAttendance.kt | 16 +- .../edziennik/data/db/entity/Attendance.kt | 2 +- .../data/db/entity/AttendanceType.kt | 4 + .../ui/modules/attendance/AttendanceBar.kt | 12 +- .../attendance/AttendanceDetailsDialog.kt | 64 +++++ .../attendance/AttendanceListFragment.kt | 14 +- .../attendance/AttendanceSummaryFragment.kt | 24 +- .../attendance/viewholder/MonthViewHolder.kt | 5 +- .../viewholder/SubjectViewHolder.kt | 2 +- .../attendance/viewholder/TypeViewHolder.kt | 9 +- .../utils/managers/AttendanceManager.kt | 16 +- .../res/layout/attendance_details_dialog.xml | 235 ++++++++++++++++++ .../main/res/layout/attendance_item_type.xml | 31 +-- app/src/main/res/values/strings.xml | 10 + 14 files changed, 381 insertions(+), 63 deletions(-) create mode 100644 app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceDetailsDialog.kt create mode 100644 app/src/main/res/layout/attendance_details_dialog.xml diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebAttendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebAttendance.kt index 498f6fbf..36dbe1e9 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebAttendance.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/api/edziennik/mobidziennik/data/web/MobidziennikWebAttendance.kt @@ -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, diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.kt index 700b9682..b2ebb5a0 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Attendance.kt @@ -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 } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/AttendanceType.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/AttendanceType.kt index 904c06c6..c5fccc4b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/AttendanceType.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/AttendanceType.kt @@ -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 { + @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 { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceBar.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceBar.kt index bfe49a41..70be9256 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceBar.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceBar.kt @@ -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) { - attendancesList = list.map { AttendanceItem(it.key, it.value) } + fun setAttendanceData(list: List>) { + 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 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceDetailsDialog.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceDetailsDialog.kt new file mode 100644 index 00000000..ce49efbd --- /dev/null +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceDetailsDialog.kt @@ -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) + }} +} diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceListFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceListFragment.kt index d317f4e9..8e68cdbb 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceListFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceListFragment.kt @@ -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()) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceSummaryFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceSummaryFragment.kt index 9e610cda..5f61020b 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceSummaryFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/AttendanceSummaryFragment.kt @@ -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) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/MonthViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/MonthViewHolder.kt index 08d2acec..71c139ac 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/MonthViewHolder.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/MonthViewHolder.kt @@ -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) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/SubjectViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/SubjectViewHolder.kt index 9a8e56a0..e439111d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/SubjectViewHolder.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/SubjectViewHolder.kt @@ -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 diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/TypeViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/TypeViewHolder.kt index 62465e8b..a2f8b689 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/TypeViewHolder.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/attendance/viewholder/TypeViewHolder.kt @@ -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, diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/AttendanceManager.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/AttendanceManager.kt index 922831fd..cd726797 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/AttendanceManager.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/AttendanceManager.kt @@ -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) } /* _ _ _____ _____ _ __ _ diff --git a/app/src/main/res/layout/attendance_details_dialog.xml b/app/src/main/res/layout/attendance_details_dialog.xml new file mode 100644 index 00000000..59ec37c3 --- /dev/null +++ b/app/src/main/res/layout/attendance_details_dialog.xml @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/attendance_item_type.xml b/app/src/main/res/layout/attendance_item_type.xml index e524fa18..2ec5cd4e 100644 --- a/app/src/main/res/layout/attendance_item_type.xml +++ b/app/src/main/res/layout/attendance_item_type.xml @@ -27,7 +27,7 @@ android:orientation="horizontal"> @@ -81,26 +81,17 @@ tools:background="@android:drawable/ic_menu_more" /> - - - - + android:layout_gravity="center_vertical" + android:layout_marginHorizontal="8dp" + android:layout_marginBottom="8dp" + android:textAppearance="@style/NavView.TextView.Helper" + android:textSize="14sp" + tools:text="57,67% • 521 przez cały rok • 134 w tym semestrze" + tools:text1="Cały rok: 3 oceny • suma: 320 pkt" + tools:text2="Cały rok: 15 ocen • średnia: 2,62" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index bc603679..e2000163 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1305,4 +1305,14 @@ Jesteś offline. Spróbuj włączyć Wi-Fi lub dane komórkowe. Połączenie sieciowe Wg typu + %d przez cały rok + %d w tym semestrze + Nauczyciel + Rodzaj + Data + Godzina + Temat lekcji + Liczona do puli? + ID frekwencji + ID rodzaju podstawowego