[UI] Add attendance summary page. Disable presence notifications.

This commit is contained in:
Kuba Szczodrzyński 2020-05-05 22:57:24 +02:00
parent 97412a3736
commit e068f1944f
21 changed files with 500 additions and 148 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,5 +24,6 @@ class AttendanceFull(
// metadata
var seen = false
get() = field || baseType == TYPE_PRESENT
var notified = false
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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