fixed online connection error and class scale question bug

This commit is contained in:
oxidiert
2026-02-23 18:20:21 +01:00
parent d30c94beeb
commit 07b7b3dc1b
2 changed files with 61 additions and 21 deletions

1
.idea/misc.xml generated
View File

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">

View File

@ -9,11 +9,10 @@ import androidx.core.widget.TextViewCompat
import kotlinx.coroutines.* import kotlinx.coroutines.*
/* /*
Zweck: Zweck:
- Stellt eine „Glas-Skala“-Frage dar, bei der pro Symptom (Zeile) genau eine von fünf Antwortstufen gewählt wird: never / little / moderate / much / extreme. - „Glas-Skala“-Frage: pro Symptom (Zeile) genau eine von fünf Stufen.
- Die Stufen werden sowohl als RadioButtons als auch über eine feste Icon-Leiste visualisiert. - Stufen als RadioButtons (clickbar) + Icon-Header.
- FIX: Pro Zeile wird Single-Select erzwungen (auch im Bearbeiten-Modus / Restore).
*/ */
class HandlerGlassScaleQuestion( class HandlerGlassScaleQuestion(
@ -54,6 +53,9 @@ class HandlerGlassScaleQuestion(
"extreme_glass" to R.drawable.ic_glass_4 "extreme_glass" to R.drawable.ic_glass_4
) )
// Damit wir beim programmatic-check keine Endlosschleifen triggern
private var suppressRowListener: Boolean = false
override fun bind(layout: View, question: QuestionItem) { override fun bind(layout: View, question: QuestionItem) {
if (question !is QuestionItem.GlassScaleQuestion) return if (question !is QuestionItem.GlassScaleQuestion) return
this.layout = layout this.layout = layout
@ -65,15 +67,16 @@ class HandlerGlassScaleQuestion(
titleTv.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: "" titleTv.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
questionTv.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: "" questionTv.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
setTextSizePercentOfScreenHeight(titleTv, 0.03f) setTextSizePercentOfScreenHeight(titleTv, 0.03f)
setTextSizePercentOfScreenHeight(questionTv, 0.03f) setTextSizePercentOfScreenHeight(questionTv, 0.03f)
// feste Icon-Leiste // Header Icons
val header = layout.findViewById<LinearLayout>(R.id.glass_header) val header = layout.findViewById<LinearLayout>(R.id.glass_header)
header.removeAllViews() header.removeAllViews()
header.addView(Space(context).apply { header.addView(Space(context).apply {
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 4f) layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 4f)
}) })
val iconSizePx = (context.resources.displayMetrics.density * 36).toInt() val iconSizePx = (context.resources.displayMetrics.density * 36).toInt()
scaleLabels.forEach { labelKey -> scaleLabels.forEach { labelKey ->
val cell = FrameLayout(context).apply { val cell = FrameLayout(context).apply {
@ -88,13 +91,12 @@ class HandlerGlassScaleQuestion(
cell.addView(img) cell.addView(img)
header.addView(cell) header.addView(cell)
} }
//
val tableLayout = layout.findViewById<TableLayout>(R.id.glass_table) val tableLayout = layout.findViewById<TableLayout>(R.id.glass_table)
tableLayout.removeAllViews() tableLayout.removeAllViews()
addSymptomRows(tableLayout) addSymptomRows(tableLayout)
// ggf. Antworten aus DB wiederherstellen // Restore aus DB (nur wenn noch nicht im answers)
val anySymptomNeedsRestore = question.symptoms.any { !answers.containsKey(it) } val anySymptomNeedsRestore = question.symptoms.any { !answers.containsKey(it) }
if (anySymptomNeedsRestore) { if (anySymptomNeedsRestore) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
@ -104,21 +106,14 @@ class HandlerGlassScaleQuestion(
val answerMap = allAnswersForClient.associateBy({ it.questionId }, { it.answerValue }) val answerMap = allAnswersForClient.associateBy({ it.questionId }, { it.answerValue })
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val table = tableLayout
for ((index, symptomKey) in question.symptoms.withIndex()) { for ((index, symptomKey) in question.symptoms.withIndex()) {
val answerMapKey = "$questionnaireMeta-$symptomKey" val answerMapKey = "$questionnaireMeta-$symptomKey"
val dbAnswer = answerMap[answerMapKey]?.takeIf { it.isNotBlank() }?.trim() val dbAnswer = answerMap[answerMapKey]?.takeIf { it.isNotBlank() }?.trim()
if (!answers.containsKey(symptomKey) && !dbAnswer.isNullOrBlank()) { if (!answers.containsKey(symptomKey) && !dbAnswer.isNullOrBlank()) {
if (index < table.childCount) { if (index < tableLayout.childCount) {
val row = table.getChildAt(index) as? TableRow ?: continue val row = tableLayout.getChildAt(index) as? TableRow ?: continue
val radioGroup = row.getChildAt(1) as? RadioGroup ?: continue val radioGroup = row.getChildAt(1) as? RadioGroup ?: continue
for (i in 0 until radioGroup.childCount) { setSingleSelection(radioGroup, dbAnswer) // <-- FIXED restore
val rb = getRadioFromChild(radioGroup.getChildAt(i)) ?: continue
if ((rb.tag as? String)?.trim() == dbAnswer) {
rb.isChecked = true
break
}
}
answers[symptomKey] = dbAnswer answers[symptomKey] = dbAnswer
points.add(pointsMap[dbAnswer] ?: 0) points.add(pointsMap[dbAnswer] ?: 0)
} }
@ -165,30 +160,75 @@ class HandlerGlassScaleQuestion(
setPadding(0, 0, 0, 0) setPadding(0, 0, 0, 0)
} }
// Build buttons
scaleLabels.forEach { labelKey -> scaleLabels.forEach { labelKey ->
val cell = FrameLayout(context).apply { val cell = FrameLayout(context).apply {
layoutParams = RadioGroup.LayoutParams(0, RadioGroup.LayoutParams.WRAP_CONTENT, 1f) layoutParams = RadioGroup.LayoutParams(0, RadioGroup.LayoutParams.WRAP_CONTENT, 1f)
} }
val rb = RadioButton(context).apply { val rb = RadioButton(context).apply {
tag = labelKey tag = labelKey
id = View.generateViewId() id = View.generateViewId()
isChecked = savedLabel == labelKey isChecked = false
setPadding(0, 0, 0, 0) setPadding(0, 0, 0, 0)
} }
rb.layoutParams = FrameLayout.LayoutParams( rb.layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT,
Gravity.CENTER Gravity.CENTER
) )
// <<< FIX: erzwinge pro Zeile genau eine Auswahl
rb.setOnCheckedChangeListener { buttonView, isChecked ->
if (suppressRowListener) return@setOnCheckedChangeListener
if (!isChecked) return@setOnCheckedChangeListener
val selectedLabel = buttonView.tag as? String ?: return@setOnCheckedChangeListener
// wenn einer checked -> alle anderen in dieser Row unchecken
suppressRowListener = true
try {
for (i in 0 until radioGroup.childCount) {
val other = getRadioFromChild(radioGroup.getChildAt(i)) ?: continue
if (other != buttonView) other.isChecked = false
}
} finally {
suppressRowListener = false
}
// Optional (wenn du willst): sofort im answers setzen (Edit-Mode fühlt sich dann "direkt" an)
answers[symptomKey] = selectedLabel
}
cell.addView(rb) cell.addView(rb)
radioGroup.addView(cell) radioGroup.addView(cell)
} }
// Restore aus answers (falls vorhanden)
if (!savedLabel.isNullOrBlank()) {
setSingleSelection(radioGroup, savedLabel)
}
row.addView(radioGroup) row.addView(radioGroup)
table.addView(row) table.addView(row)
} }
} }
/**
* Setzt in diesem Row-RadioGroup genau einen Wert aktiv und alle anderen aus.
*/
private fun setSingleSelection(radioGroup: RadioGroup, labelKey: String) {
suppressRowListener = true
try {
for (i in 0 until radioGroup.childCount) {
val rb = getRadioFromChild(radioGroup.getChildAt(i)) ?: continue
val tag = (rb.tag as? String)?.trim()
rb.isChecked = (tag == labelKey.trim())
}
} finally {
suppressRowListener = false
}
}
override fun validate(): Boolean { override fun validate(): Boolean {
val table = layout.findViewById<TableLayout>(R.id.glass_table) val table = layout.findViewById<TableLayout>(R.id.glass_table)
@ -217,6 +257,7 @@ class HandlerGlassScaleQuestion(
val row = table.getChildAt(i) as TableRow val row = table.getChildAt(i) as TableRow
val symptomKey = question.symptoms[i] val symptomKey = question.symptoms[i]
val radioGroup = row.getChildAt(1) as RadioGroup val radioGroup = row.getChildAt(1) as RadioGroup
for (j in 0 until radioGroup.childCount) { for (j in 0 until radioGroup.childCount) {
val rb = getRadioFromChild(radioGroup.getChildAt(j)) ?: continue val rb = getRadioFromChild(radioGroup.getChildAt(j)) ?: continue
if (rb.isChecked) { if (rb.isChecked) {
@ -242,4 +283,4 @@ class HandlerGlassScaleQuestion(
TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE)
view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp) view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp)
} }
} }