Skip to content
This repository has been archived by the owner on Jan 10, 2024. It is now read-only.

Commit

Permalink
Average grade improvement (#1439)
Browse files Browse the repository at this point in the history
* Added ui to enter grade parameters

* Added menu item to enter edit mode

* Added click listener to edit menu item to toggle edit mode

* toggeling edit mode changes now all ui elements

* Exam List is stored as a json object in shared preferences

* connected ui to the model - exams are saved permanently in shared preferences

* connected checkbox to the exam model

* reordered ui elements for better visibility

* extracted string ressources

* downloading new grades update the existing grade list

* hidden grades are o displayed correctly after initialization

* added ui to manually add exams

* added ui to delete manually added exams

* added logic to delete manually added exams

* added logic to manually add exams

* reduced number of store operations, newly added exams are now directly shown

* finished manually adding and deleting exams

* started input sanitization, tried to fix recycler overwrites

* improved input sanitization for remaining attributes

* adapted computation of the average grade

* prevented wrong updates of credits in the recycler view

* fixed ui inconsistencies, reduced method sizes

* persistently stored exams in shared preferences

* extracted string resources

* linted small warnings

* fixed styling issue with the add grade dialog, added option for weighted grades in the diagrams.

* fixed unclickable checkbox

* fixed inconsistency in saving exams to the shared preferences

* changed stickyListHeaders import to a still maintained project to prevent occasionally crashes on closing

* added translations, linted; show program spinner only if more than one program exits

* added more intuitive icons, fixed translation

* Added ui to enter grade parameters

* Added menu item to enter edit mode

* Added click listener to edit menu item to toggle edit mode

* toggeling edit mode changes now all ui elements

* Exam List is stored as a json object in shared preferences

* connected ui to the model - exams are saved permanently in shared preferences

* connected checkbox to the exam model

* reordered ui elements for better visibility

* extracted string ressources

* downloading new grades update the existing grade list

* hidden grades are o displayed correctly after initialization

* added ui to manually add exams

* added ui to delete manually added exams

* added logic to delete manually added exams

* added logic to manually add exams

* reduced number of store operations, newly added exams are now directly shown

* finished manually adding and deleting exams

* started input sanitization, tried to fix recycler overwrites

* improved input sanitization for remaining attributes

* adapted computation of the average grade

* prevented wrong updates of credits in the recycler view

* fixed ui inconsistencies, reduced method sizes

* persistently stored exams in shared preferences

* extracted string resources

* linted small warnings

* fixed styling issue with the add grade dialog, added option for weighted grades in the diagrams.

* fixed unclickable checkbox

* fixed inconsistency in saving exams to the shared preferences

* added translations, linted; show program spinner only if more than one program exits

* Fix Build (#1436)

* Fix ktlint errors

* Help out Kotlins type inference

* Move long test strings from code to seperate json files

* Bump Robolectric version to 4.8

* Update appcompat, fragment libraries and fix findViewById() in Fragments (#1434)

* Update appcompat, fragment and fix findViewById() in Fragments

* fix formatting

Co-authored-by: Kordian Bruck <[email protected]>

* Release 3.18

* added more intuitive icons, fixed translation

* fixed errors from merging

* added questionmark to text

* Update app/src/main/java/de/tum/in/tumcampusapp/component/tumui/grades/ExamListAdapter.kt

Co-authored-by: Fabian Sauter <[email protected]>

* Apply suggestions from code review

Co-authored-by: Fabian Sauter <[email protected]>

* Apply suggestions from code review in grades Fragment

Co-authored-by: Fabian Sauter <[email protected]>

* implemented requested feedback

* linted grade files

Co-authored-by: Christian <[email protected]>
Co-authored-by: Jakob Foerste <[email protected]>
Co-authored-by: Kordian Bruck <[email protected]>
Co-authored-by: Kordian Bruck <[email protected]>
Co-authored-by: Fabian Sauter <[email protected]>
  • Loading branch information
6 people authored Jul 28, 2022
1 parent 207bc94 commit 4f75a32
Show file tree
Hide file tree
Showing 17 changed files with 1,223 additions and 434 deletions.
17 changes: 13 additions & 4 deletions app/src/main/java/de/tum/in/tumcampusapp/api/app/DateSerializer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ import java.util.*
*/
class DateSerializer : JsonDeserializer<DateTime>, JsonSerializer<DateTime> {
private val formatStrings = arrayOf(
"yyyy-MM-dd",
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd'T'HH:mm:ss'Z'"
"dd.MM.yy",
"dd.MM.yyyy",
"yyyy.MM.dd",
"dd-MM-yy",
"dd-MM-yyyy",
"yyyy-MM-dd",
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd'T'HH:mm:ss'Z'"
)

private val dateFormats = formatStrings.map {
Expand All @@ -37,7 +42,11 @@ class DateSerializer : JsonDeserializer<DateTime>, JsonSerializer<DateTime> {
throw JsonParseException("Unparseable date: \"${json?.asString.orEmpty()}\". Supported formats: ${formatStrings.contentToString()}")
}

override fun serialize(time: DateTime, typeOfT: Type, context: JsonSerializationContext): JsonElement {
override fun serialize(
time: DateTime,
typeOfT: Type,
context: JsonSerializationContext
): JsonElement {
return JsonPrimitive(DateTimeUtils.getDateString(time))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import org.joda.time.format.DateTimeFormat
class DateTimeConverter : TypeConverter<DateTime?> {

private val formats = arrayOf(
"yyyy-MM-dd",
"yyyy-MM-dd HH:mm",
"yyyy-MM-dd HH:mm:ss"
"dd.MM.yy",
"dd.MM.yyyy",
"yyyy-MM-dd",
"yyyy-MM-dd HH:mm",
"yyyy-MM-dd HH:mm:ss"
)

override fun read(value: String?): DateTime? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import android.widget.EditText
import android.widget.LinearLayout
import android.widget.CheckBox
import android.widget.Button
import android.widget.ImageView
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import de.tum.`in`.tumcampusapp.R
import de.tum.`in`.tumcampusapp.component.other.generic.adapter.SimpleStickyListHeadersAdapter
import de.tum.`in`.tumcampusapp.component.tumui.grades.model.Exam
Expand All @@ -12,7 +19,10 @@ import org.joda.time.format.DateTimeFormat
/**
* Custom UI adapter for a list of exams.
*/
class ExamListAdapter(context: Context, results: List<Exam>) : SimpleStickyListHeadersAdapter<Exam>(context, results.toMutableList()) {
class ExamListAdapter(context: Context, results: List<Exam>, gradesFragment: GradesFragment) :
SimpleStickyListHeadersAdapter<Exam>(context, results.toMutableList()) {

val localGradesFragment: GradesFragment = gradesFragment

init {
itemList.sort()
Expand All @@ -32,24 +42,175 @@ class ExamListAdapter(context: Context, results: List<Exam>) : SimpleStickyListH
}

val exam = itemList[position]
holder.nameTextView.text = exam.course
holder.gradeTextView.text = exam.grade

val gradeColor = exam.getGradeColor(context)
holder.gradeTextView.background.setTint(gradeColor)
initUIEditElements(holder, exam)
initUIDisplayElements(holder, exam)
return view
}

private fun initUIDisplayElements(holder: ViewHolder, exam: Exam) {
holder.nameTextView.text = exam.course
holder.gradeTextView.text = exam.grade
adaptUIToCheckboxStatus(holder, exam)
val date: String = if (exam.date == null) {
context.getString(R.string.not_specified)
} else {
DATE_FORMAT.print(exam.date)
}
holder.examDateTextView.text = String.format("%s: %s", context.getString(R.string.date), date)
holder.examDateTextView.text =
String.format("%s: %s", context.getString(R.string.date), date)

holder.additionalInfoTextView.text = String.format("%s: %s, %s: %s",
context.getString(R.string.examiner), exam.examiner,
context.getString(R.string.mode), exam.modus)
holder.additionalInfoTextView.text = String.format(
"%s: %s, %s: %s",
context.getString(R.string.examiner), exam.examiner,
context.getString(R.string.mode), exam.modus
)
}

return view
/**
* Init the ui Elements to change the parameters of the grade
*/
private fun initUIEditElements(holder: ViewHolder, exam: Exam) {
if (localGradesFragment.isEditModeEnabled()) {
holder.editGradesContainer.visibility = View.GONE
holder.gradeTextViewDeleteCustomGrade.visibility = View.GONE
} else {
holder.editGradesContainer.visibility = View.VISIBLE

initListenerDeleteCustomGrade(exam, holder)
initListenerEditTexts(exam, holder)
initListenerResetGradeParameters(exam, holder)
initCheckBoxUsedInAverage(exam, holder)
}
}

/**
* Adds a ClickListener which will show a confirmation dialog whether the exam should actually be deleted.
*/
private fun initListenerDeleteCustomGrade(exam: Exam, holder: ViewHolder) {
if (exam.manuallyAdded) {
holder.gradeTextViewDeleteCustomGrade.visibility = View.VISIBLE

holder.gradeTextViewDeleteCustomGrade.setOnClickListener {
val dialog = AlertDialog.Builder(localGradesFragment.requireContext())
.setTitle(context.getString(R.string.delete_exam))
.setMessage(
context.getString(R.string.delete_exam_dialog_message)
)
.setPositiveButton(R.string.delete) { _, _ ->

localGradesFragment.deleteExamFromList(exam)
}
.setNegativeButton(android.R.string.cancel, null)
.create()
.apply {
window?.setBackgroundDrawableResource(R.drawable.rounded_corners_background)
}
dialog.show()
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(
localGradesFragment.resources.getColor(R.color.text_primary)
)
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(
localGradesFragment.resources.getColor(R.color.text_primary)
)
}
} else {
holder.gradeTextViewDeleteCustomGrade.visibility = View.GONE
}
}

/**
* Adds on Focus change listeners which store the value to the exam object if and only if the
* user finished editing the exam.
*/
private fun initListenerEditTexts(exam: Exam, holder: ViewHolder) {
holder.editTextGradeWeights.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
val oldWeight = holder.editTextGradeWeights.text.toString().toDouble()
if (exam.weight != oldWeight) {
exam.weight = oldWeight
localGradesFragment.storeExamListInSharedPreferences()
notifyDataSetChanged()
}
}
}

holder.editTextGradeCredits.setOnFocusChangeListener { _, hasFocus ->
if (!hasFocus) {
val oldCredits = holder.editTextGradeCredits.text.toString().toInt()
if (exam.credits_new != oldCredits) {
exam.credits_new = oldCredits
localGradesFragment.storeExamListInSharedPreferences()
notifyDataSetChanged()
}
}
}
holder.editTextGradeWeights.setText(exam.weight.toString())
holder.editTextGradeCredits.setText(exam.credits_new.toString())
}

/**
* Adds a ClickListener to reset one exam to the default values and adapts the UI accordingly.
*/
private fun initListenerResetGradeParameters(exam: Exam, holder: ViewHolder) {
holder.buttonResetGradeParameters.setOnClickListener(object : View.OnClickListener {
override fun onClick(p0: View?) {
exam.gradeUsedInAverage = true
adaptUIToCheckboxStatus(holder, exam)
holder.checkBoxUseGradeForAverage.isChecked = true
exam.weight = 1.0
exam.credits_new = 6
holder.editTextGradeWeights.setText(exam.weight.toString())
holder.editTextGradeCredits.setText(exam.credits_new.toString())
localGradesFragment.storeExamListInSharedPreferences()
notifyDataSetChanged()
}
})
}

/**
* Initializes the state of the checkbox and adapts the UI accordingly.
*/
private fun initCheckBoxUsedInAverage(exam: Exam, holder: ViewHolder) {
holder.checkBoxUseGradeForAverage.isChecked = exam.gradeUsedInAverage
adaptUIToCheckboxStatus(holder, exam)
holder.checkBoxUseGradeForAverage.setOnClickListener() {
exam.gradeUsedInAverage = holder.checkBoxUseGradeForAverage.isChecked
adaptUIToCheckboxStatus(holder, exam)
localGradesFragment.storeExamListInSharedPreferences()
notifyDataSetChanged()
}
}

/**
* Enables/disables Edittexts, and adapts the color of the grade bar on the right side.
*/
private fun adaptUIToCheckboxStatus(
holder: ViewHolder,
exam: Exam
) {
if (exam.gradeUsedInAverage) {
holder.editTextGradeCredits.isEnabled = true
holder.editTextGradeWeights.isEnabled = true
val gradeColor = exam.getGradeColor(context)
holder.gradeTextView.background.setTint(gradeColor)
holder.gradeTextView.setTextColor(ContextCompat.getColor(context, R.color.white))
} else {
holder.editTextGradeCredits.isEnabled = false
holder.editTextGradeWeights.isEnabled = false
holder.gradeTextView.background.setTint(
ContextCompat.getColor(
context,
R.color.transparent
)
)
holder.gradeTextView.setTextColor(
ContextCompat.getColor(
context,
R.color.grade_default
)
)
}
}

override fun generateHeaderName(item: Exam): String {
Expand All @@ -71,6 +232,16 @@ class ExamListAdapter(context: Context, results: List<Exam>) : SimpleStickyListH
var gradeTextView: TextView = itemView.findViewById(R.id.gradeTextView)
var examDateTextView: TextView = itemView.findViewById(R.id.examDateTextView)
var additionalInfoTextView: TextView = itemView.findViewById(R.id.additionalInfoTextView)

var editTextGradeWeights: EditText = itemView.findViewById(R.id.editTextGradeWeight)
var editTextGradeCredits: EditText = itemView.findViewById(R.id.editTextCreditsOfSubject)
var editGradesContainer: LinearLayout = itemView.findViewById(R.id.editGradesContainer)
var checkBoxUseGradeForAverage: CheckBox =
itemView.findViewById(R.id.checkBoxUseGradeForAverage)
val buttonResetGradeParameters: Button =
itemView.findViewById(R.id.buttonResetGradeParameters)
val gradeTextViewDeleteCustomGrade: ImageView =
itemView.findViewById(R.id.gradeTextViewDeleteCustomGrade)
}

companion object {
Expand Down
Loading

0 comments on commit 4f75a32

Please sign in to comment.