fixed wrong loading bug, fixed editing bug

This commit is contained in:
oxidiert
2025-09-08 10:27:23 +02:00
parent 45deee664b
commit 650a3bb050
4 changed files with 93 additions and 77 deletions

View File

@ -14,7 +14,9 @@ class EditButtonHandler(
private val questionnaireFiles: Map<Button, String>,
private val buttonPoints: MutableMap<String, Int>,
private val updateButtonTexts: () -> Unit,
private val setButtonsEnabled: (List<Button>) -> Unit
private val setButtonsEnabled: (List<Button>) -> Unit,
// vor "Bearbeiten" ggf. Laden anstoßen
private val triggerLoad: () -> Unit
) {
fun setup() {
@ -23,18 +25,38 @@ class EditButtonHandler(
}
private fun handleEditButtonClick() {
val clientCode = editText.text.toString().trim()
if (clientCode.isBlank()) {
val typed = editText.text.toString().trim()
val desiredCode = when {
typed.isNotBlank() -> typed
!GlobalValues.LOADED_CLIENT_CODE.isNullOrBlank() -> GlobalValues.LOADED_CLIENT_CODE!!
else -> ""
}
if (desiredCode.isBlank()) {
val message = LanguageManager.getText(languageIDProvider(), "please_client_code")
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
return
}
GlobalValues.LAST_CLIENT_CODE = clientCode
// Nutzerwunsch merken (info)
GlobalValues.LAST_CLIENT_CODE = desiredCode
// Nur laden, wenn noch nicht/anders geladen
val needLoad = GlobalValues.LOADED_CLIENT_CODE?.equals(desiredCode) != true
if (needLoad) triggerLoad()
CoroutineScope(Dispatchers.IO).launch {
val loadedOk = waitUntilClientLoaded(desiredCode, timeoutMs = 2500, stepMs = 50)
if (!loadedOk) {
withContext(Dispatchers.Main) {
Toast.makeText(activity, "Bitte den Klienten über \"Laden\" öffnen.", Toast.LENGTH_LONG).show()
}
return@launch
}
// Ab hier: geladen → Bearbeiten-Logik
val completedEntries: List<CompletedQuestionnaire> =
MyApp.database.completedQuestionnaireDao().getAllForClient(clientCode)
MyApp.database.completedQuestionnaireDao().getAllForClient(desiredCode)
val completedFiles = completedEntries
.filter { it.isDone }
@ -58,4 +80,17 @@ class EditButtonHandler(
}
}
}
private suspend fun waitUntilClientLoaded(expectedCode: String, timeoutMs: Long, stepMs: Long): Boolean {
// sofort ok, wenn bereits korrekt geladen
if (GlobalValues.LOADED_CLIENT_CODE?.equals(expectedCode) == true) return true
var waited = 0L
while (waited < timeoutMs) {
delay(stepMs)
waited += stepMs
if (GlobalValues.LOADED_CLIENT_CODE?.equals(expectedCode) == true) return true
}
return GlobalValues.LOADED_CLIENT_CODE?.equals(expectedCode) == true
}
}

View File

@ -26,50 +26,39 @@ class HandlerClientCoachCode(
this.layout = layout
this.question = question
// Bind UI components
val clientCodeField = layout.findViewById<EditText>(R.id.client_code)
val coachCodeField = layout.findViewById<EditText>(R.id.coach_code)
val questionTextView = layout.findViewById<TextView>(R.id.question)
val titleTextView = layout.findViewById<TextView>(R.id.textView)
// Fill question text using language manager
questionTextView.text = question.question?.let {
LanguageManager.getText(languageID, it)
} ?: ""
questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
// --- Schriftgrößen prozentual zur Bildschirmhöhe setzen ---
// Passe die Prozente bei Bedarf an:
setTextSizePercentOfScreenHeight(titleTextView, 0.03f) // 5.5% der Bildschirmhöhe
setTextSizePercentOfScreenHeight(questionTextView,0.03f) // 5.0% der Bildschirmhöhe
setTextSizePercentOfScreenHeight(clientCodeField, 0.025f) // 3.5% der Bildschirmhöhe
setTextSizePercentOfScreenHeight(coachCodeField, 0.025f) // anpassen nach Geschmack
// ----------------------------------------------------------
setTextSizePercentOfScreenHeight(titleTextView, 0.03f)
setTextSizePercentOfScreenHeight(questionTextView,0.03f)
setTextSizePercentOfScreenHeight(clientCodeField, 0.025f)
setTextSizePercentOfScreenHeight(coachCodeField, 0.025f)
// Load last used client code if available
val lastClientCode = GlobalValues.LAST_CLIENT_CODE
if (!lastClientCode.isNullOrBlank()) {
clientCodeField.setText(lastClientCode)
// *** WICHTIG: Nur den ERFOLGREICH GELADENEN Code verwenden ***
val loadedClientCode = GlobalValues.LOADED_CLIENT_CODE
if (!loadedClientCode.isNullOrBlank()) {
clientCodeField.setText(loadedClientCode)
clientCodeField.isEnabled = false
} else {
clientCodeField.setText(answers["client_code"] as? String ?: "")
// Nichts ist geladen → Feld bleibt leer und editierbar
clientCodeField.setText("")
clientCodeField.isEnabled = true
}
// Load saved coach code
coachCodeField.setText(answers["coach_code"] as? String ?: "")
// Set click listener for Next button
layout.findViewById<Button>(R.id.Qnext).setOnClickListener {
onNextClicked(clientCodeField, coachCodeField)
}
// Set click listener for Previous button
layout.findViewById<Button>(R.id.Qprev).setOnClickListener {
onPreviousClicked(clientCodeField, coachCodeField)
}
}
// Deaktiviert AutoSize und setzt textSize in sp prozentual zur Bildschirmhöhe
private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) {
val dm = layout.resources.displayMetrics
val sp = (dm.heightPixels * percentOfHeight) / dm.scaledDensity
@ -77,8 +66,10 @@ class HandlerClientCoachCode(
view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp)
}
// Handle Next button click
private fun onNextClicked(clientCodeField: EditText, coachCodeField: EditText) {
// 1) Ohne vorher geladenen Client NICHT weiter
val loadedClientCode = GlobalValues.LOADED_CLIENT_CODE
if (!validate()) {
val message = LanguageManager.getText(languageID, "fill_both_fields")
showToast(message)
@ -92,21 +83,18 @@ class HandlerClientCoachCode(
val dbPath = layout.context.getDatabasePath("questionnaire_database")
val dbExistedBefore = dbPath.exists()
// Check if client code already exists asynchronously
CoroutineScope(Dispatchers.IO).launch {
val existingClient = MyApp.database.clientDao().getClientByCode(clientCode)
withContext(Dispatchers.Main) {
// Wenn Feld editierbar war und Code bereits existiert → Hinweis
if (existingClient != null && clientCodeField.isEnabled) {
// Client code already exists and field was editable
val message = LanguageManager.getText(languageID, "client_code_exists")
showToast(message)
} else {
// Either no existing client or re-using previous code
saveAnswers(clientCode, coachCode)
goToNextQuestion()
// Lösche DB-Dateien nur, wenn sie vorher nicht existierten
if (!dbExistedBefore) {
MyApp.database.close()
dbPath.delete()
@ -118,7 +106,6 @@ class HandlerClientCoachCode(
}
}
// Handle Previous button click
private fun onPreviousClicked(clientCodeField: EditText, coachCodeField: EditText) {
val clientCode = clientCodeField.text.toString()
val coachCode = coachCodeField.text.toString()
@ -126,21 +113,19 @@ class HandlerClientCoachCode(
goToPreviousQuestion()
}
// Validate that both fields are filled
override fun validate(): Boolean {
val clientCode = layout.findViewById<EditText>(R.id.client_code).text
val coachCode = layout.findViewById<EditText>(R.id.coach_code).text
return clientCode.isNotBlank() && coachCode.isNotBlank()
}
// Save answers to shared state and global value
private fun saveAnswers(clientCode: String, coachCode: String) {
// Optional: LAST_CLIENT_CODE kann gesetzt bleiben; maßgeblich ist LOADED_CLIENT_CODE
GlobalValues.LAST_CLIENT_CODE = clientCode
answers["client_code"] = clientCode
answers["coach_code"] = coachCode
}
// Required override but not used here
override fun saveAnswer() {
// Not used
}

View File

@ -48,7 +48,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
val pathExists = File(dbPath).exists()
updateMainButtonsState(pathExists)
if (!editText.text.isNullOrBlank()) {
if (pathExists && !editText.text.isNullOrBlank()) {
buttonLoad.performClick()
}
}
@ -154,21 +154,26 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
questionnaireFiles[button] = entry.file
}
updateButtonTexts()
val alwaysButtons = questionnaireEntries.mapIndexedNotNull { idx, entry ->
val btn = dynamicButtons.getOrNull(idx)
if (entry.condition is QuestionItem.Condition.AlwaysAvailable) btn else null
}
setButtonsEnabled(alwaysButtons)
dynamicButtons.forEach { button ->
button.setOnClickListener {
val clientCode = editText.text.toString().trim()
GlobalValues.LAST_CLIENT_CODE = clientCode
// Optional: LAST_CLIENT_CODE synchronisieren (rein informativ)
GlobalValues.LAST_CLIENT_CODE = GlobalValues.LOADED_CLIENT_CODE
startQuestionnaireForButton(button)
setButtonsEnabled(dynamicButtons.filter { it == button })
}
}
}
private fun restorePreviousClientCode() {
GlobalValues.LAST_CLIENT_CODE?.let { editText.setText(it) }
}
@ -276,10 +281,13 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
questionnaireFiles = questionnaireFiles,
buttonPoints = buttonPoints,
updateButtonTexts = { updateButtonTexts() },
setButtonsEnabled = { setButtonsEnabled(it) }
setButtonsEnabled = { setButtonsEnabled(it) },
// Vor "Bearbeiten" ggf. den Load-Button ausführen
triggerLoad = { buttonLoad.performClick() }
).setup()
}
private fun setupUploadButton() {
uploadButton.text = "Upload"
uploadButton.setOnClickListener {

View File

@ -42,10 +42,11 @@ class LoadButtonHandler(
CoroutineScope(Dispatchers.IO).launch {
val client = MyApp.database.clientDao().getClientByCode(clientCode)
if (client == null) {
// Kein Profil → als NICHT geladen markieren
GlobalValues.LOADED_CLIENT_CODE = null
withContext(Dispatchers.Main) {
val message = LanguageManager.getText(languageIDProvider(), "no_profile")
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
// enable only alwaysAvailable ones if no client found
val questionnaireEntries = questionnaireEntriesProvider()
val alwaysButtons = questionnaireEntries.mapIndexedNotNull { idx, entry ->
val btn = dynamicButtonsProvider().getOrNull(idx)
@ -56,27 +57,28 @@ class LoadButtonHandler(
return@launch
}
// Profil gefunden → als geladen markieren
GlobalValues.LOADED_CLIENT_CODE = clientCode
withContext(Dispatchers.Main) {
updateMainButtonsState(true) // Datenbank vorhanden -> Buttons aktivieren
updateMainButtonsState(true)
}
handleNormalLoad(clientCode)
}
}
// Evaluierung der Bedingung: suspend, weil DB-Abfragen stattfinden.
private suspend fun evaluateCondition(
condition: QuestionItem.Condition?,
clientCode: String,
completedEntries: List<CompletedQuestionnaire>
): Boolean {
if (condition == null) return false
when (condition) {
is QuestionItem.Condition.AlwaysAvailable -> return true
return when (condition) {
is QuestionItem.Condition.AlwaysAvailable -> true
is QuestionItem.Condition.RequiresCompleted -> {
val normalizedCompleted = completedEntries.map { normalizeQuestionnaireId(it.questionnaireId) }
return condition.required.all { req ->
condition.required.all { req ->
val nReq = normalizeQuestionnaireId(req)
normalizedCompleted.any { it.contains(nReq) || nReq.contains(it) }
}
@ -85,48 +87,37 @@ class LoadButtonHandler(
val answers = MyApp.database.answerDao().getAnswersForClientAndQuestionnaire(clientCode, condition.questionnaire)
val relevant = answers.find { it.questionId.endsWith(condition.questionId, ignoreCase = true) }
val answerValue = relevant?.answerValue ?: ""
return when (condition.operator) {
when (condition.operator) {
"==" -> answerValue == condition.value
"!=" -> answerValue != condition.value
else -> false
}
}
is QuestionItem.Condition.Combined -> {
val reqOk = if (condition.requiresCompleted.isNullOrEmpty()) true
else {
val normalizedCompleted = completedEntries.map { normalizeQuestionnaireId(it.questionnaireId) }
condition.requiresCompleted.all { req ->
val nReq = normalizeQuestionnaireId(req)
normalizedCompleted.any { it.contains(nReq) || nReq.contains(it) }
}
val normalizedCompleted = completedEntries.map { normalizeQuestionnaireId(it.questionnaireId) }
val reqOk = condition.requiresCompleted.isNullOrEmpty() || condition.requiresCompleted.all { req ->
val nReq = normalizeQuestionnaireId(req)
normalizedCompleted.any { it.contains(nReq) || nReq.contains(it) }
}
if (!reqOk) return false
val q = condition.questionCheck
if (q != null) {
val answers = MyApp.database.answerDao().getAnswersForClientAndQuestionnaire(clientCode, q.questionnaire)
val relevant = answers.find { it.questionId.endsWith(q.questionId, ignoreCase = true) }
val answerValue = relevant?.answerValue ?: ""
return when (q.operator) {
"==" -> answerValue == q.value
"!=" -> answerValue != q.value
else -> false
}
val q = condition.questionCheck ?: return true
val answers = MyApp.database.answerDao().getAnswersForClientAndQuestionnaire(clientCode, q.questionnaire)
val relevant = answers.find { it.questionId.endsWith(q.questionId, ignoreCase = true) }
val answerValue = relevant?.answerValue ?: ""
when (q.operator) {
"==" -> answerValue == q.value
"!=" -> answerValue != q.value
else -> false
}
return reqOk
}
is QuestionItem.Condition.AnyOf -> {
for (sub in condition.conditions) {
val subRes = evaluateCondition(sub, clientCode, completedEntries)
if (subRes) return true
}
return false
condition.conditions.any { evaluateCondition(it, clientCode, completedEntries) }
}
}
}
private fun normalizeQuestionnaireId(name: String): String {
return name.lowercase().removeSuffix(".json")
}
private fun normalizeQuestionnaireId(name: String): String =
name.lowercase().removeSuffix(".json")
private suspend fun handleNormalLoad(clientCode: String) {
val completedEntries = withContext(Dispatchers.IO) {
@ -147,7 +138,6 @@ class LoadButtonHandler(
updateButtonTexts()
}
// für jeden Fragebogen prüfen, ob er aktiv sein darf
val enabledButtons = mutableListOf<Button>()
val questionnaireEntries = questionnaireEntriesProvider()
val dynamicButtons = dynamicButtonsProvider()
@ -155,7 +145,6 @@ class LoadButtonHandler(
for ((idx, entry) in questionnaireEntries.withIndex()) {
val button = dynamicButtons.getOrNull(idx) ?: continue
// falls bereits erledigt: nicht anklickbar
val isCompleted = completedEntries.any { completed ->
normalizeQuestionnaireId(completed.questionnaireId).let { completedNorm ->
val targetNorm = normalizeQuestionnaireId(entry.file)
@ -164,7 +153,6 @@ class LoadButtonHandler(
}
if (isCompleted) continue
// auswerten der Bedingung (suspend)
val condMet = evaluateCondition(entry.condition, clientCode, completedEntries)
if (condMet) enabledButtons.add(button)
}