outsourcing functions to new classes
This commit is contained in:
61
app/src/main/java/com/dano/test1/EditButtonHandler.kt
Normal file
61
app/src/main/java/com/dano/test1/EditButtonHandler.kt
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package com.dano.test1
|
||||||
|
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.Toast
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import com.dano.test1.data.CompletedQuestionnaire
|
||||||
|
|
||||||
|
class EditButtonHandler(
|
||||||
|
private val activity: MainActivity,
|
||||||
|
private val editButton: Button,
|
||||||
|
private val editText: EditText,
|
||||||
|
private val languageIDProvider: () -> String,
|
||||||
|
private val questionnaireFiles: Map<Button, String>,
|
||||||
|
private val buttonPoints: MutableMap<String, Int>,
|
||||||
|
private val updateButtonTexts: () -> Unit,
|
||||||
|
private val setButtonsEnabled: (List<Button>) -> Unit
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun setup() {
|
||||||
|
editButton.text = LanguageManager.getText(languageIDProvider(), "edit")
|
||||||
|
editButton.setOnClickListener { handleEditButtonClick() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleEditButtonClick() {
|
||||||
|
val clientCode = editText.text.toString().trim()
|
||||||
|
if (clientCode.isBlank()) {
|
||||||
|
val message = LanguageManager.getText(languageIDProvider(), "please_client_code")
|
||||||
|
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalValues.LAST_CLIENT_CODE = clientCode
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val completedEntries: List<CompletedQuestionnaire> =
|
||||||
|
MyApp.database.completedQuestionnaireDao().getAllForClient(clientCode)
|
||||||
|
|
||||||
|
val completedFiles = completedEntries
|
||||||
|
.filter { it.isDone }
|
||||||
|
.map { it.questionnaireId.lowercase() }
|
||||||
|
|
||||||
|
buttonPoints.clear()
|
||||||
|
for (entry in completedEntries) {
|
||||||
|
if (entry.isDone) {
|
||||||
|
buttonPoints[entry.questionnaireId] = entry.sumPoints ?: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
updateButtonTexts()
|
||||||
|
|
||||||
|
val enabledButtons = questionnaireFiles.filter { (_, fileName) ->
|
||||||
|
completedFiles.any { completedId -> fileName.lowercase().contains(completedId) }
|
||||||
|
}.keys.toList()
|
||||||
|
|
||||||
|
setButtonsEnabled(enabledButtons)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,16 +1,11 @@
|
|||||||
package com.dano.test1
|
package com.dano.test1
|
||||||
|
|
||||||
import android.graphics.Canvas
|
|
||||||
import android.graphics.Paint
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.pdf.PdfDocument
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import android.util.Log
|
|
||||||
import com.dano.test1.data.CompletedQuestionnaire
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
var RHS_POINTS: Int? = null
|
var RHS_POINTS: Int? = null
|
||||||
@ -45,25 +40,19 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
|||||||
setupLanguageSpinner()
|
setupLanguageSpinner()
|
||||||
setupLoadButton()
|
setupLoadButton()
|
||||||
setupSaveButton()
|
setupSaveButton()
|
||||||
setupEditButton()
|
setupEditButtonHandler()
|
||||||
setupUploadButton()
|
setupUploadButton()
|
||||||
setupDownloadButton()
|
setupDownloadButton()
|
||||||
|
|
||||||
val dbPath = "/data/data/com.dano.test1/databases/questionnaire_database"
|
val dbPath = "/data/data/com.dano.test1/databases/questionnaire_database"
|
||||||
val pathExists = File(dbPath).exists()
|
val pathExists = File(dbPath).exists()
|
||||||
if (pathExists) {
|
updateMainButtonsState(pathExists)
|
||||||
updateMainButtonsState(true)
|
|
||||||
} else {
|
|
||||||
updateMainButtonsState(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!editText.text.isNullOrBlank()) {
|
if (!editText.text.isNullOrBlank()) {
|
||||||
buttonLoad.performClick()
|
buttonLoad.performClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun bindViews() {
|
private fun bindViews() {
|
||||||
editText = activity.findViewById(R.id.editText)
|
editText = activity.findViewById(R.id.editText)
|
||||||
spinner = activity.findViewById(R.id.string_spinner1)
|
spinner = activity.findViewById(R.id.string_spinner1)
|
||||||
@ -104,8 +93,6 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
|||||||
// Parser: erzeugt ein QuestionItem.Condition? (sehr robust gegenüber verschiedenen JSON-Formaten)
|
// Parser: erzeugt ein QuestionItem.Condition? (sehr robust gegenüber verschiedenen JSON-Formaten)
|
||||||
private fun parseCondition(conditionObj: JSONObject?): QuestionItem.Condition? {
|
private fun parseCondition(conditionObj: JSONObject?): QuestionItem.Condition? {
|
||||||
if (conditionObj == null) return null
|
if (conditionObj == null) return null
|
||||||
|
|
||||||
// anyOf
|
|
||||||
if (conditionObj.has("anyOf")) {
|
if (conditionObj.has("anyOf")) {
|
||||||
val arr = conditionObj.optJSONArray("anyOf") ?: JSONArray()
|
val arr = conditionObj.optJSONArray("anyOf") ?: JSONArray()
|
||||||
val conditions = mutableListOf<QuestionItem.Condition>()
|
val conditions = mutableListOf<QuestionItem.Condition>()
|
||||||
@ -115,14 +102,10 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
|||||||
}
|
}
|
||||||
return QuestionItem.Condition.AnyOf(conditions)
|
return QuestionItem.Condition.AnyOf(conditions)
|
||||||
}
|
}
|
||||||
|
|
||||||
// alwaysAvailable
|
|
||||||
if (conditionObj.has("alwaysAvailable")) {
|
if (conditionObj.has("alwaysAvailable")) {
|
||||||
val flag = conditionObj.optBoolean("alwaysAvailable", false)
|
val flag = conditionObj.optBoolean("alwaysAvailable", false)
|
||||||
if (flag) return QuestionItem.Condition.AlwaysAvailable
|
if (flag) return QuestionItem.Condition.AlwaysAvailable
|
||||||
}
|
}
|
||||||
|
|
||||||
// requiresCompleted (array)
|
|
||||||
val requiresList = mutableListOf<String>()
|
val requiresList = mutableListOf<String>()
|
||||||
if (conditionObj.has("requiresCompleted")) {
|
if (conditionObj.has("requiresCompleted")) {
|
||||||
val reqArr = conditionObj.optJSONArray("requiresCompleted")
|
val reqArr = conditionObj.optJSONArray("requiresCompleted")
|
||||||
@ -131,19 +114,14 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
|||||||
requiresList.add(reqArr.optString(i))
|
requiresList.add(reqArr.optString(i))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// sometimes it's a single string
|
|
||||||
conditionObj.optString("requiresCompleted")?.let { if (it.isNotBlank()) requiresList.add(it) }
|
conditionObj.optString("requiresCompleted")?.let { if (it.isNotBlank()) requiresList.add(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// question-check fields
|
|
||||||
val questionnaire = conditionObj.optString("questionnaire", null)
|
val questionnaire = conditionObj.optString("questionnaire", null)
|
||||||
val questionId = conditionObj.optString("questionId", null)
|
val questionId = conditionObj.optString("questionId", null)
|
||||||
val operator = conditionObj.optString("operator", null)
|
val operator = conditionObj.optString("operator", null)
|
||||||
val value = conditionObj.optString("value", null)
|
val value = conditionObj.optString("value", null)
|
||||||
|
|
||||||
val hasQuestionCheck = !questionnaire.isNullOrBlank() && !questionId.isNullOrBlank() && !operator.isNullOrBlank() && value != null
|
val hasQuestionCheck = !questionnaire.isNullOrBlank() && !questionId.isNullOrBlank() && !operator.isNullOrBlank() && value != null
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
requiresList.isNotEmpty() && hasQuestionCheck -> {
|
requiresList.isNotEmpty() && hasQuestionCheck -> {
|
||||||
QuestionItem.Condition.Combined(requiresList, QuestionItem.Condition.QuestionCondition(questionnaire!!, questionId!!, operator!!, value!!))
|
QuestionItem.Condition.Combined(requiresList, QuestionItem.Condition.QuestionCondition(questionnaire!!, questionId!!, operator!!, value!!))
|
||||||
@ -162,7 +140,6 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
|||||||
buttonContainer.removeAllViews()
|
buttonContainer.removeAllViews()
|
||||||
dynamicButtons.clear()
|
dynamicButtons.clear()
|
||||||
questionnaireFiles.clear()
|
questionnaireFiles.clear()
|
||||||
|
|
||||||
for ((index, entry) in questionnaireEntries.withIndex()) {
|
for ((index, entry) in questionnaireEntries.withIndex()) {
|
||||||
val button = Button(activity).apply {
|
val button = Button(activity).apply {
|
||||||
layoutParams = LinearLayout.LayoutParams(
|
layoutParams = LinearLayout.LayoutParams(
|
||||||
@ -176,24 +153,17 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
|||||||
dynamicButtons.add(button)
|
dynamicButtons.add(button)
|
||||||
questionnaireFiles[button] = entry.file
|
questionnaireFiles[button] = entry.file
|
||||||
}
|
}
|
||||||
|
|
||||||
updateButtonTexts()
|
updateButtonTexts()
|
||||||
|
|
||||||
// Initial: enable those with AlwaysAvailable (falls vorhanden)
|
|
||||||
val alwaysButtons = questionnaireEntries.mapIndexedNotNull { idx, entry ->
|
val alwaysButtons = questionnaireEntries.mapIndexedNotNull { idx, entry ->
|
||||||
val btn = dynamicButtons.getOrNull(idx)
|
val btn = dynamicButtons.getOrNull(idx)
|
||||||
if (entry.condition is QuestionItem.Condition.AlwaysAvailable) btn else null
|
if (entry.condition is QuestionItem.Condition.AlwaysAvailable) btn else null
|
||||||
}
|
}
|
||||||
setButtonsEnabled(alwaysButtons)
|
setButtonsEnabled(alwaysButtons)
|
||||||
|
|
||||||
dynamicButtons.forEach { button ->
|
dynamicButtons.forEach { button ->
|
||||||
button.setOnClickListener {
|
button.setOnClickListener {
|
||||||
// require a client code to start actual questionnaire (sichere Kontrolle)
|
|
||||||
val clientCode = editText.text.toString().trim()
|
val clientCode = editText.text.toString().trim()
|
||||||
|
|
||||||
GlobalValues.LAST_CLIENT_CODE = clientCode
|
GlobalValues.LAST_CLIENT_CODE = clientCode
|
||||||
startQuestionnaireForButton(button)
|
startQuestionnaireForButton(button)
|
||||||
// disable other buttons while one questionnaire is open
|
|
||||||
setButtonsEnabled(dynamicButtons.filter { it == button })
|
setButtonsEnabled(dynamicButtons.filter { it == button })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,7 +180,6 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
|||||||
}
|
}
|
||||||
spinner.adapter = adapter
|
spinner.adapter = adapter
|
||||||
spinner.setSelection(languages.indexOf(languageID))
|
spinner.setSelection(languages.indexOf(languageID))
|
||||||
|
|
||||||
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
|
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
|
||||||
languageID = languages[position]
|
languageID = languages[position]
|
||||||
@ -218,175 +187,23 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
|||||||
val hintTag = editText.tag as? String ?: ""
|
val hintTag = editText.tag as? String ?: ""
|
||||||
editText.hint = LanguageManager.getText(languageID, hintTag)
|
editText.hint = LanguageManager.getText(languageID, hintTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onNothingSelected(parent: AdapterView<*>) {}
|
override fun onNothingSelected(parent: AdapterView<*>) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupLoadButton() {
|
private fun setupLoadButton() {
|
||||||
buttonLoad.text = LanguageManager.getText(languageID, "load")
|
LoadButtonHandler(
|
||||||
buttonLoad.setOnClickListener { handleLoadButton() }
|
activity = activity,
|
||||||
}
|
loadButton = buttonLoad,
|
||||||
|
editText = editText,
|
||||||
private fun handleLoadButton() {
|
languageIDProvider = { languageID },
|
||||||
buttonPoints.clear()
|
questionnaireEntriesProvider = { questionnaireEntries },
|
||||||
updateButtonTexts()
|
dynamicButtonsProvider = { dynamicButtons },
|
||||||
setButtonsEnabled(emptyList())
|
buttonPoints = buttonPoints,
|
||||||
|
updateButtonTexts = { updateButtonTexts() },
|
||||||
val inputText = editText.text.toString().trim()
|
setButtonsEnabled = { setButtonsEnabled(it) },
|
||||||
if (inputText.isBlank()) {
|
updateMainButtonsState = { updateMainButtonsState(it) },
|
||||||
val message = LanguageManager.getText(languageID, "please_client_code")
|
).setup()
|
||||||
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val clientCode = inputText
|
|
||||||
|
|
||||||
GlobalValues.LAST_CLIENT_CODE = clientCode
|
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
val client = MyApp.database.clientDao().getClientByCode(clientCode)
|
|
||||||
if (client == null) {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
val message = LanguageManager.getText(languageID, "no_profile")
|
|
||||||
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
|
|
||||||
// enable only alwaysAvailable ones if no client found
|
|
||||||
val alwaysButtons = questionnaireEntries.mapIndexedNotNull { idx, entry ->
|
|
||||||
val btn = dynamicButtons.getOrNull(idx)
|
|
||||||
if (entry.condition is QuestionItem.Condition.AlwaysAvailable) btn else null
|
|
||||||
}
|
|
||||||
setButtonsEnabled(alwaysButtons)
|
|
||||||
}
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
updateMainButtonsState(true) // Datenbank vorhanden -> Buttons aktivieren
|
|
||||||
}
|
|
||||||
|
|
||||||
handleNormalLoad(clientCode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluierung der Bedingung: suspend, weil DB-Abfragen stattfinden.
|
|
||||||
private suspend fun evaluateCondition(
|
|
||||||
condition: QuestionItem.Condition?,
|
|
||||||
clientCode: String,
|
|
||||||
completedEntries: List<CompletedQuestionnaire> // Anpassung an deinem DAO-Objekt-Name
|
|
||||||
): Boolean {
|
|
||||||
if (condition == null) return false
|
|
||||||
|
|
||||||
when (condition) {
|
|
||||||
is QuestionItem.Condition.AlwaysAvailable -> return true
|
|
||||||
is QuestionItem.Condition.RequiresCompleted -> {
|
|
||||||
// prüfen, ob alle required items in completedEntries vorhanden und isDone == true sind
|
|
||||||
val normalizedCompleted = completedEntries.map { normalizeQuestionnaireId(it.questionnaireId) }
|
|
||||||
return condition.required.all { req ->
|
|
||||||
val nReq = normalizeQuestionnaireId(req)
|
|
||||||
normalizedCompleted.any { it.contains(nReq) || nReq.contains(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is QuestionItem.Condition.QuestionCondition -> {
|
|
||||||
// need to fetch the answer for that questionnaire/questionId
|
|
||||||
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) {
|
|
||||||
"==" -> answerValue == condition.value
|
|
||||||
"!=" -> answerValue != condition.value
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is QuestionItem.Condition.Combined -> {
|
|
||||||
// Combined: requiresCompleted (if present) AND questionCheck must match
|
|
||||||
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) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!reqOk) return false
|
|
||||||
// dann Frage-Check auswerten
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return reqOk
|
|
||||||
}
|
|
||||||
is QuestionItem.Condition.AnyOf -> {
|
|
||||||
// true, wenn irgendeine der Sub-Bedingungen erfüllt ist
|
|
||||||
for (sub in condition.conditions) {
|
|
||||||
val subRes = evaluateCondition(sub, clientCode, completedEntries)
|
|
||||||
if (subRes) return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun normalizeQuestionnaireId(name: String): String {
|
|
||||||
return name.lowercase().removeSuffix(".json")
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun handleNormalLoad(clientCode: String) {
|
|
||||||
val completedEntries = withContext(Dispatchers.IO) {
|
|
||||||
MyApp.database.completedQuestionnaireDao().getAllForClient(clientCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
buttonPoints.clear()
|
|
||||||
for (entry in completedEntries) {
|
|
||||||
if (entry.isDone) {
|
|
||||||
buttonPoints[entry.questionnaireId] = entry.sumPoints ?: 0
|
|
||||||
if (entry.questionnaireId.contains("questionnaire_2_rhs", ignoreCase = true)) {
|
|
||||||
RHS_POINTS = entry.sumPoints
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
updateButtonTexts()
|
|
||||||
}
|
|
||||||
|
|
||||||
// für jeden Fragebogen prüfen, ob er aktiv sein darf
|
|
||||||
val enabledButtons = mutableListOf<Button>()
|
|
||||||
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)
|
|
||||||
completedNorm.contains(targetNorm) || targetNorm.contains(completedNorm)
|
|
||||||
} && completed.isDone
|
|
||||||
}
|
|
||||||
if (isCompleted) {
|
|
||||||
// ausdrücklich deaktivieren
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// auswerten der Bedingung (suspend)
|
|
||||||
val condMet = evaluateCondition(entry.condition, clientCode, completedEntries)
|
|
||||||
if (condMet) enabledButtons.add(button)
|
|
||||||
}
|
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
if (enabledButtons.isEmpty()) {
|
|
||||||
setButtonsEnabled(emptyList())
|
|
||||||
val message = LanguageManager.getText(languageID, "questionnaires_finished")
|
|
||||||
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
|
|
||||||
} else {
|
|
||||||
setButtonsEnabled(enabledButtons)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateButtonTexts() {
|
private fun updateButtonTexts() {
|
||||||
@ -422,7 +239,6 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
|||||||
buttonLoad.text = LanguageManager.getText(languageID, "load")
|
buttonLoad.text = LanguageManager.getText(languageID, "load")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun setButtonsEnabled(enabledButtons: List<Button>) {
|
private fun setButtonsEnabled(enabledButtons: List<Button>) {
|
||||||
questionnaireFiles.keys.forEach { button ->
|
questionnaireFiles.keys.forEach { button ->
|
||||||
button.isEnabled = enabledButtons.contains(button)
|
button.isEnabled = enabledButtons.contains(button)
|
||||||
@ -442,238 +258,26 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
|||||||
|
|
||||||
fun onBackPressed(): Boolean = false
|
fun onBackPressed(): Boolean = false
|
||||||
|
|
||||||
private fun showCompletedQuestionnaires(clientCode: String) {
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
val actualClientCode = clientCode.removeSuffix("_database")
|
|
||||||
val completedEntries = MyApp.database.completedQuestionnaireDao().getAllForClient(actualClientCode)
|
|
||||||
|
|
||||||
Log.d("PDF_DEBUG", "Completed entries for client $actualClientCode:")
|
|
||||||
for (entry in completedEntries) {
|
|
||||||
Log.d("PDF_DEBUG", "Questionnaire ID: ${entry.questionnaireId}, Done: ${entry.isDone}, Points: ${entry.sumPoints}")
|
|
||||||
}
|
|
||||||
|
|
||||||
val pdfDocument = PdfDocument()
|
|
||||||
val pageWidth = 595
|
|
||||||
val pageHeight = 842
|
|
||||||
val paint = Paint().apply { textSize = 12f }
|
|
||||||
|
|
||||||
val csvBuilder = StringBuilder()
|
|
||||||
csvBuilder.appendLine("ClientCode,QuestionnaireID,IsDone,Points,Question,Answer")
|
|
||||||
|
|
||||||
for ((index, entry) in completedEntries.withIndex()) {
|
|
||||||
val pageInfo = PdfDocument.PageInfo.Builder(pageWidth, pageHeight, index + 1).create()
|
|
||||||
var page = pdfDocument.startPage(pageInfo)
|
|
||||||
var canvas = page.canvas
|
|
||||||
var yPosition = 40f
|
|
||||||
|
|
||||||
canvas.drawText("Client Code: $actualClientCode", 20f, yPosition, paint)
|
|
||||||
yPosition += 20f
|
|
||||||
canvas.drawText("Questionnaire: ${entry.questionnaireId}", 20f, yPosition, paint)
|
|
||||||
yPosition += 20f
|
|
||||||
canvas.drawText("Status: ${entry.isDone}", 20f, yPosition, paint)
|
|
||||||
yPosition += 20f
|
|
||||||
canvas.drawText("Points: ${entry.sumPoints ?: "N/A"}", 20f, yPosition, paint)
|
|
||||||
yPosition += 30f
|
|
||||||
|
|
||||||
val answers = MyApp.database.answerDao().getAnswersForClientAndQuestionnaire(actualClientCode, entry.questionnaireId)
|
|
||||||
|
|
||||||
for (answer in answers) {
|
|
||||||
val questionKey = answer.questionId.substringAfter("-")
|
|
||||||
val questionText = LanguageManager.getText("ENGLISH", questionKey)
|
|
||||||
val rawAnswerText = LanguageManager.getText("ENGLISH", answer.answerValue)
|
|
||||||
println("Entry " + entry)
|
|
||||||
println("Question " + questionKey)
|
|
||||||
println("Answer " + answer.answerValue)
|
|
||||||
val answerText = rawAnswerText.trim().removePrefix("[").removeSuffix("]")
|
|
||||||
|
|
||||||
yPosition = drawMultilineText(canvas, "Question: $questionText", 20f, yPosition, paint, pageWidth - 40, isBold = true)
|
|
||||||
yPosition += 8f
|
|
||||||
yPosition = drawMultilineText(canvas, "Answer: $answerText", 20f, yPosition, paint, pageWidth - 40)
|
|
||||||
yPosition += 20f
|
|
||||||
|
|
||||||
paint.strokeWidth = 0.5f
|
|
||||||
canvas.drawLine(20f, yPosition - 30f, pageWidth - 20f, yPosition - 30f, paint)
|
|
||||||
paint.strokeWidth = 0f
|
|
||||||
|
|
||||||
val sanitizedQuestion = questionText.replace(",", " ").replace("\n", " ")
|
|
||||||
val sanitizedAnswer = answerText.replace(",", " ").replace("\n", " ")
|
|
||||||
csvBuilder.appendLine("${actualClientCode},${entry.questionnaireId},${entry.isDone},${entry.sumPoints ?: ""},\"$sanitizedQuestion\",\"$sanitizedAnswer\"")
|
|
||||||
|
|
||||||
if (yPosition > pageHeight - 60) {
|
|
||||||
pdfDocument.finishPage(page)
|
|
||||||
val newPageInfo = PdfDocument.PageInfo.Builder(pageWidth, pageHeight, pdfDocument.pages.size + 1).create()
|
|
||||||
page = pdfDocument.startPage(newPageInfo)
|
|
||||||
canvas = page.canvas
|
|
||||||
yPosition = 40f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pdfDocument.finishPage(page)
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d("CSV_OUTPUT", "Generated CSV:\n${csvBuilder.toString()}")
|
|
||||||
|
|
||||||
val pdfFileName = "DatabaseOutput_${actualClientCode}.pdf"
|
|
||||||
val csvFileName = "DatabaseOutput_${actualClientCode}.csv"
|
|
||||||
val resolver = activity.contentResolver
|
|
||||||
|
|
||||||
val deleteIfExists: (String) -> Unit = { name ->
|
|
||||||
val projection = arrayOf(android.provider.MediaStore.MediaColumns._ID)
|
|
||||||
val selection = "${android.provider.MediaStore.MediaColumns.DISPLAY_NAME} = ?"
|
|
||||||
val selectionArgs = arrayOf(name)
|
|
||||||
val query = resolver.query(android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, null)
|
|
||||||
query?.use { cursor ->
|
|
||||||
if (cursor.moveToFirst()) {
|
|
||||||
val idColumn = cursor.getColumnIndexOrThrow(android.provider.MediaStore.MediaColumns._ID)
|
|
||||||
val id = cursor.getLong(idColumn)
|
|
||||||
val deleteUri = android.content.ContentUris.withAppendedId(android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI, id)
|
|
||||||
resolver.delete(deleteUri, null, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteIfExists(pdfFileName)
|
|
||||||
deleteIfExists(csvFileName)
|
|
||||||
|
|
||||||
try {
|
|
||||||
val pdfUri = resolver.insert(
|
|
||||||
android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI,
|
|
||||||
android.content.ContentValues().apply {
|
|
||||||
put(android.provider.MediaStore.MediaColumns.DISPLAY_NAME, pdfFileName)
|
|
||||||
put(android.provider.MediaStore.MediaColumns.MIME_TYPE, "application/pdf")
|
|
||||||
put(android.provider.MediaStore.MediaColumns.RELATIVE_PATH, "Download/")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
val csvUri = resolver.insert(
|
|
||||||
android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI,
|
|
||||||
android.content.ContentValues().apply {
|
|
||||||
put(android.provider.MediaStore.MediaColumns.DISPLAY_NAME, csvFileName)
|
|
||||||
put(android.provider.MediaStore.MediaColumns.MIME_TYPE, "text/csv")
|
|
||||||
put(android.provider.MediaStore.MediaColumns.RELATIVE_PATH, "Download/")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
pdfUri?.let {
|
|
||||||
resolver.openOutputStream(it)?.use { out -> pdfDocument.writeTo(out) }
|
|
||||||
pdfDocument.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
csvUri?.let {
|
|
||||||
resolver.openOutputStream(it)?.use { out ->
|
|
||||||
out.write(csvBuilder.toString().toByteArray(Charsets.UTF_8))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
Toast.makeText(activity, "PDF und CSV gespeichert in Downloads", Toast.LENGTH_LONG).show()
|
|
||||||
|
|
||||||
pdfUri?.let {
|
|
||||||
val intent = android.content.Intent(android.content.Intent.ACTION_VIEW).apply {
|
|
||||||
setDataAndType(it, "application/pdf")
|
|
||||||
addFlags(android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION or android.content.Intent.FLAG_ACTIVITY_NO_HISTORY)
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
activity.startActivity(intent)
|
|
||||||
} catch (e: android.content.ActivityNotFoundException) {
|
|
||||||
Toast.makeText(activity, "Kein PDF-Viewer installiert", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("SAVE", "Fehler beim Speichern der Dateien", e)
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
Toast.makeText(activity, "Fehler beim Speichern: ${e.message}", Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun drawMultilineText(
|
|
||||||
canvas: Canvas,
|
|
||||||
text: String,
|
|
||||||
x: Float,
|
|
||||||
yStart: Float,
|
|
||||||
paint: Paint,
|
|
||||||
maxWidth: Int,
|
|
||||||
isBold: Boolean = false
|
|
||||||
): Float {
|
|
||||||
paint.isFakeBoldText = isBold
|
|
||||||
|
|
||||||
val words = text.split(" ")
|
|
||||||
var line = ""
|
|
||||||
var y = yStart
|
|
||||||
for (word in words) {
|
|
||||||
val testLine = if (line.isEmpty()) word else "$line $word"
|
|
||||||
val lineWidth = paint.measureText(testLine)
|
|
||||||
if (lineWidth > maxWidth) {
|
|
||||||
canvas.drawText(line, x, y, paint)
|
|
||||||
y += paint.textSize * 1.4f
|
|
||||||
line = word
|
|
||||||
} else {
|
|
||||||
line = testLine
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (line.isNotEmpty()) {
|
|
||||||
canvas.drawText(line, x, y, paint)
|
|
||||||
y += paint.textSize * 1.4f
|
|
||||||
}
|
|
||||||
|
|
||||||
paint.isFakeBoldText = false
|
|
||||||
return y
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupSaveButton() {
|
private fun setupSaveButton() {
|
||||||
saveButton.text = LanguageManager.getText(languageID, "save")
|
SaveButtonHandler(
|
||||||
saveButton.setOnClickListener {
|
activity = activity,
|
||||||
val clientCode = editText.text.toString().trim()
|
saveButton = saveButton,
|
||||||
if (clientCode.isBlank()) {
|
editText = editText,
|
||||||
val message = LanguageManager.getText(languageID, "please_client_code")
|
languageIDProvider = { languageID }
|
||||||
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
|
).setup()
|
||||||
return@setOnClickListener
|
|
||||||
}
|
|
||||||
GlobalValues.LAST_CLIENT_CODE = clientCode
|
|
||||||
showCompletedQuestionnaires(clientCode)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupEditButton() {
|
private fun setupEditButtonHandler() {
|
||||||
editButton.text = LanguageManager.getText(languageID, "edit")
|
EditButtonHandler(
|
||||||
editButton.setOnClickListener {
|
activity = activity,
|
||||||
val clientCode = editText.text.toString().trim()
|
editButton = editButton,
|
||||||
if (clientCode.isBlank()) {
|
editText = editText,
|
||||||
val message = LanguageManager.getText(languageID, "please_client_code")
|
languageIDProvider = { languageID },
|
||||||
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
|
questionnaireFiles = questionnaireFiles,
|
||||||
return@setOnClickListener
|
buttonPoints = buttonPoints,
|
||||||
}
|
updateButtonTexts = { updateButtonTexts() },
|
||||||
|
setButtonsEnabled = { setButtonsEnabled(it) }
|
||||||
GlobalValues.LAST_CLIENT_CODE = clientCode
|
).setup()
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
val completedEntries = MyApp.database.completedQuestionnaireDao().getAllForClient(clientCode)
|
|
||||||
val completedFiles = completedEntries
|
|
||||||
.filter { it.isDone }
|
|
||||||
.map { it.questionnaireId.lowercase() }
|
|
||||||
|
|
||||||
buttonPoints.clear()
|
|
||||||
for (entry in completedEntries) {
|
|
||||||
if (entry.isDone) {
|
|
||||||
buttonPoints[entry.questionnaireId] = entry.sumPoints ?: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
updateButtonTexts()
|
|
||||||
|
|
||||||
val enabledButtons = questionnaireFiles.filter { (_, fileName) ->
|
|
||||||
completedFiles.any { completedId -> fileName.lowercase().contains(completedId) }
|
|
||||||
}.keys.toList()
|
|
||||||
|
|
||||||
setButtonsEnabled(enabledButtons)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupUploadButton() {
|
private fun setupUploadButton() {
|
||||||
@ -712,8 +316,6 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private fun setupDownloadButton() {
|
private fun setupDownloadButton() {
|
||||||
downloadButton.text = "Download"
|
downloadButton.text = "Download"
|
||||||
downloadButton.setOnClickListener {
|
downloadButton.setOnClickListener {
|
||||||
@ -753,7 +355,6 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun updateMainButtonsState(isDatabaseAvailable: Boolean) {
|
private fun updateMainButtonsState(isDatabaseAvailable: Boolean) {
|
||||||
val buttons = listOf(buttonLoad, saveButton, editButton)
|
val buttons = listOf(buttonLoad, saveButton, editButton)
|
||||||
buttons.forEach { button ->
|
buttons.forEach { button ->
|
||||||
|
|||||||
182
app/src/main/java/com/dano/test1/LoadButtonHandler.kt
Normal file
182
app/src/main/java/com/dano/test1/LoadButtonHandler.kt
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
package com.dano.test1
|
||||||
|
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.Toast
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import com.dano.test1.data.CompletedQuestionnaire
|
||||||
|
|
||||||
|
class LoadButtonHandler(
|
||||||
|
private val activity: MainActivity,
|
||||||
|
private val loadButton: Button,
|
||||||
|
private val editText: EditText,
|
||||||
|
private val languageIDProvider: () -> String,
|
||||||
|
private val questionnaireEntriesProvider: () -> List<QuestionItem.QuestionnaireEntry>,
|
||||||
|
private val dynamicButtonsProvider: () -> List<Button>,
|
||||||
|
private val buttonPoints: MutableMap<String, Int>,
|
||||||
|
private val updateButtonTexts: () -> Unit,
|
||||||
|
private val setButtonsEnabled: (List<Button>) -> Unit,
|
||||||
|
private val updateMainButtonsState: (Boolean) -> Unit,
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun setup() {
|
||||||
|
loadButton.text = LanguageManager.getText(languageIDProvider(), "load")
|
||||||
|
loadButton.setOnClickListener { handleLoadButton() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleLoadButton() {
|
||||||
|
buttonPoints.clear()
|
||||||
|
updateButtonTexts()
|
||||||
|
setButtonsEnabled(emptyList())
|
||||||
|
|
||||||
|
val inputText = editText.text.toString().trim()
|
||||||
|
if (inputText.isBlank()) {
|
||||||
|
val message = LanguageManager.getText(languageIDProvider(), "please_client_code")
|
||||||
|
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val clientCode = inputText
|
||||||
|
GlobalValues.LAST_CLIENT_CODE = clientCode
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val client = MyApp.database.clientDao().getClientByCode(clientCode)
|
||||||
|
if (client == 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)
|
||||||
|
if (entry.condition is QuestionItem.Condition.AlwaysAvailable) btn else null
|
||||||
|
}
|
||||||
|
setButtonsEnabled(alwaysButtons)
|
||||||
|
}
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
updateMainButtonsState(true) // Datenbank vorhanden -> Buttons aktivieren
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
is QuestionItem.Condition.RequiresCompleted -> {
|
||||||
|
val normalizedCompleted = completedEntries.map { normalizeQuestionnaireId(it.questionnaireId) }
|
||||||
|
return condition.required.all { req ->
|
||||||
|
val nReq = normalizeQuestionnaireId(req)
|
||||||
|
normalizedCompleted.any { it.contains(nReq) || nReq.contains(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is QuestionItem.Condition.QuestionCondition -> {
|
||||||
|
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) {
|
||||||
|
"==" -> 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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reqOk
|
||||||
|
}
|
||||||
|
is QuestionItem.Condition.AnyOf -> {
|
||||||
|
for (sub in condition.conditions) {
|
||||||
|
val subRes = evaluateCondition(sub, clientCode, completedEntries)
|
||||||
|
if (subRes) return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun normalizeQuestionnaireId(name: String): String {
|
||||||
|
return name.lowercase().removeSuffix(".json")
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun handleNormalLoad(clientCode: String) {
|
||||||
|
val completedEntries = withContext(Dispatchers.IO) {
|
||||||
|
MyApp.database.completedQuestionnaireDao().getAllForClient(clientCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonPoints.clear()
|
||||||
|
for (entry in completedEntries) {
|
||||||
|
if (entry.isDone) {
|
||||||
|
buttonPoints[entry.questionnaireId] = entry.sumPoints ?: 0
|
||||||
|
if (entry.questionnaireId.contains("questionnaire_2_rhs", ignoreCase = true)) {
|
||||||
|
RHS_POINTS = entry.sumPoints
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
updateButtonTexts()
|
||||||
|
}
|
||||||
|
|
||||||
|
// für jeden Fragebogen prüfen, ob er aktiv sein darf
|
||||||
|
val enabledButtons = mutableListOf<Button>()
|
||||||
|
val questionnaireEntries = questionnaireEntriesProvider()
|
||||||
|
val dynamicButtons = dynamicButtonsProvider()
|
||||||
|
|
||||||
|
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)
|
||||||
|
(completedNorm.contains(targetNorm) || targetNorm.contains(completedNorm)) && completed.isDone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isCompleted) continue
|
||||||
|
|
||||||
|
// auswerten der Bedingung (suspend)
|
||||||
|
val condMet = evaluateCondition(entry.condition, clientCode, completedEntries)
|
||||||
|
if (condMet) enabledButtons.add(button)
|
||||||
|
}
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
if (enabledButtons.isEmpty()) {
|
||||||
|
setButtonsEnabled(emptyList())
|
||||||
|
val message = LanguageManager.getText(languageIDProvider(), "questionnaires_finished")
|
||||||
|
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
|
||||||
|
} else {
|
||||||
|
setButtonsEnabled(enabledButtons)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
212
app/src/main/java/com/dano/test1/SaveButtonHandler.kt
Normal file
212
app/src/main/java/com/dano/test1/SaveButtonHandler.kt
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
package com.dano.test1
|
||||||
|
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.pdf.PdfDocument
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.Button
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.Toast
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
|
||||||
|
class SaveButtonHandler(
|
||||||
|
private val activity: MainActivity,
|
||||||
|
private val saveButton: Button,
|
||||||
|
private val editText: EditText,
|
||||||
|
private val languageIDProvider: () -> String
|
||||||
|
) {
|
||||||
|
|
||||||
|
fun setup() {
|
||||||
|
saveButton.text = LanguageManager.getText(languageIDProvider(), "save")
|
||||||
|
saveButton.setOnClickListener { handleSaveClick() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSaveClick() {
|
||||||
|
val clientCode = editText.text.toString().trim()
|
||||||
|
if (clientCode.isBlank()) {
|
||||||
|
val message = LanguageManager.getText(languageIDProvider(), "please_client_code")
|
||||||
|
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
GlobalValues.LAST_CLIENT_CODE = clientCode
|
||||||
|
showCompletedQuestionnaires(clientCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showCompletedQuestionnaires(clientCode: String) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val actualClientCode = clientCode.removeSuffix("_database")
|
||||||
|
val completedEntries = MyApp.database.completedQuestionnaireDao().getAllForClient(actualClientCode)
|
||||||
|
|
||||||
|
Log.d("PDF_DEBUG", "Completed entries for client $actualClientCode:")
|
||||||
|
for (entry in completedEntries) {
|
||||||
|
Log.d("PDF_DEBUG", "Questionnaire ID: ${entry.questionnaireId}, Done: ${entry.isDone}, Points: ${entry.sumPoints}")
|
||||||
|
}
|
||||||
|
|
||||||
|
val pdfDocument = PdfDocument()
|
||||||
|
val pageWidth = 595
|
||||||
|
val pageHeight = 842
|
||||||
|
val paint = Paint().apply { textSize = 12f }
|
||||||
|
|
||||||
|
val csvBuilder = StringBuilder()
|
||||||
|
csvBuilder.appendLine("ClientCode,QuestionnaireID,IsDone,Points,Question,Answer")
|
||||||
|
|
||||||
|
for ((index, entry) in completedEntries.withIndex()) {
|
||||||
|
val pageInfo = PdfDocument.PageInfo.Builder(pageWidth, pageHeight, index + 1).create()
|
||||||
|
var page = pdfDocument.startPage(pageInfo)
|
||||||
|
var canvas = page.canvas
|
||||||
|
var yPosition = 40f
|
||||||
|
|
||||||
|
canvas.drawText("Client Code: $actualClientCode", 20f, yPosition, paint)
|
||||||
|
yPosition += 20f
|
||||||
|
canvas.drawText("Questionnaire: ${entry.questionnaireId}", 20f, yPosition, paint)
|
||||||
|
yPosition += 20f
|
||||||
|
canvas.drawText("Status: ${entry.isDone}", 20f, yPosition, paint)
|
||||||
|
yPosition += 20f
|
||||||
|
canvas.drawText("Points: ${entry.sumPoints ?: "N/A"}", 20f, yPosition, paint)
|
||||||
|
yPosition += 30f
|
||||||
|
|
||||||
|
val answers = MyApp.database.answerDao().getAnswersForClientAndQuestionnaire(actualClientCode, entry.questionnaireId)
|
||||||
|
|
||||||
|
for (answer in answers) {
|
||||||
|
val questionKey = answer.questionId.substringAfter("-")
|
||||||
|
val questionText = LanguageManager.getText("ENGLISH", questionKey)
|
||||||
|
val rawAnswerText = LanguageManager.getText("ENGLISH", answer.answerValue)
|
||||||
|
val answerText = rawAnswerText.trim().removePrefix("[").removeSuffix("]")
|
||||||
|
|
||||||
|
yPosition = drawMultilineText(canvas, "Question: $questionText", 20f, yPosition, paint, pageWidth - 40, isBold = true)
|
||||||
|
yPosition += 8f
|
||||||
|
yPosition = drawMultilineText(canvas, "Answer: $answerText", 20f, yPosition, paint, pageWidth - 40)
|
||||||
|
yPosition += 20f
|
||||||
|
|
||||||
|
paint.strokeWidth = 0.5f
|
||||||
|
canvas.drawLine(20f, yPosition - 30f, pageWidth - 20f, yPosition - 30f, paint)
|
||||||
|
paint.strokeWidth = 0f
|
||||||
|
|
||||||
|
val sanitizedQuestion = questionText.replace(",", " ").replace("\n", " ")
|
||||||
|
val sanitizedAnswer = answerText.replace(",", " ").replace("\n", " ")
|
||||||
|
csvBuilder.appendLine("${actualClientCode},${entry.questionnaireId},${entry.isDone},${entry.sumPoints ?: ""},\"$sanitizedQuestion\",\"$sanitizedAnswer\"")
|
||||||
|
|
||||||
|
if (yPosition > pageHeight - 60) {
|
||||||
|
pdfDocument.finishPage(page)
|
||||||
|
val newPageInfo = PdfDocument.PageInfo.Builder(pageWidth, pageHeight, pdfDocument.pages.size + 1).create()
|
||||||
|
page = pdfDocument.startPage(newPageInfo)
|
||||||
|
canvas = page.canvas
|
||||||
|
yPosition = 40f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pdfDocument.finishPage(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d("CSV_OUTPUT", "Generated CSV:\n${csvBuilder.toString()}")
|
||||||
|
|
||||||
|
val pdfFileName = "DatabaseOutput_${actualClientCode}.pdf"
|
||||||
|
val csvFileName = "DatabaseOutput_${actualClientCode}.csv"
|
||||||
|
val resolver = activity.contentResolver
|
||||||
|
|
||||||
|
val deleteIfExists: (String) -> Unit = { name ->
|
||||||
|
val projection = arrayOf(android.provider.MediaStore.MediaColumns._ID)
|
||||||
|
val selection = "${android.provider.MediaStore.MediaColumns.DISPLAY_NAME} = ?"
|
||||||
|
val selectionArgs = arrayOf(name)
|
||||||
|
val query = resolver.query(android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, null)
|
||||||
|
query?.use { cursor ->
|
||||||
|
if (cursor.moveToFirst()) {
|
||||||
|
val idColumn = cursor.getColumnIndexOrThrow(android.provider.MediaStore.MediaColumns._ID)
|
||||||
|
val id = cursor.getLong(idColumn)
|
||||||
|
val deleteUri = android.content.ContentUris.withAppendedId(android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI, id)
|
||||||
|
resolver.delete(deleteUri, null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteIfExists(pdfFileName)
|
||||||
|
deleteIfExists(csvFileName)
|
||||||
|
|
||||||
|
try {
|
||||||
|
val pdfUri = resolver.insert(
|
||||||
|
android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI,
|
||||||
|
android.content.ContentValues().apply {
|
||||||
|
put(android.provider.MediaStore.MediaColumns.DISPLAY_NAME, pdfFileName)
|
||||||
|
put(android.provider.MediaStore.MediaColumns.MIME_TYPE, "application/pdf")
|
||||||
|
put(android.provider.MediaStore.MediaColumns.RELATIVE_PATH, "Download/")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
val csvUri = resolver.insert(
|
||||||
|
android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI,
|
||||||
|
android.content.ContentValues().apply {
|
||||||
|
put(android.provider.MediaStore.MediaColumns.DISPLAY_NAME, csvFileName)
|
||||||
|
put(android.provider.MediaStore.MediaColumns.MIME_TYPE, "text/csv")
|
||||||
|
put(android.provider.MediaStore.MediaColumns.RELATIVE_PATH, "Download/")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
pdfUri?.let {
|
||||||
|
resolver.openOutputStream(it)?.use { out -> pdfDocument.writeTo(out) }
|
||||||
|
pdfDocument.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
csvUri?.let {
|
||||||
|
resolver.openOutputStream(it)?.use { out ->
|
||||||
|
out.write(csvBuilder.toString().toByteArray(Charsets.UTF_8))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(activity, "PDF und CSV gespeichert in Downloads", Toast.LENGTH_LONG).show()
|
||||||
|
|
||||||
|
pdfUri?.let {
|
||||||
|
val intent = android.content.Intent(android.content.Intent.ACTION_VIEW).apply {
|
||||||
|
setDataAndType(it, "application/pdf")
|
||||||
|
addFlags(android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION or android.content.Intent.FLAG_ACTIVITY_NO_HISTORY)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
activity.startActivity(intent)
|
||||||
|
} catch (e: android.content.ActivityNotFoundException) {
|
||||||
|
Toast.makeText(activity, "Kein PDF-Viewer installiert", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("SAVE", "Fehler beim Speichern der Dateien", e)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
Toast.makeText(activity, "Fehler beim Speichern: ${e.message}", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawMultilineText(
|
||||||
|
canvas: Canvas,
|
||||||
|
text: String,
|
||||||
|
x: Float,
|
||||||
|
yStart: Float,
|
||||||
|
paint: Paint,
|
||||||
|
maxWidth: Int,
|
||||||
|
isBold: Boolean = false
|
||||||
|
): Float {
|
||||||
|
paint.isFakeBoldText = isBold
|
||||||
|
|
||||||
|
val words = text.split(" ")
|
||||||
|
var line = ""
|
||||||
|
var y = yStart
|
||||||
|
for (word in words) {
|
||||||
|
val testLine = if (line.isEmpty()) word else "$line $word"
|
||||||
|
val lineWidth = paint.measureText(testLine)
|
||||||
|
if (lineWidth > maxWidth) {
|
||||||
|
canvas.drawText(line, x, y, paint)
|
||||||
|
y += paint.textSize * 1.4f
|
||||||
|
line = word
|
||||||
|
} else {
|
||||||
|
line = testLine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (line.isNotEmpty()) {
|
||||||
|
canvas.drawText(line, x, y, paint)
|
||||||
|
y += paint.textSize * 1.4f
|
||||||
|
}
|
||||||
|
|
||||||
|
paint.isFakeBoldText = false
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user