From 42ef40439e9a587b30b2ce6621571ac0682b2ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Szczodrzy=C5=84ski?= Date: Mon, 9 Mar 2020 20:18:11 +0100 Subject: [PATCH] [Grades] Implement showing unseen badges and marking as seen. Change default "hide improved" config value. --- .../edziennik/config/ProfileConfigGrades.kt | 2 +- .../edziennik/data/db/entity/Grade.kt | 7 ++ .../ui/modules/grades/GradesAdapter.kt | 103 ++++++++++++------ .../ui/modules/grades/GradesFragment.kt | 37 +++++-- .../modules/grades/models/GradesSemester.kt | 7 +- .../ui/modules/grades/models/GradesSubject.kt | 3 + .../grades/viewholder/BindableViewHolder.kt | 3 +- .../grades/viewholder/EmptyViewHolder.kt | 3 +- .../grades/viewholder/GradeViewHolder.kt | 37 +++++-- .../grades/viewholder/SemesterViewHolder.kt | 31 +++++- .../grades/viewholder/StatsViewHolder.kt | 5 +- .../grades/viewholder/SubjectViewHolder.kt | 9 +- .../edziennik/utils/managers/GradesManager.kt | 18 ++- app/src/main/res/layout/grades_item_grade.xml | 25 ++++- .../main/res/layout/grades_item_semester.xml | 11 ++ .../main/res/layout/grades_item_subject.xml | 18 ++- 16 files changed, 248 insertions(+), 71 deletions(-) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfigGrades.kt b/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfigGrades.kt index 07e88c45..50e50fbf 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfigGrades.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/config/ProfileConfigGrades.kt @@ -28,7 +28,7 @@ class ProfileConfigGrades(private val config: ProfileConfig) { private var mHideImproved: Boolean? = null var hideImproved: Boolean - get() { mHideImproved = mHideImproved ?: config.values.get("hideImproved", true); return mHideImproved ?: true } + get() { mHideImproved = mHideImproved ?: config.values.get("hideImproved", false); return mHideImproved ?: false } set(value) { config.set("hideImproved", value); mHideImproved = value } private var mAverageWithoutWeight: Boolean? = null diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Grade.kt b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Grade.kt index bed7a191..393ab332 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Grade.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/data/db/entity/Grade.kt @@ -5,6 +5,7 @@ package pl.szczodrzynski.edziennik.data.db.entity import androidx.room.ColumnInfo import androidx.room.Entity +import androidx.room.Ignore import androidx.room.Index /*public Grade(int profileId, long id, String category, int color, String description, String name, float value, float weight, int semester, long teacherId, long subjectId) { @@ -81,5 +82,11 @@ open class Grade( */ @ColumnInfo(name = "gradeIsImprovement") var isImprovement = false + + @Ignore + var showAsUnseen = false + + val isImproved + get() = parentId ?: -1L != -1L } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesAdapter.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesAdapter.kt index 39796686..4b1c4f1e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesAdapter.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesAdapter.kt @@ -10,19 +10,24 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.RecyclerView +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.entity.Grade import pl.szczodrzynski.edziennik.data.db.full.GradeFull import pl.szczodrzynski.edziennik.onClick +import pl.szczodrzynski.edziennik.startCoroutineTimer import pl.szczodrzynski.edziennik.ui.modules.grades.models.* import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.* +import kotlin.coroutines.CoroutineContext class GradesAdapter( val activity: AppCompatActivity, var onGradeClick: ((item: GradeFull) -> Unit)? = null, var onGradesEditorClick: ((subject: GradesSubject, semester: GradesSemester) -> Unit)? = null -) : RecyclerView.Adapter() { +) : RecyclerView.Adapter(), CoroutineScope { companion object { private const val TAG = "GradesAdapter" private const val ITEM_TYPE_SUBJECT = 0 @@ -34,6 +39,13 @@ class GradesAdapter( const val STATE_OPENED = 1 } + private val app = activity.applicationContext as App + private val manager = app.gradesManager + + private val job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Main + var items = mutableListOf() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { @@ -67,56 +79,63 @@ class GradesAdapter( } if (model !is ExpandableItemModel<*>) return@OnClickListener + expandModel(model, view) + } + + fun expandModel(model: ExpandableItemModel<*>?, view: View?, notifyAdapter: Boolean = true) { + model ?: return val position = items.indexOf(model) if (position == -1) - return@OnClickListener - //val position = it.getTag(R.string.tag_key_position) as? Int ?: return@OnClickListener + return if (model is GradesSubject || model is GradesSemester) { - view.findViewById(R.id.dropdownIcon)?.let { dropdownIcon -> + view?.findViewById(R.id.dropdownIcon)?.let { dropdownIcon -> ObjectAnimator.ofFloat( - dropdownIcon, - View.ROTATION, - if (model.state == STATE_CLOSED) 0f else 180f, - if (model.state == STATE_CLOSED) 180f else 0f + dropdownIcon, + View.ROTATION, + if (model.state == STATE_CLOSED) 0f else 180f, + if (model.state == STATE_CLOSED) 180f else 0f ).setDuration(200).start(); } } if (model is GradesSubject) { - val preview = view.findViewById(R.id.previewContainer) - val summary = view.findViewById(R.id.yearSummary) + val preview = view?.findViewById(R.id.previewContainer) + val summary = view?.findViewById(R.id.yearSummary) preview?.visibility = if (model.state == STATE_CLOSED) View.INVISIBLE else View.VISIBLE summary?.visibility = if (model.state == STATE_CLOSED) View.VISIBLE else View.INVISIBLE } if (model.state == STATE_CLOSED) { - val subItems = if (model is GradesSemester && model.grades.isEmpty()) - listOf(GradesEmpty()) - else - model.items + val subItems = when { + model is GradesSemester && model.grades.isEmpty() -> + listOf(GradesEmpty()) + model is GradesSemester && manager.hideImproved -> + model.items.filter { !it.seen || !it.isImproved } + else -> model.items + } model.state = STATE_OPENED items.addAll(position + 1, subItems.filterNotNull()) - notifyItemRangeInserted(position + 1, subItems.size) - /*notifyItemRangeChanged( - position + subItems.size, - items.size - (position + subItems.size) - )*/ - //notifyItemRangeChanged(position, items.size - position) + if (notifyAdapter) notifyItemRangeInserted(position + 1, subItems.size) if (model is GradesSubject) { // auto expand first semester if (model.semesters.isNotEmpty()) { val semester = model.semesters.firstOrNull { it.grades.isNotEmpty() } ?: model.semesters.first() val semesterIndex = model.semesters.indexOf(semester) - val grades = if (semester.grades.isEmpty()) - listOf(GradesEmpty()) - else - semester.grades + + val grades = when { + semester.grades.isEmpty() -> + listOf(GradesEmpty()) + manager.hideImproved -> + semester.grades.filter { !it.seen || !it.isImproved } + else -> semester.grades + } + semester.state = STATE_OPENED items.addAll(position + 2 + semesterIndex, grades) - notifyItemRangeInserted(position + 2 + semesterIndex, grades.size) + if (notifyAdapter) notifyItemRangeInserted(position + 2 + semesterIndex, grades.size) } } } @@ -138,9 +157,7 @@ class GradesAdapter( if (end != -1) { items.subList(start, end).clear() - notifyItemRangeRemoved(start, end - start) - //notifyItemRangeChanged(start, end - start) - //notifyItemRangeChanged(position, items.size - position) + if (notifyAdapter) notifyItemRangeRemoved(start, end - start) } model.state = STATE_CLOSED @@ -152,8 +169,6 @@ class GradesAdapter( if (holder !is BindableViewHolder<*>) return - val app = activity.applicationContext as App - val viewType = when (holder) { is SubjectViewHolder -> ITEM_TYPE_SUBJECT is SemesterViewHolder -> ITEM_TYPE_SEMESTER @@ -167,11 +182,11 @@ class GradesAdapter( holder.itemView.setTag(R.string.tag_key_model, item) when { - holder is SubjectViewHolder && item is GradesSubject -> holder.onBind(activity, app, item, position) - holder is SemesterViewHolder && item is GradesSemester -> holder.onBind(activity, app, item, position) - holder is EmptyViewHolder && item is GradesEmpty -> holder.onBind(activity, app, item, position) - holder is GradeViewHolder && item is GradeFull -> holder.onBind(activity, app, item, position) - holder is StatsViewHolder && item is GradesStats -> holder.onBind(activity, app, item, position) + holder is SubjectViewHolder && item is GradesSubject -> holder.onBind(activity, app, item, position, this) + holder is SemesterViewHolder && item is GradesSemester -> holder.onBind(activity, app, item, position, this) + holder is EmptyViewHolder && item is GradesEmpty -> holder.onBind(activity, app, item, position, this) + holder is GradeViewHolder && item is GradeFull -> holder.onBind(activity, app, item, position, this) + holder is StatsViewHolder && item is GradesStats -> holder.onBind(activity, app, item, position, this) } if (holder is SemesterViewHolder && item is GradesSemester) { @@ -184,5 +199,23 @@ class GradesAdapter( holder.itemView.setOnClickListener(onClickListener) } + fun notifyItemChanged(model: Any) { + startCoroutineTimer(1000L, 0L) { + val index = items.indexOf(model) + if (index != -1) + notifyItemChanged(index) + } + } + + fun removeItem(model: Any) { + startCoroutineTimer(2000L, 0L) { + val index = items.indexOf(model) + if (index != -1) { + items.removeAt(index) + notifyItemRemoved(index) + } + } + } + override fun getItemCount() = items.size } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesFragment.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesFragment.kt index ed0c6220..4823e653 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesFragment.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/GradesFragment.kt @@ -12,11 +12,8 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.Observer import androidx.recyclerview.widget.LinearLayoutManager import kotlinx.coroutines.* -import pl.szczodrzynski.edziennik.App -import pl.szczodrzynski.edziennik.Bundle -import pl.szczodrzynski.edziennik.MainActivity +import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.MainActivity.Companion.TARGET_GRADES_EDITOR -import pl.szczodrzynski.edziennik.averageOrNull import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.full.GradeFull import pl.szczodrzynski.edziennik.databinding.GradesFragmentBinding @@ -48,6 +45,7 @@ class GradesFragment : Fragment(), CoroutineScope { } private val manager by lazy { app.gradesManager } private val dontCountGrades by lazy { manager.dontCountGrades } + private var expandSubjectId = 0L override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { activity = (getActivity() as MainActivity?) ?: return null @@ -62,6 +60,8 @@ class GradesFragment : Fragment(), CoroutineScope { if (!isAdded) return + expandSubjectId = arguments?.getLong("gradesSubjectId") ?: 0L + app.db.gradeDao() .getAllOrderBy(App.profileId, app.gradesManager.getOrderByString()) .observe(this, Observer { grades -> @@ -113,6 +113,7 @@ class GradesFragment : Fragment(), CoroutineScope { } } + @Suppress("SuspendFunctionOnCoroutineScope") private suspend fun processGrades(grades: List) { val items = mutableListOf() @@ -146,6 +147,11 @@ class GradesFragment : Fragment(), CoroutineScope { ?: GradesSemester(subject.subjectId, grade.semester).also { subject.semesters += it } } + grade.showAsUnseen = !grade.seen + if (!grade.seen) { + semester.hasUnseen = true + } + when (grade.type) { Grade.TYPE_SEMESTER1_PROPOSED, Grade.TYPE_SEMESTER2_PROPOSED -> semester.proposedGrade = grade @@ -154,10 +160,7 @@ class GradesFragment : Fragment(), CoroutineScope { Grade.TYPE_YEAR_PROPOSED -> subject.proposedGrade = grade Grade.TYPE_YEAR_FINAL -> subject.finalGrade = grade else -> { - if (!hideImproved || grade.parentId ?: -1L == -1L) { - // hide improved grades if parent(new grade) ID is not set - semester.grades += grade - } + semester.grades += grade countGrade(grade, subject.averages) countGrade(grade, semester.averages) } @@ -242,9 +245,27 @@ class GradesFragment : Fragment(), CoroutineScope { adapter.items = items.toMutableList() adapter.items.add(stats) + var expandSubjectModel: GradesSubject? = null + if (expandSubjectId != 0L) { + expandSubjectModel = items.firstOrNull { it.subjectId == expandSubjectId } + adapter.expandModel( + model = expandSubjectModel, + view = null, + notifyAdapter = false + ) + } + withContext(Dispatchers.Main) { adapter.notifyDataSetChanged() } + + startCoroutineTimer(500L, 0L) { + if (expandSubjectModel != null) { + b.gradesRecyclerView.smoothScrollToPosition( + items.indexOf(expandSubjectModel) + expandSubjectModel.semesters.size + (expandSubjectModel.semesters.firstOrNull()?.grades?.size ?: 0) + ) + } + } } private fun countGrade(grade: Grade, averages: GradesAverages) { diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/GradesSemester.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/GradesSemester.kt index 94d96acf..e8e107ad 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/GradesSemester.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/GradesSemester.kt @@ -4,16 +4,17 @@ package pl.szczodrzynski.edziennik.ui.modules.grades.models -import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.full.GradeFull data class GradesSemester( val subjectId: Long, val number: Int, - val grades: MutableList = mutableListOf() -) : ExpandableItemModel(grades) { + val grades: MutableList = mutableListOf() +) : ExpandableItemModel(grades) { override var level = 2 + var hasUnseen = false + val averages = GradesAverages() var proposedGrade: GradeFull? = null var finalGrade: GradeFull? = null diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/GradesSubject.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/GradesSubject.kt index abd4ce47..89b07e6f 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/GradesSubject.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/models/GradesSubject.kt @@ -16,6 +16,9 @@ data class GradesSubject( var lastAddedDate = 0L var semester: Int = 1 + val hasUnseen + get() = semesters.any { it.hasUnseen } + val averages = GradesAverages() var proposedGrade: GradeFull? = null var finalGrade: GradeFull? = null diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/BindableViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/BindableViewHolder.kt index 75dba275..876d9055 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/BindableViewHolder.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/BindableViewHolder.kt @@ -6,7 +6,8 @@ package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder import androidx.appcompat.app.AppCompatActivity import pl.szczodrzynski.edziennik.App +import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter interface BindableViewHolder { - fun onBind(activity: AppCompatActivity, app: App, item: T, position: Int) + fun onBind(activity: AppCompatActivity, app: App, item: T, position: Int, adapter: GradesAdapter) } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/EmptyViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/EmptyViewHolder.kt index 3665e182..ea28847e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/EmptyViewHolder.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/EmptyViewHolder.kt @@ -10,6 +10,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.RecyclerView import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.databinding.GradesItemEmptyBinding +import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesEmpty class EmptyViewHolder( @@ -21,7 +22,7 @@ class EmptyViewHolder( private const val TAG = "EmptyViewHolder" } - override fun onBind(activity: AppCompatActivity, app: App, item: GradesEmpty, position: Int) { + override fun onBind(activity: AppCompatActivity, app: App, item: GradesEmpty, position: Int, adapter: GradesAdapter) { } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/GradeViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/GradeViewHolder.kt index 6cb589a7..9927eabe 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/GradeViewHolder.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/GradeViewHolder.kt @@ -7,11 +7,14 @@ package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder import android.view.LayoutInflater import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity +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.full.GradeFull import pl.szczodrzynski.edziennik.databinding.GradesItemGradeBinding +import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter +import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject import pl.szczodrzynski.edziennik.utils.models.Date class GradeViewHolder( @@ -24,7 +27,7 @@ class GradeViewHolder( } @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") - override fun onBind(activity: AppCompatActivity, app: App, grade: GradeFull, position: Int) { + override fun onBind(activity: AppCompatActivity, app: App, grade: GradeFull, position: Int, adapter: GradesAdapter) { val manager = app.gradesManager b.gradeName.setGrade(grade, manager, bigView = true) @@ -45,19 +48,35 @@ class GradeViewHolder( grade.category } - b.gradeWeight.text = manager.getWeightString(activity, grade, showClassAverage = true) + val weightText = manager.getWeightString(activity, grade, showClassAverage = true) + b.gradeWeight.text = weightText + b.gradeWeight.isVisible = weightText != null b.gradeTeacherName.text = grade.teacherFullName b.gradeAddedDate.text = Date.fromMillis(grade.addedDate).let { it.getRelativeString(app, 5) ?: it.formattedStringShort } - /*if (!grade.seen) { - b.gradeDescription.setBackground(mContext.getResources().getDrawable(R.drawable.bg_rounded_4dp)) - b.gradeDescription.getBackground() - .setColorFilter(PorterDuffColorFilter(0x692196f3, PorterDuff.Mode.MULTIPLY)) - } else { - b.gradeDescription.setBackground(null) - }*/ + b.unread.isVisible = grade.showAsUnseen + if (!grade.seen) { + manager.markAsSeen(grade) + val subject = adapter.items.firstOrNull { + it is GradesSubject && it.subjectId == grade.subjectId + } as? GradesSubject ?: return + + val semester = subject.semesters.firstOrNull { it.number == grade.semester } ?: return + + semester.hasUnseen = semester.grades.any { !it.seen } + // check if the unseen status has changed + if (!semester.hasUnseen) { + adapter.notifyItemChanged(semester) + } + if (!subject.hasUnseen) { + adapter.notifyItemChanged(subject) + } + if (manager.hideImproved && grade.isImproved) { + adapter.removeItem(grade) + } + } } } diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/SemesterViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/SemesterViewHolder.kt index 7e8ef900..d10d671e 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/SemesterViewHolder.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/SemesterViewHolder.kt @@ -7,6 +7,7 @@ package pl.szczodrzynski.edziennik.ui.modules.grades.viewholder import android.view.LayoutInflater import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.R @@ -14,6 +15,7 @@ import pl.szczodrzynski.edziennik.databinding.GradesItemSemesterBinding import pl.szczodrzynski.edziennik.setText import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSemester +import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject class SemesterViewHolder( inflater: LayoutInflater, @@ -24,7 +26,7 @@ class SemesterViewHolder( private const val TAG = "SemesterViewHolder" } - override fun onBind(activity: AppCompatActivity, app: App, item: GradesSemester, position: Int) { + override fun onBind(activity: AppCompatActivity, app: App, item: GradesSemester, position: Int, adapter: GradesAdapter) { val manager = app.gradesManager b.semesterName.setText(R.string.grades_semester_format, item.number) b.dropdownIcon.rotation = when (item.state) { @@ -32,6 +34,33 @@ class SemesterViewHolder( else -> 180f } + b.unread.isVisible = item.hasUnseen + + var unseenChanged = false + if (item.proposedGrade?.seen == false) { + manager.markAsSeen(item.proposedGrade!!) + unseenChanged = true + } + if (item.finalGrade?.seen == false) { + manager.markAsSeen(item.finalGrade!!) + unseenChanged = true + } + + if (unseenChanged) { + val subject = adapter.items.firstOrNull { + it is GradesSubject && it.subjectId == item.subjectId + } as? GradesSubject ?: return + + item.hasUnseen = item.grades.any { !it.seen } + // check if the unseen status has changed + if (!item.hasUnseen) { + adapter.notifyItemChanged(item) + } + if (!subject.hasUnseen) { + adapter.notifyItemChanged(subject) + } + } + b.average.text = manager.getAverageString(app, item.averages) b.proposedGrade.setGrade(item.proposedGrade, manager) b.finalGrade.setGrade(item.finalGrade, manager) diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/StatsViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/StatsViewHolder.kt index 5aa1fc4d..61ff6c28 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/StatsViewHolder.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/StatsViewHolder.kt @@ -16,6 +16,7 @@ import pl.szczodrzynski.edziennik.R import pl.szczodrzynski.edziennik.databinding.GradesItemStatsBinding import pl.szczodrzynski.edziennik.onClick import pl.szczodrzynski.edziennik.ui.dialogs.settings.GradesConfigDialog +import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesStats import java.text.DecimalFormat @@ -28,7 +29,7 @@ class StatsViewHolder( private const val TAG = "StatsViewHolder" } - override fun onBind(activity: AppCompatActivity, app: App, item: GradesStats, position: Int) { + override fun onBind(activity: AppCompatActivity, app: App, item: GradesStats, position: Int, adapter: GradesAdapter) { val manager = app.gradesManager val showAverages = mutableListOf() val showPoint = mutableListOf() @@ -109,7 +110,7 @@ class StatsViewHolder( } private fun getSemesterString(context: Context, expected: Float, proposed: Float, final: Float, notAllFinal: Boolean) : Pair { - val format = DecimalFormat("#.##") + val format = DecimalFormat("#.00") val average = when { final != 0f -> final diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/SubjectViewHolder.kt b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/SubjectViewHolder.kt index f67a8c70..ecff434d 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/SubjectViewHolder.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/ui/modules/grades/viewholder/SubjectViewHolder.kt @@ -14,6 +14,7 @@ import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ContextThemeWrapper import androidx.core.view.get +import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.R @@ -21,6 +22,7 @@ import pl.szczodrzynski.edziennik.databinding.GradesItemSubjectBinding import pl.szczodrzynski.edziennik.dp import pl.szczodrzynski.edziennik.setText import pl.szczodrzynski.edziennik.ui.modules.grades.GradeView +import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter import pl.szczodrzynski.edziennik.ui.modules.grades.GradesAdapter.Companion.STATE_CLOSED import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSubject import pl.szczodrzynski.edziennik.utils.Themes @@ -34,7 +36,7 @@ class SubjectViewHolder( private const val TAG = "SubjectViewHolder" } - override fun onBind(activity: AppCompatActivity, app: App, item: GradesSubject, position: Int) { + override fun onBind(activity: AppCompatActivity, app: App, item: GradesSubject, position: Int, adapter: GradesAdapter) { val manager = app.gradesManager val contextWrapper = ContextThemeWrapper(activity, Themes.themeInt) @@ -44,6 +46,8 @@ class SubjectViewHolder( else -> 180f } + b.unread.isVisible = item.hasUnseen + b.previewContainer.visibility = if (item.state == STATE_CLOSED) View.VISIBLE else View.INVISIBLE b.yearSummary.visibility = if (item.state == STATE_CLOSED) View.INVISIBLE else View.VISIBLE @@ -71,7 +75,10 @@ class SubjectViewHolder( }) }*/ + val hideImproved = manager.hideImproved for (grade in firstSemester.grades) { + if (hideImproved && grade.isImproved) + continue b.gradesContainer.addView(GradeView( contextWrapper, grade, diff --git a/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/GradesManager.kt b/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/GradesManager.kt index 9b3735fc..2f95ea6a 100644 --- a/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/GradesManager.kt +++ b/app/src/main/java/pl/szczodrzynski/edziennik/utils/managers/GradesManager.kt @@ -5,18 +5,23 @@ package pl.szczodrzynski.edziennik.utils.managers import android.content.Context +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import pl.szczodrzynski.edziennik.* import pl.szczodrzynski.edziennik.data.db.entity.Grade import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_NORMAL import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_AVG import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_POINT_SUM import pl.szczodrzynski.edziennik.data.db.entity.Grade.Companion.TYPE_YEAR_FINAL +import pl.szczodrzynski.edziennik.data.db.full.GradeFull import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesAverages import pl.szczodrzynski.edziennik.ui.modules.grades.models.GradesSemester import java.text.DecimalFormat +import kotlin.coroutines.CoroutineContext import kotlin.math.floor -class GradesManager(val app: App) { +class GradesManager(val app: App) : CoroutineScope { companion object { const val ORDER_BY_DATE_DESC = 0 const val ORDER_BY_SUBJECT_ASC = 1 @@ -31,6 +36,10 @@ class GradesManager(val app: App) { const val COLOR_MODE_WEIGHTED = 1 } + private val job = Job() + override val coroutineContext: CoroutineContext + get() = job + Dispatchers.Default + private val gradeRegex by lazy { """([0-6])([+-])?""".toRegex() } private val format = DecimalFormat("#.##") @@ -144,6 +153,13 @@ class GradesManager(val app: App) { return color or 0xff000000.toInt() } + fun markAsSeen(grade: GradeFull) { + grade.seen = true + startCoroutineTimer(500L, 0L) { + app.db.metadataDao().setSeen(grade.profileId, grade, true) + } + } + fun calculateAverages(averages: GradesAverages, semesters: List? = null) { if (averages.pointAvgMax != 0f) averages.pointAvgPercent = averages.pointAvgSum / averages.pointAvgMax * 100f diff --git a/app/src/main/res/layout/grades_item_grade.xml b/app/src/main/res/layout/grades_item_grade.xml index fa05de4d..10cc06b2 100644 --- a/app/src/main/res/layout/grades_item_grade.xml +++ b/app/src/main/res/layout/grades_item_grade.xml @@ -20,6 +20,10 @@ android:layout_height="40dp" android:layout_marginStart="8dp" android:layout_marginLeft="8dp" + tools:background="@drawable/bg_rounded_8dp" + tools:backgroundTint="#4caf50" + tools:textSize="24sp" + tools:gravity="center" tools:text="5+" /> + tools:text="kraje" /> + + + tools:text="Kartkówki - K1" /> + tools:text="Jan Kowalski" /> diff --git a/app/src/main/res/layout/grades_item_semester.xml b/app/src/main/res/layout/grades_item_semester.xml index 25d8f03a..4126cca5 100644 --- a/app/src/main/res/layout/grades_item_semester.xml +++ b/app/src/main/res/layout/grades_item_semester.xml @@ -37,6 +37,17 @@ android:textSize="18sp" tools:text="Semestr 1" /> + + + android:layout_height="wrap_content" + android:orientation="horizontal"> + tools:text="systemy operacyjne" /> + + + tools:visibility="visible"/>