forked from github/szkolny
[UI] Add attendance summary page. Disable presence notifications.
This commit is contained in:
parent
97412a3736
commit
e068f1944f
@ -96,8 +96,8 @@ class EdudziennikWebAttendance(override val data: DataEdudziennik,
|
||||
profileId,
|
||||
Metadata.TYPE_ATTENDANCE,
|
||||
id,
|
||||
profile.empty,
|
||||
profile.empty
|
||||
profile.empty || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN,
|
||||
profile.empty || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -148,8 +148,8 @@ class IdziennikWebAttendance(override val data: DataIdziennik,
|
||||
profileId,
|
||||
Metadata.TYPE_ATTENDANCE,
|
||||
attendanceObject.id,
|
||||
profile?.empty ?: false,
|
||||
profile?.empty ?: false
|
||||
profile?.empty ?: false || baseType == TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN,
|
||||
profile?.empty ?: false || baseType == TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -77,8 +77,8 @@ class LibrusApiAttendances(override val data: DataLibrus,
|
||||
profileId,
|
||||
Metadata.TYPE_ATTENDANCE,
|
||||
id,
|
||||
profile?.empty ?: false,
|
||||
profile?.empty ?: false
|
||||
profile?.empty ?: false || type?.baseType == Attendance.TYPE_PRESENT_CUSTOM || type?.baseType == Attendance.TYPE_UNKNOWN,
|
||||
profile?.empty ?: false || type?.baseType == Attendance.TYPE_PRESENT_CUSTOM || type?.baseType == Attendance.TYPE_UNKNOWN
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -72,8 +72,8 @@ class MobidziennikApiAttendance(val data: DataMobidziennik, rows: List<String>)
|
||||
data.profileId,
|
||||
Metadata.TYPE_ATTENDANCE,
|
||||
id,
|
||||
data.profile?.empty ?: false,
|
||||
data.profile?.empty ?: false
|
||||
data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN,
|
||||
data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == Attendance.TYPE_UNKNOWN
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -175,8 +175,8 @@ class MobidziennikWebAttendance(override val data: DataMobidziennik,
|
||||
data.profileId,
|
||||
Metadata.TYPE_ATTENDANCE,
|
||||
id,
|
||||
data.profile?.empty ?: false,
|
||||
data.profile?.empty ?: false
|
||||
data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN,
|
||||
data.profile?.empty ?: false || baseType == Attendance.TYPE_PRESENT_CUSTOM || baseType == TYPE_UNKNOWN
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -73,8 +73,8 @@ class VulcanApiAttendance(override val data: DataVulcan,
|
||||
profileId,
|
||||
Metadata.TYPE_ATTENDANCE,
|
||||
attendanceObject.id,
|
||||
profile.empty,
|
||||
profile.empty
|
||||
profile.empty || type.baseType == Attendance.TYPE_PRESENT_CUSTOM || type.baseType == Attendance.TYPE_UNKNOWN,
|
||||
profile.empty || type.baseType == Attendance.TYPE_PRESENT_CUSTOM || type.baseType == Attendance.TYPE_UNKNOWN
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -68,4 +68,9 @@ open class Attendance(
|
||||
|
||||
@Ignore
|
||||
var showAsUnseen: Boolean? = null
|
||||
|
||||
@delegate:Ignore
|
||||
val typeObject by lazy {
|
||||
AttendanceType(profileId, baseType.toLong(), baseType, typeName, typeShort, typeSymbol, typeColor)
|
||||
}
|
||||
}
|
||||
|
@ -21,4 +21,35 @@ data class AttendanceType (
|
||||
val typeSymbol: String,
|
||||
/** A color that the e-register would display, null falls back to app's default */
|
||||
val typeColor: Int?
|
||||
)
|
||||
) : Comparable<AttendanceType> {
|
||||
|
||||
// attendance bar order:
|
||||
// day_free, present, present_custom, unknown, belated_excused, belated, released, absent_excused, absent,
|
||||
override fun compareTo(other: AttendanceType): Int {
|
||||
val type1 = when (baseType) {
|
||||
Attendance.TYPE_DAY_FREE -> 0
|
||||
Attendance.TYPE_PRESENT -> 1
|
||||
Attendance.TYPE_PRESENT_CUSTOM -> 2
|
||||
Attendance.TYPE_UNKNOWN -> 3
|
||||
Attendance.TYPE_BELATED_EXCUSED -> 4
|
||||
Attendance.TYPE_BELATED -> 5
|
||||
Attendance.TYPE_RELEASED -> 6
|
||||
Attendance.TYPE_ABSENT_EXCUSED -> 7
|
||||
Attendance.TYPE_ABSENT -> 8
|
||||
else -> 9
|
||||
}
|
||||
val type2 = when (other.baseType) {
|
||||
Attendance.TYPE_DAY_FREE -> 0
|
||||
Attendance.TYPE_PRESENT -> 1
|
||||
Attendance.TYPE_PRESENT_CUSTOM -> 2
|
||||
Attendance.TYPE_UNKNOWN -> 3
|
||||
Attendance.TYPE_BELATED_EXCUSED -> 4
|
||||
Attendance.TYPE_BELATED -> 5
|
||||
Attendance.TYPE_RELEASED -> 6
|
||||
Attendance.TYPE_ABSENT_EXCUSED -> 7
|
||||
Attendance.TYPE_ABSENT -> 8
|
||||
else -> 9
|
||||
}
|
||||
return type1 - type2
|
||||
}
|
||||
}
|
||||
|
@ -24,5 +24,6 @@ class AttendanceFull(
|
||||
|
||||
// metadata
|
||||
var seen = false
|
||||
get() = field || baseType == TYPE_PRESENT
|
||||
var notified = false
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ class AttendanceBar : View {
|
||||
|
||||
val textBounds = Rect()
|
||||
textPaint.getTextBounds(e.count.toString(), 0, e.count.toString().length, textBounds)
|
||||
if (width > textBounds.width() + 8.dp) {
|
||||
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)
|
||||
}
|
||||
|
@ -49,7 +49,6 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
|
||||
context ?: return null
|
||||
app = activity.application as App
|
||||
b = AttendanceListFragmentBinding.inflate(inflater)
|
||||
b.refreshLayout.setParent(activity.swipeRefreshLayout)
|
||||
return b.root
|
||||
}
|
||||
|
||||
@ -67,7 +66,7 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
|
||||
|
||||
// load & configure the adapter
|
||||
adapter.items = withContext(Dispatchers.Default) { processAttendance(items) }
|
||||
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
|
||||
if (adapter.items.isNotNullNorEmpty() && b.list.adapter == null) {
|
||||
b.list.adapter = adapter
|
||||
b.list.apply {
|
||||
setHasFixedSize(true)
|
||||
@ -76,6 +75,7 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
|
||||
}
|
||||
}
|
||||
adapter.notifyDataSetChanged()
|
||||
setSwipeToRefresh(adapter.items.isNullOrEmpty())
|
||||
|
||||
if (firstRun) {
|
||||
expandSubject(adapter)
|
||||
@ -84,7 +84,7 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
|
||||
|
||||
// show/hide relevant views
|
||||
b.progressBar.isVisible = false
|
||||
if (items.isNullOrEmpty()) {
|
||||
if (adapter.items.isNullOrEmpty()) {
|
||||
b.list.isVisible = false
|
||||
b.noData.isVisible = true
|
||||
} else {
|
||||
@ -141,6 +141,8 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
|
||||
items.sortByDescending { it.rangeStart }
|
||||
val iterator = items.listIterator()
|
||||
|
||||
if (!iterator.hasNext())
|
||||
return items.toMutableList()
|
||||
var element = iterator.next()
|
||||
while (iterator.hasNext()) {
|
||||
var nextElement = iterator.next()
|
||||
@ -170,14 +172,19 @@ class AttendanceListFragment : LazyFragment(), CoroutineScope {
|
||||
|
||||
items.forEach { month ->
|
||||
month.typeCountMap = month.items
|
||||
.groupBy { it.baseType }
|
||||
.groupBy { it.typeObject }
|
||||
.map { it.key to it.value.size }
|
||||
.sortedBy { it.first }
|
||||
.toMap()
|
||||
|
||||
val totalCount = month.typeCountMap.entries.sumBy { it.value }
|
||||
val totalCount = month.typeCountMap.entries.sumBy {
|
||||
when (it.key.baseType) {
|
||||
Attendance.TYPE_UNKNOWN -> 0
|
||||
else -> it.value
|
||||
}
|
||||
}
|
||||
val presenceCount = month.typeCountMap.entries.sumBy {
|
||||
when (it.key) {
|
||||
when (it.key.baseType) {
|
||||
Attendance.TYPE_PRESENT,
|
||||
Attendance.TYPE_PRESENT_CUSTOM,
|
||||
Attendance.TYPE_BELATED,
|
||||
|
@ -4,35 +4,43 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.ui.modules.attendance
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.Transformation
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kotlinx.coroutines.*
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.MainActivity
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
|
||||
import pl.szczodrzynski.edziennik.databinding.AttendanceListFragmentBinding
|
||||
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
|
||||
import pl.szczodrzynski.edziennik.startCoroutineTimer
|
||||
import pl.szczodrzynski.edziennik.databinding.AttendanceSummaryFragmentBinding
|
||||
import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceFragment.Companion.VIEW_SUMMARY
|
||||
import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceSubject
|
||||
import pl.szczodrzynski.edziennik.ui.modules.base.lazypager.LazyFragment
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import java.text.DecimalFormat
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
companion object {
|
||||
private const val TAG = "AttendanceSummaryFragment"
|
||||
private var periodSelection = 0
|
||||
}
|
||||
|
||||
private lateinit var app: App
|
||||
private lateinit var activity: MainActivity
|
||||
private lateinit var b: AttendanceListFragmentBinding
|
||||
private lateinit var b: AttendanceSummaryFragmentBinding
|
||||
|
||||
private val job: Job = Job()
|
||||
override val coroutineContext: CoroutineContext
|
||||
@ -41,13 +49,13 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
// local/private variables go here
|
||||
private val manager by lazy { app.attendanceManager }
|
||||
private var expandSubjectId = 0L
|
||||
private var attendance = listOf<AttendanceFull>()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
activity = (getActivity() as MainActivity?) ?: return null
|
||||
context ?: return null
|
||||
app = activity.application as App
|
||||
b = AttendanceListFragmentBinding.inflate(inflater)
|
||||
b.refreshLayout.setParent(activity.swipeRefreshLayout)
|
||||
b = AttendanceSummaryFragmentBinding.inflate(inflater)
|
||||
return b.root
|
||||
}
|
||||
|
||||
@ -63,16 +71,17 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
if (!isAdded) return@launch
|
||||
|
||||
// load & configure the adapter
|
||||
adapter.items = withContext(Dispatchers.Default) { processAttendance(items) }
|
||||
if (items.isNotNullNorEmpty() && b.list.adapter == null) {
|
||||
attendance = items
|
||||
adapter.items = withContext(Dispatchers.Default) { processAttendance() }
|
||||
if (adapter.items.isNotNullNorEmpty() && b.list.adapter == null) {
|
||||
b.list.adapter = adapter
|
||||
b.list.apply {
|
||||
setHasFixedSize(true)
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
addOnScrollListener(onScrollListener)
|
||||
}
|
||||
}
|
||||
adapter.notifyDataSetChanged()
|
||||
setSwipeToRefresh(adapter.items.isNullOrEmpty())
|
||||
|
||||
if (firstRun) {
|
||||
expandSubject(adapter)
|
||||
@ -81,10 +90,12 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
|
||||
// show/hide relevant views
|
||||
b.progressBar.isVisible = false
|
||||
if (items.isNullOrEmpty()) {
|
||||
if (adapter.items.isNullOrEmpty()) {
|
||||
b.statsLayout.isVisible = false
|
||||
b.list.isVisible = false
|
||||
b.noData.isVisible = true
|
||||
} else {
|
||||
b.statsLayout.isVisible = true
|
||||
b.list.isVisible = true
|
||||
b.noData.isVisible = false
|
||||
}
|
||||
@ -93,6 +104,36 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
adapter.onAttendanceClick = {
|
||||
//GradeDetailsDialog(activity, it)
|
||||
}
|
||||
|
||||
b.toggleGroup.check(when (periodSelection) {
|
||||
0 -> R.id.allYear
|
||||
1 -> R.id.semester1
|
||||
2 -> R.id.semester2
|
||||
else -> R.id.allYear
|
||||
})
|
||||
b.toggleGroup.addOnButtonCheckedListener { _, checkedId, isChecked ->
|
||||
if (!isChecked)
|
||||
return@addOnButtonCheckedListener
|
||||
periodSelection = when (checkedId) {
|
||||
R.id.allYear -> 0
|
||||
R.id.semester1 -> 1
|
||||
R.id.semester2 -> 2
|
||||
else -> 0
|
||||
}
|
||||
this@AttendanceSummaryFragment.launch {
|
||||
adapter.items = withContext(Dispatchers.Default) { processAttendance() }
|
||||
if (adapter.items.isNullOrEmpty()) {
|
||||
b.statsLayout.isVisible = false
|
||||
b.list.isVisible = false
|
||||
b.noData.isVisible = true
|
||||
} else {
|
||||
b.statsLayout.isVisible = true
|
||||
b.list.isVisible = true
|
||||
b.noData.isVisible = false
|
||||
}
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}; return true}
|
||||
|
||||
private fun expandSubject(adapter: AttendanceAdapter) {
|
||||
@ -116,7 +157,14 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
}
|
||||
|
||||
@Suppress("SuspendFunctionOnCoroutineScope")
|
||||
private fun processAttendance(attendance: List<AttendanceFull>): MutableList<Any> {
|
||||
private fun processAttendance(): MutableList<Any> {
|
||||
val attendance = when (periodSelection) {
|
||||
0 -> attendance
|
||||
1 -> attendance.filter { it.semester == 1 }
|
||||
2 -> attendance.filter { it.semester == 2 }
|
||||
else -> attendance
|
||||
}
|
||||
|
||||
if (attendance.isEmpty())
|
||||
return mutableListOf()
|
||||
|
||||
@ -129,16 +177,24 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
) }
|
||||
.sortedBy { it.subjectName.toLowerCase() }
|
||||
|
||||
var totalCountSum = 0
|
||||
var presenceCountSum = 0
|
||||
|
||||
items.forEach { subject ->
|
||||
subject.typeCountMap = subject.items
|
||||
.groupBy { it.baseType }
|
||||
.groupBy { it.typeObject }
|
||||
.map { it.key to it.value.size }
|
||||
.sortedBy { it.first }
|
||||
.toMap()
|
||||
|
||||
val totalCount = subject.typeCountMap.entries.sumBy { it.value }
|
||||
val totalCount = subject.typeCountMap.entries.sumBy {
|
||||
when (it.key.baseType) {
|
||||
Attendance.TYPE_UNKNOWN -> 0
|
||||
else -> it.value
|
||||
}
|
||||
}
|
||||
val presenceCount = subject.typeCountMap.entries.sumBy {
|
||||
when (it.key) {
|
||||
when (it.key.baseType) {
|
||||
Attendance.TYPE_PRESENT,
|
||||
Attendance.TYPE_PRESENT_CUSTOM,
|
||||
Attendance.TYPE_BELATED,
|
||||
@ -147,6 +203,8 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
totalCountSum += totalCount
|
||||
presenceCountSum += presenceCount
|
||||
|
||||
subject.percentage = if (totalCount == 0)
|
||||
0f
|
||||
@ -157,6 +215,91 @@ class AttendanceSummaryFragment : LazyFragment(), CoroutineScope {
|
||||
subject.items.removeAll { it.baseType == Attendance.TYPE_PRESENT }
|
||||
}
|
||||
|
||||
val typeCountMap = attendance
|
||||
.groupBy { it.typeObject }
|
||||
.map { it.key to it.value.size }
|
||||
.sortedBy { it.first }
|
||||
.toMap()
|
||||
|
||||
val percentage = if (totalCountSum == 0)
|
||||
0f
|
||||
else
|
||||
presenceCountSum.toFloat() / totalCountSum.toFloat() * 100f
|
||||
|
||||
launch {
|
||||
b.attendanceBar.setAttendanceData(typeCountMap.mapKeys { manager.getAttendanceColor(it.key) })
|
||||
b.attendanceBar.isInvisible = typeCountMap.isEmpty()
|
||||
|
||||
b.previewContainer.removeAllViews()
|
||||
val sum = typeCountMap.entries.sumBy { it.value }.toFloat()
|
||||
typeCountMap.forEach { (type, count) ->
|
||||
val layout = LinearLayout(activity)
|
||||
val attendanceObject = Attendance(
|
||||
profileId = 0,
|
||||
id = 0,
|
||||
baseType = type.baseType,
|
||||
typeName = "",
|
||||
typeShort = type.typeShort,
|
||||
typeSymbol = type.typeSymbol,
|
||||
typeColor = type.typeColor,
|
||||
date = Date(0, 0, 0),
|
||||
startTime = null,
|
||||
semester = 0,
|
||||
teacherId = 0,
|
||||
subjectId = 0,
|
||||
addedDate = 0
|
||||
)
|
||||
layout.addView(AttendanceView(activity, attendanceObject, manager))
|
||||
layout.addView(TextView(activity).also {
|
||||
it.setText(R.string.attendance_percentage_format, count/sum*100f)
|
||||
it.setPadding(0, 0, 5.dp, 0)
|
||||
})
|
||||
layout.setPadding(0, 8.dp, 0, 8.dp)
|
||||
b.previewContainer.addView(layout)
|
||||
}
|
||||
|
||||
if (percentage == 0f) {
|
||||
b.percentage.isInvisible = true
|
||||
b.percentageCircle.isInvisible = true
|
||||
}
|
||||
else {
|
||||
b.percentage.isVisible = true
|
||||
b.percentageCircle.isVisible = true
|
||||
b.percentage.setText(R.string.attendance_period_summary_format, percentage)
|
||||
|
||||
val df = DecimalFormat("0.##")
|
||||
b.percentageCircle.setProgressTextAdapter { value ->
|
||||
df.format(value) + "%"
|
||||
}
|
||||
b.percentageCircle.maxProgress = 100.0
|
||||
animatePercentageIndicator(percentage.toDouble())
|
||||
}
|
||||
}
|
||||
|
||||
return items.toMutableList()
|
||||
}
|
||||
|
||||
private fun animatePercentageIndicator(targetProgress: Double) {
|
||||
val startingProgress = b.percentageCircle.progress
|
||||
val progressChange = targetProgress - startingProgress
|
||||
|
||||
val a: Animation = object : Animation() {
|
||||
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
|
||||
val progress = startingProgress + (progressChange * interpolatedTime)
|
||||
//if (interpolatedTime == 1f)
|
||||
// progress = startingProgress + progressChange
|
||||
|
||||
val color = ColorUtils.blendARGB(Color.RED, Color.GREEN, progress.toFloat() / 100.0f)
|
||||
b.percentageCircle.progressColor = color
|
||||
b.percentageCircle.setProgress(progress, 100.0)
|
||||
}
|
||||
|
||||
override fun willChangeBounds(): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
a.duration = 1300
|
||||
a.interpolator = AccelerateDecelerateInterpolator()
|
||||
b.percentageCircle.startAnimation(a)
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.attendance.models
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel
|
||||
|
||||
@ -20,6 +21,6 @@ data class AttendanceMonth(
|
||||
var hasUnseen: Boolean = false
|
||||
get() = field || items.any { it.baseType != Attendance.TYPE_PRESENT && !it.seen }
|
||||
|
||||
var typeCountMap: Map<Int, Int> = mapOf()
|
||||
var typeCountMap: Map<AttendanceType, Int> = mapOf()
|
||||
var percentage: Float = 0f
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
package pl.szczodrzynski.edziennik.ui.modules.attendance.models
|
||||
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.models.ExpandableItemModel
|
||||
|
||||
@ -20,6 +21,6 @@ data class AttendanceSubject(
|
||||
var hasUnseen: Boolean = false
|
||||
get() = field || items.any { it.baseType != Attendance.TYPE_PRESENT && !it.seen }
|
||||
|
||||
var typeCountMap: Map<Int, Int> = mapOf()
|
||||
var typeCountMap: Map<AttendanceType, Int> = mapOf()
|
||||
var percentage: Float = 0f
|
||||
}
|
||||
|
@ -63,11 +63,11 @@ class MonthViewHolder(
|
||||
val attendance = Attendance(
|
||||
profileId = 0,
|
||||
id = 0,
|
||||
baseType = type,
|
||||
baseType = type.baseType,
|
||||
typeName = "",
|
||||
typeShort = manager.getTypeShort(type),
|
||||
typeSymbol = manager.getTypeShort(type),
|
||||
typeColor = manager.getAttendanceColor(type),
|
||||
typeShort = type.typeShort,
|
||||
typeSymbol = type.typeSymbol,
|
||||
typeColor = type.typeColor,
|
||||
date = Date(0, 0, 0),
|
||||
startTime = null,
|
||||
semester = 0,
|
||||
@ -80,7 +80,7 @@ class MonthViewHolder(
|
||||
it.setText(R.string.attendance_percentage_format, count/sum*100f)
|
||||
it.setPadding(0, 0, 5.dp, 0)
|
||||
})
|
||||
layout.setPadding(0, 8.dp, 0, 0)
|
||||
layout.setPadding(0, 8.dp, 0, 8.dp)
|
||||
b.previewContainer.addView(layout)
|
||||
}
|
||||
|
||||
|
@ -6,31 +6,24 @@ package pl.szczodrzynski.edziennik.ui.modules.attendance.viewholder
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.R
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
|
||||
import pl.szczodrzynski.edziennik.databinding.AttendanceItemContainerBarBinding
|
||||
import pl.szczodrzynski.edziennik.dp
|
||||
import pl.szczodrzynski.edziennik.databinding.AttendanceItemContainerSubjectBinding
|
||||
import pl.szczodrzynski.edziennik.setText
|
||||
import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter
|
||||
import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceAdapter.Companion.STATE_CLOSED
|
||||
import pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceView
|
||||
import pl.szczodrzynski.edziennik.ui.modules.attendance.models.AttendanceSubject
|
||||
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
|
||||
import pl.szczodrzynski.edziennik.utils.Themes
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
|
||||
class SubjectViewHolder(
|
||||
inflater: LayoutInflater,
|
||||
parent: ViewGroup,
|
||||
val b: AttendanceItemContainerBarBinding = AttendanceItemContainerBarBinding.inflate(inflater, parent, false)
|
||||
val b: AttendanceItemContainerSubjectBinding = AttendanceItemContainerSubjectBinding.inflate(inflater, parent, false)
|
||||
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<AttendanceSubject, AttendanceAdapter> {
|
||||
companion object {
|
||||
private const val TAG = "SubjectViewHolder"
|
||||
@ -51,48 +44,14 @@ class SubjectViewHolder(
|
||||
|
||||
b.attendanceBar.setAttendanceData(item.typeCountMap.mapKeys { manager.getAttendanceColor(it.key) })
|
||||
|
||||
b.previewContainer.isInvisible = item.state != STATE_CLOSED
|
||||
b.summaryContainer.isInvisible = item.state == STATE_CLOSED
|
||||
b.percentage.isVisible = item.state == STATE_CLOSED
|
||||
|
||||
b.previewContainer.removeAllViews()
|
||||
|
||||
val sum = item.typeCountMap.entries.sumBy { it.value }.toFloat()
|
||||
item.typeCountMap.forEach { (type, count) ->
|
||||
val layout = LinearLayout(contextWrapper)
|
||||
val attendance = Attendance(
|
||||
profileId = 0,
|
||||
id = 0,
|
||||
baseType = type,
|
||||
typeName = "",
|
||||
typeShort = manager.getTypeShort(type),
|
||||
typeSymbol = manager.getTypeShort(type),
|
||||
typeColor = manager.getAttendanceColor(type),
|
||||
date = Date(0, 0, 0),
|
||||
startTime = null,
|
||||
semester = 0,
|
||||
teacherId = 0,
|
||||
subjectId = 0,
|
||||
addedDate = 0
|
||||
)
|
||||
layout.addView(AttendanceView(contextWrapper, attendance, manager))
|
||||
layout.addView(TextView(contextWrapper).also {
|
||||
it.setText(R.string.attendance_percentage_format, count/sum*100f)
|
||||
it.setPadding(0, 0, 5.dp, 0)
|
||||
})
|
||||
layout.setPadding(0, 8.dp, 0, 0)
|
||||
b.previewContainer.addView(layout)
|
||||
}
|
||||
b.percentage.isVisible = true
|
||||
|
||||
if (item.percentage == 0f) {
|
||||
b.percentage.isVisible = false
|
||||
b.percentage.text = null
|
||||
b.summaryContainer.isVisible = false
|
||||
b.summaryContainer.text = null
|
||||
}
|
||||
else {
|
||||
b.percentage.setText(R.string.attendance_percentage_format, item.percentage)
|
||||
b.summaryContainer.setText(R.string.attendance_period_summary_format, item.percentage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import pl.szczodrzynski.edziennik.App
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.Attendance
|
||||
import pl.szczodrzynski.edziennik.data.db.entity.AttendanceType
|
||||
import pl.szczodrzynski.edziennik.data.db.full.AttendanceFull
|
||||
import pl.szczodrzynski.edziennik.startCoroutineTimer
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
@ -49,6 +50,12 @@ class AttendanceManager(val app: App) : CoroutineScope {
|
||||
else -> 0xff64b5f6.toInt()
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
fun getAttendanceColor(attendance: Attendance): Int {
|
||||
return (if (useSymbols) attendance.typeColor else null) ?: when (attendance.baseType) {
|
||||
Attendance.TYPE_PRESENT_CUSTOM -> attendance.typeColor ?: 0xff64b5f6.toInt()
|
||||
|
@ -52,8 +52,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="32dp"
|
||||
android:text="@string/attendance_config_show_presence_in_month"
|
||||
android:visibility="gone" />
|
||||
android:text="@string/attendance_config_show_presence_in_month" />
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</layout>
|
||||
|
@ -0,0 +1,80 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) Kuba Szczodrzyński 2020-5-5.
|
||||
-->
|
||||
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="?selectableItemBackground">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@drawable/divider"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif"
|
||||
android:maxLines="2"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="20sp"
|
||||
tools:text="historia i społeczeństwo" />
|
||||
|
||||
<View
|
||||
android:id="@+id/unread"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:visibility="gone"
|
||||
android:background="@drawable/unread_red_circle"
|
||||
tools:visibility="visible"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/percentage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
tools:text="6,5%" />
|
||||
|
||||
<com.mikepenz.iconics.view.IconicsImageView
|
||||
android:id="@+id/dropdownIcon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:scaleType="centerInside"
|
||||
app:iiv_color="?android:textColorSecondary"
|
||||
app:iiv_icon="cmd-chevron-down"
|
||||
app:iiv_size="18dp"
|
||||
tools:src="@android:drawable/ic_menu_more" />
|
||||
</LinearLayout>
|
||||
|
||||
<pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceBar
|
||||
android:id="@+id/attendanceBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:layout_marginBottom="8dp" />
|
||||
</LinearLayout>
|
||||
</layout>
|
@ -7,41 +7,35 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator
|
||||
android:id="@+id/refreshLayout"
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/noData"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:drawablePadding="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/attendances_no_data"
|
||||
android:textSize="24sp"
|
||||
android:visibility="gone"
|
||||
app:drawableTopCompat="@drawable/ic_no_grades"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/noData"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:drawablePadding="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/attendances_no_data"
|
||||
android:textSize="24sp"
|
||||
android:visibility="gone"
|
||||
app:drawableTopCompat="@drawable/ic_no_grades"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
tools:listitem="@layout/attendance_item_attendance"
|
||||
tools:visibility="visible" />
|
||||
</FrameLayout>
|
||||
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
tools:listitem="@layout/attendance_item_attendance"
|
||||
tools:visibility="visible" />
|
||||
</FrameLayout>
|
||||
</layout>
|
||||
|
@ -7,41 +7,165 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator
|
||||
android:id="@+id/refreshLayout"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<FrameLayout
|
||||
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||
android:id="@+id/toggleGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
app:singleSelection="true"
|
||||
app:selectionRequired="true"
|
||||
android:gravity="center_horizontal">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/semester1"
|
||||
style="?materialButtonOutlinedStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
android:text="Semestr 1" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/noData"
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/semester2"
|
||||
style="?materialButtonOutlinedStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:drawablePadding="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/attendances_no_data"
|
||||
android:textSize="24sp"
|
||||
android:visibility="gone"
|
||||
app:drawableTopCompat="@drawable/ic_no_grades"
|
||||
tools:visibility="visible" />
|
||||
android:text="Semestr 2" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/allYear"
|
||||
style="?materialButtonOutlinedStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Cały rok" />
|
||||
</com.google.android.material.button.MaterialButtonToggleGroup>
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone"
|
||||
tools:listitem="@layout/attendance_item_attendance"
|
||||
tools:visibility="visible" />
|
||||
</FrameLayout>
|
||||
</pl.szczodrzynski.edziennik.utils.SwipeRefreshLayoutNoIndicator>
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/statsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/percentage"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:textAppearance="@style/NavView.TextView.Subtitle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Obecność w tym okresie: 99,7%" />
|
||||
|
||||
<antonkozyriatskyi.circularprogressindicator.CircularProgressIndicator
|
||||
android:id="@+id/percentageCircle"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:direction="clockwise"
|
||||
app:drawDot="false"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:progressBackgroundStrokeWidth="9dp"
|
||||
app:progressStrokeWidth="10dp"
|
||||
app:progressCap="butt"
|
||||
app:textSize="0sp"
|
||||
android:visibility="invisible"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceBar
|
||||
android:id="@+id/attendanceBar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/percentageCircle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/percentage"
|
||||
android:visibility="invisible"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.flexbox.FlexboxLayout
|
||||
android:id="@+id/previewContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:orientation="horizontal"
|
||||
app:flexWrap="wrap"
|
||||
app:layout_constraintEnd_toStartOf="@+id/percentageCircle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/attendanceBar">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<pl.szczodrzynski.edziennik.ui.modules.attendance.AttendanceView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:background="@drawable/bg_rounded_4dp"
|
||||
tools:backgroundTint="#43a047"
|
||||
tools:layout_marginEnd="5dp"
|
||||
tools:layout_marginRight="5dp"
|
||||
tools:paddingHorizontal="5dp"
|
||||
tools:singleLine="true"
|
||||
tools:text="w"
|
||||
tools:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:layout_marginEnd="5dp"
|
||||
tools:layout_marginRight="5dp"
|
||||
tools:text="6,8%" />
|
||||
</LinearLayout>
|
||||
</com.google.android.flexbox.FlexboxLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginVertical="64dp"
|
||||
tools:visibility="gone" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/noData"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:drawablePadding="16dp"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:text="@string/attendances_no_data"
|
||||
android:textSize="24sp"
|
||||
android:visibility="gone"
|
||||
app:drawableTopCompat="@drawable/ic_no_grades"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
tools:listitem="@layout/attendance_item_attendance"
|
||||
tools:visibility="visible" />
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
||||
</layout>
|
||||
|
Loading…
x
Reference in New Issue
Block a user