added edit mode for every important question layout
This commit is contained in:
@ -3,6 +3,7 @@ package com.dano.test1
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.widget.*
|
||||
import kotlinx.coroutines.*
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@ -12,7 +13,8 @@ class HandlerDateSpinner(
|
||||
private val languageID: String,
|
||||
private val goToNextQuestion: () -> Unit,
|
||||
private val goToPreviousQuestion: () -> Unit,
|
||||
private val showToast: (String) -> Unit
|
||||
private val showToast: (String) -> Unit,
|
||||
private val questionnaireMeta: String // neu für DB-Abfrage
|
||||
) : QuestionHandler {
|
||||
|
||||
private lateinit var question: QuestionItem.DateSpinnerQuestion
|
||||
@ -36,6 +38,7 @@ class HandlerDateSpinner(
|
||||
questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
|
||||
textView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
|
||||
|
||||
// --- gespeicherte Antwort aus answers oder null ---
|
||||
val (savedYear, savedMonthIndex, savedDay) = question.question?.let {
|
||||
parseSavedDate(answers[it] as? String)
|
||||
} ?: Triple(null, null, null)
|
||||
@ -54,6 +57,49 @@ class HandlerDateSpinner(
|
||||
setupSpinner(spinnerMonth, months, defaultMonth)
|
||||
setupSpinner(spinnerYear, years, defaultYear)
|
||||
|
||||
// --- DB-Abfrage falls noch nichts in answers gespeichert ---
|
||||
val answerMapKey = question.question ?: (question.id ?: "")
|
||||
if (answerMapKey.isNotBlank() && !answers.containsKey(answerMapKey)) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val clientCode = GlobalValues.LAST_CLIENT_CODE
|
||||
if (clientCode.isNullOrBlank()) return@launch
|
||||
|
||||
val allAnswersForClient = MyApp.database.answerDao().getAnswersForClient(clientCode)
|
||||
val myQuestionId = questionnaireMeta + "-" + question.question
|
||||
val dbAnswer = allAnswersForClient.find { it.questionId == myQuestionId }?.answerValue
|
||||
|
||||
if (!dbAnswer.isNullOrBlank()) {
|
||||
withContext(Dispatchers.Main) {
|
||||
answers[answerMapKey] = dbAnswer
|
||||
|
||||
val (dbYear, dbMonthIndex, dbDay) = parseSavedDate(dbAnswer)
|
||||
|
||||
dbYear?.let { year ->
|
||||
val index = years.indexOf(year)
|
||||
if (index >= 0) spinnerYear.setSelection(index)
|
||||
}
|
||||
|
||||
dbMonthIndex?.let { monthIndex ->
|
||||
if (monthIndex in months.indices) {
|
||||
val monthObj = months[monthIndex]
|
||||
val idx = months.indexOf(monthObj)
|
||||
if (idx >= 0) spinnerMonth.setSelection(idx)
|
||||
}
|
||||
}
|
||||
|
||||
dbDay?.let { day ->
|
||||
val idx = days.indexOf(day)
|
||||
if (idx >= 0) spinnerDay.setSelection(idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout.findViewById<Button>(R.id.Qnext).setOnClickListener {
|
||||
if (validate()) {
|
||||
saveAnswer()
|
||||
|
||||
@ -4,6 +4,7 @@ import android.content.Context
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.*
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
class HandlerGlassScaleQuestion(
|
||||
private val context: Context,
|
||||
@ -12,7 +13,8 @@ class HandlerGlassScaleQuestion(
|
||||
private val languageID: String,
|
||||
private val goToNextQuestion: () -> Unit,
|
||||
private val goToPreviousQuestion: () -> Unit,
|
||||
private val showToast: (String) -> Unit
|
||||
private val showToast: (String) -> Unit,
|
||||
private val questionnaireMeta: String // neu: für DB-Abfrage wie bei anderen Handlern
|
||||
) : QuestionHandler {
|
||||
|
||||
private lateinit var layout: View
|
||||
@ -71,6 +73,63 @@ class HandlerGlassScaleQuestion(
|
||||
|
||||
addSymptomRows(tableLayout)
|
||||
|
||||
// --- DB-Abfrage: falls für eine Symptom-Antwort noch nichts im answers-Map steht ---
|
||||
// Wir holen alle Antworten für den Client und setzen dort, wo answers noch keinen Eintrag hat,
|
||||
// die gespeicherte DB-Antwort (und aktualisieren UI + points).
|
||||
val anySymptomNeedsRestore = question.symptoms.any { sym ->
|
||||
val key = sym
|
||||
!answers.containsKey(key)
|
||||
}
|
||||
|
||||
if (anySymptomNeedsRestore) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val clientCode = GlobalValues.LAST_CLIENT_CODE
|
||||
if (clientCode.isNullOrBlank()) return@launch
|
||||
|
||||
val allAnswersForClient = MyApp.database.answerDao().getAnswersForClient(clientCode)
|
||||
|
||||
// Bereite ein Map von questionId -> answerValue vor für schnellen Zugriff
|
||||
val answerMap = allAnswersForClient.associateBy({ it.questionId }, { it.answerValue })
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
// tableLayout: header ist bei index 0, symptom-rows ab index 1 in der gleichen Reihenfolge wie question.symptoms
|
||||
for ((index, symptomKey) in question.symptoms.withIndex()) {
|
||||
val answerMapKey = questionnaireMeta + "-" + symptomKey
|
||||
val dbAnswerRaw = answerMap[answerMapKey]
|
||||
val dbAnswer = dbAnswerRaw?.takeIf { it.isNotBlank() }?.trim()
|
||||
|
||||
// nur wenn noch nichts im answers-Map steht und DB-Antwort vorhanden
|
||||
if (!answers.containsKey(symptomKey) && !dbAnswer.isNullOrBlank()) {
|
||||
// finde die entsprechende TableRow (header = 0, erste symptom row = 1)
|
||||
val rowIndex = index + 1
|
||||
if (rowIndex < tableLayout.childCount) {
|
||||
val row = tableLayout.getChildAt(rowIndex) as? TableRow ?: continue
|
||||
val radioGroup = row.getChildAt(1) as? RadioGroup ?: continue
|
||||
|
||||
// Suche RadioButton mit tag == dbAnswer und markiere ihn
|
||||
for (i in 0 until radioGroup.childCount) {
|
||||
val rb = radioGroup.getChildAt(i) as? RadioButton ?: continue
|
||||
if ((rb.tag as? String)?.trim() == dbAnswer) {
|
||||
rb.isChecked = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// answers und points aktualisieren
|
||||
answers[symptomKey] = dbAnswer
|
||||
val point = pointsMap[dbAnswer] ?: 0
|
||||
points.add(point)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout.findViewById<Button>(R.id.Qnext).setOnClickListener {
|
||||
if (validate()) {
|
||||
saveAnswer()
|
||||
@ -138,10 +197,10 @@ class HandlerGlassScaleQuestion(
|
||||
}
|
||||
|
||||
override fun saveAnswer() {
|
||||
// Vorherige Punkte dieser Frage entfernen
|
||||
// Vorherige Punkte dieser Frage entfernen (falls vorhanden)
|
||||
question.symptoms.forEach {
|
||||
val previousLabel = answers[it] as? String
|
||||
val previousPoint = pointsMap[previousLabel]
|
||||
val previousPoint = previousLabel?.let { lbl -> pointsMap[lbl] }
|
||||
if (previousPoint != null) {
|
||||
points.remove(previousPoint)
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package com.dano.test1
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.widget.*
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
class HandlerMultiCheckboxQuestion(
|
||||
private val context: Context,
|
||||
@ -11,7 +12,8 @@ class HandlerMultiCheckboxQuestion(
|
||||
private val languageID: String,
|
||||
private val goToNextQuestion: () -> Unit,
|
||||
private val goToPreviousQuestion: () -> Unit,
|
||||
private val showToast: (String) -> Unit
|
||||
private val showToast: (String) -> Unit,
|
||||
private val questionnaireMeta: String // neu: für DB-ID wie bei den anderen Handlern
|
||||
) : QuestionHandler {
|
||||
|
||||
private lateinit var layout: View
|
||||
@ -25,12 +27,12 @@ class HandlerMultiCheckboxQuestion(
|
||||
val questionTitle = layout.findViewById<TextView>(R.id.question)
|
||||
val questionTextView = layout.findViewById<TextView>(R.id.textView)
|
||||
|
||||
// Hier jetzt identisch zur RadioQuestion:
|
||||
questionTextView.text = this.question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
|
||||
questionTitle.text = this.question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
|
||||
|
||||
container.removeAllViews()
|
||||
|
||||
// bestehende Auswahl aus answers (falls vorhanden) als Set
|
||||
val selectedKeys = this.question.question?.let {
|
||||
(answers[it] as? List<*>)?.map { it.toString() }?.toSet()
|
||||
} ?: emptySet()
|
||||
@ -52,6 +54,44 @@ class HandlerMultiCheckboxQuestion(
|
||||
container.addView(checkBox)
|
||||
}
|
||||
|
||||
// --- DB-Abfrage falls noch kein Eintrag im answers-Map existiert ---
|
||||
val answerMapKey = question.question ?: (question.id ?: "")
|
||||
if (answerMapKey.isNotBlank() && !answers.containsKey(answerMapKey)) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val clientCode = GlobalValues.LAST_CLIENT_CODE
|
||||
if (clientCode.isNullOrBlank()) return@launch
|
||||
|
||||
val allAnswersForClient = MyApp.database.answerDao().getAnswersForClient(clientCode)
|
||||
val myQuestionId = questionnaireMeta + "-" + question.question
|
||||
val dbAnswer = allAnswersForClient.find { it.questionId == myQuestionId }?.answerValue
|
||||
|
||||
if (!dbAnswer.isNullOrBlank()) {
|
||||
val parsed = parseMultiAnswer(dbAnswer)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
// UI: Checkboxen setzen
|
||||
for (i in 0 until container.childCount) {
|
||||
val cb = container.getChildAt(i) as? CheckBox ?: continue
|
||||
cb.isChecked = parsed.contains(cb.tag.toString())
|
||||
}
|
||||
|
||||
// answers-Map aktualisieren (Liste)
|
||||
answers[answerMapKey] = parsed.toList()
|
||||
|
||||
// Punkte berechnen und hinzufügen
|
||||
val totalPoints = parsed.sumOf { key ->
|
||||
question.pointsMap?.get(key) ?: 0
|
||||
}
|
||||
points.add(totalPoints)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout.findViewById<Button>(R.id.Qnext).setOnClickListener {
|
||||
if (validate()) {
|
||||
saveAnswer()
|
||||
@ -97,8 +137,54 @@ class HandlerMultiCheckboxQuestion(
|
||||
}
|
||||
|
||||
question.question?.let { questionKey ->
|
||||
// entferne ggf. vorherige Punkte-Einträge für diese Frage, falls die answers-Map vorher schon einen Wert hatte.
|
||||
val oldList = answers[questionKey] as? List<*>
|
||||
if (oldList != null) {
|
||||
val oldTotal = oldList.mapNotNull { it?.toString() }.sumOf { oldKey ->
|
||||
question.pointsMap?.get(oldKey) ?: 0
|
||||
}
|
||||
// Entfernen eines einzelnen Int-Wertes aus points (falls vorhanden)
|
||||
points.remove(oldTotal)
|
||||
}
|
||||
|
||||
answers[questionKey] = selectedKeys
|
||||
points.add(totalPoints)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsen der DB-Antwort in ein Set von Keys. Unterstützt:
|
||||
* - JSON-Array: ["a","b"]
|
||||
* - kommasepariert: a,b
|
||||
* - semikolon-separiert: a;b
|
||||
* - einzelner Wert: a
|
||||
*/
|
||||
private fun parseMultiAnswer(dbAnswer: String): Set<String> {
|
||||
val trimmed = dbAnswer.trim()
|
||||
// JSON-Array-like
|
||||
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
||||
val inner = trimmed.substring(1, trimmed.length - 1)
|
||||
if (inner.isBlank()) return emptySet()
|
||||
return inner.split(",")
|
||||
.map { it.trim().trim('"', '\'') }
|
||||
.filter { it.isNotEmpty() }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
// If contains comma or semicolon
|
||||
val separator = when {
|
||||
trimmed.contains(",") -> ","
|
||||
trimmed.contains(";") -> ";"
|
||||
else -> null
|
||||
}
|
||||
|
||||
return if (separator != null) {
|
||||
trimmed.split(separator)
|
||||
.map { it.trim().trim('"', '\'') }
|
||||
.filter { it.isNotEmpty() }
|
||||
.toSet()
|
||||
} else {
|
||||
setOf(trimmed.trim().trim('"', '\''))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package com.dano.test1
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.widget.*
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
class HandlerValueSpinner(
|
||||
private val context: Context,
|
||||
@ -11,7 +12,8 @@ class HandlerValueSpinner(
|
||||
private val goToNextQuestion: () -> Unit,
|
||||
private val goToPreviousQuestion: () -> Unit,
|
||||
private val goToQuestionById: (String) -> Unit,
|
||||
private val showToast: (String) -> Unit
|
||||
private val showToast: (String) -> Unit,
|
||||
private val questionnaireMeta: String // neu: für die DB-Abfrage
|
||||
) : QuestionHandler {
|
||||
|
||||
private lateinit var layout: View
|
||||
@ -40,6 +42,31 @@ class HandlerValueSpinner(
|
||||
val savedValue = question.question?.let { answers[it] as? String }
|
||||
setupSpinner(spinner, spinnerItems, savedValue)
|
||||
|
||||
// --- DB-Abfrage falls noch keine Antwort im Map existiert ---
|
||||
val answerMapKey = question.question ?: (question.id ?: "")
|
||||
if (answerMapKey.isNotBlank() && !answers.containsKey(answerMapKey)) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val clientCode = GlobalValues.LAST_CLIENT_CODE
|
||||
if (clientCode.isNullOrBlank()) return@launch
|
||||
|
||||
val allAnswersForClient = MyApp.database.answerDao().getAnswersForClient(clientCode)
|
||||
val myQuestionId = questionnaireMeta + "-" + question.question
|
||||
val dbAnswer = allAnswersForClient.find { it.questionId == myQuestionId }?.answerValue
|
||||
|
||||
if (!dbAnswer.isNullOrBlank()) {
|
||||
withContext(Dispatchers.Main) {
|
||||
answers[answerMapKey] = dbAnswer
|
||||
val index = spinnerItems.indexOf(dbAnswer)
|
||||
if (index >= 0) spinner.setSelection(index)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layout.findViewById<Button>(R.id.Qnext).setOnClickListener {
|
||||
if (validate()) {
|
||||
saveAnswer()
|
||||
|
||||
@ -157,12 +157,12 @@ abstract class QuestionnaireBase<T> {
|
||||
return when (question) {
|
||||
is QuestionItem.RadioQuestion -> HandlerRadioQuestion(context, answers, points, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::goToQuestionById, ::showToast, questionnaireMeta.id)
|
||||
is QuestionItem.ClientCoachCodeQuestion -> HandlerClientCoachCode(answers, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast)
|
||||
is QuestionItem.DateSpinnerQuestion -> HandlerDateSpinner(context, answers, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast)
|
||||
is QuestionItem.ValueSpinnerQuestion -> HandlerValueSpinner(context, answers, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::goToQuestionById, ::showToast)
|
||||
is QuestionItem.GlassScaleQuestion -> HandlerGlassScaleQuestion(context, answers, points, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast)
|
||||
is QuestionItem.DateSpinnerQuestion -> HandlerDateSpinner(context, answers, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast, questionnaireMeta.id)
|
||||
is QuestionItem.ValueSpinnerQuestion -> HandlerValueSpinner(context, answers, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::goToQuestionById, ::showToast, questionnaireMeta.id)
|
||||
is QuestionItem.GlassScaleQuestion -> HandlerGlassScaleQuestion(context, answers, points, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast, questionnaireMeta.id)
|
||||
is QuestionItem.ClientNotSigned -> HandlerClientNotSigned(answers, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast)
|
||||
is QuestionItem.StringSpinnerQuestion -> HandlerStringSpinner(context, answers, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast, questionnaireMeta.id)
|
||||
is QuestionItem.MultiCheckboxQuestion -> HandlerMultiCheckboxQuestion(context, answers, points, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast)
|
||||
is QuestionItem.MultiCheckboxQuestion -> HandlerMultiCheckboxQuestion(context, answers, points, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast, questionnaireMeta.id)
|
||||
is QuestionItem.LastPage -> HandlerLastPage(
|
||||
answers, languageID, ::goToNextQuestion, ::goToPreviousQuestion
|
||||
) { CoroutineScope(Dispatchers.IO).launch { saveAnswersToDatabase(answers, questionnaireMeta.id) } }
|
||||
|
||||
Reference in New Issue
Block a user