Initialer Upload neues Unity-Projekt

This commit is contained in:
Daniel Ocks
2025-07-25 11:17:02 +02:00
commit f7a629ccde
91 changed files with 8743 additions and 0 deletions

View File

@ -0,0 +1,513 @@
package com.dano.test1
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Color
import android.graphics.pdf.PdfDocument
import android.view.View
import android.widget.*
import kotlinx.coroutines.*
import org.json.JSONArray
import android.util.Log
import java.util.Calendar
// Global constants and values
var INTEGRATION_INDEX_POINTS: Int? = null
class HandlerOpeningScreen(private val activity: MainActivity) {
private var languageID: String = "GERMAN"
private lateinit var editText: EditText
private lateinit var spinner: Spinner
private lateinit var textView: TextView
private lateinit var buttonContainer: LinearLayout
private lateinit var buttonLoad: Button
private val dynamicButtons = mutableListOf<Button>()
private val questionnaireFiles = mutableMapOf<Button, String>()
private val buttonPoints: MutableMap<String, Int> = mutableMapOf()
private var questionnaireOrder: List<String> = emptyList()
private var questionnaireEntries: List<QuestionItem.QuestionnaireEntry> = emptyList()
fun init() {
activity.setContentView(R.layout.opening_screen)
bindViews()
loadQuestionnaireOrder()
createQuestionnaireButtons()
restorePreviousClientCode()
setupLanguageSpinner()
setupLoadButton()
if (!editText.text.isNullOrBlank()) {
buttonLoad.performClick()
}
}
private fun bindViews() {
editText = activity.findViewById(R.id.editText)
spinner = activity.findViewById(R.id.string_spinner1)
textView = activity.findViewById(R.id.textView)
buttonContainer = activity.findViewById(R.id.buttonContainer)
buttonLoad = activity.findViewById(R.id.loadButton)
val tag = editText.tag as? String ?: ""
editText.hint = LanguageManager.getText(languageID, tag)
textView.text = LanguageManager.getText(languageID, "example_text")
}
private fun loadQuestionnaireOrder() {
try {
val inputStream = activity.assets.open("questionnaire_order.json")
val json = inputStream.bufferedReader().use { it.readText() }
val jsonArray = JSONArray(json)
questionnaireEntries = (0 until jsonArray.length()).map { i ->
val obj = jsonArray.getJSONObject(i)
val file = obj.getString("file")
val conditionObj = obj.optJSONObject("condition")
val condition = if (conditionObj != null) {
QuestionItem.Condition(
questionnaire = conditionObj.getString("questionnaire"),
questionId = conditionObj.getString("questionId"),
operator = conditionObj.getString("operator"),
value = conditionObj.getString("value")
)
} else null
QuestionItem.QuestionnaireEntry(file, condition)
}
} catch (e: Exception) {
e.printStackTrace()
questionnaireEntries = emptyList()
}
}
private fun createQuestionnaireButtons() {
buttonContainer.removeAllViews()
dynamicButtons.clear()
questionnaireFiles.clear()
for ((index, entry) in questionnaireEntries.withIndex()) {
val button = Button(activity).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply { setMargins(0, 8, 0, 8) }
text = "Questionnaire ${index + 1}"
id = View.generateViewId()
}
buttonContainer.addView(button)
dynamicButtons.add(button)
questionnaireFiles[button] = entry.file
}
updateButtonTexts()
setButtonsEnabled(listOf(dynamicButtons.firstOrNull()).filterNotNull())
dynamicButtons.forEach { button ->
button.setOnClickListener {
startQuestionnaireForButton(button)
setButtonsEnabled(dynamicButtons.filter { it != button })
}
}
}
private fun restorePreviousClientCode() {
GlobalValues.LAST_CLIENT_CODE?.let { editText.setText(it) }
}
private fun setupLanguageSpinner() {
val languages = listOf("GERMAN", "ENGLISH", "FRENCH", "ROMANIAN", "ARABIC", "POLISH", "TURKISH", "UKRAINIAN", "RUSSIAN", "SPANISH")
val adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, languages).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
spinner.adapter = adapter
spinner.setSelection(languages.indexOf(languageID))
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
languageID = languages[position]
updateButtonTexts()
val hintTag = editText.tag as? String ?: ""
editText.hint = LanguageManager.getText(languageID, hintTag)
}
override fun onNothingSelected(parent: AdapterView<*>) {}
}
}
private fun setupLoadButton() {
buttonLoad.text = LanguageManager.getText(languageID, "load")
buttonLoad.setOnClickListener { handleLoadButton() }
}
private fun handleLoadButton() {
buttonPoints.clear()
updateButtonTexts()
setButtonsEnabled(emptyList())
val inputText = editText.text.toString().trim()
if (inputText.isBlank()) {
val message = LanguageManager.getText(languageID, "please_client_code")
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
return
}
val isDatabaseView = inputText.endsWith("_database")
val clientCode = if (isDatabaseView) inputText.removeSuffix("_database") else 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()
setButtonsEnabled(listOf(dynamicButtons.firstOrNull()).filterNotNull())
}
return@launch
}
// Profil ist gültig → entweder normal laden oder PDF erzeugen
withContext(Dispatchers.Main) {
if (isDatabaseView) {
// Profil laden + PDF erzeugen
handleNormalLoad(clientCode) // ← Option: Zeige auch Punktefarben etc.
showCompletedQuestionnaires(clientCode)
} else {
handleNormalLoad(clientCode)
}
}
}
}
private suspend fun handleNormalLoad(clientCode: String) {
val completedIds = withContext(Dispatchers.IO) {
MyApp.database.completedQuestionnaireDao().getCompletedQuestionnairesForClient(clientCode)
}
if (completedIds.isEmpty()) {
setButtonsEnabled(listOf(dynamicButtons.firstOrNull()).filterNotNull())
val message = LanguageManager.getText(languageID, "no_profile")
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
return
}
val completedIndexes = completedIds.mapNotNull { id ->
questionnaireEntries.indexOfFirst { it.file.contains(id, ignoreCase = true) }.takeIf { it >= 0 }
}.sorted()
val completedEntries = withContext(Dispatchers.IO) {
MyApp.database.completedQuestionnaireDao().getAllForClient(clientCode)
}
buttonPoints.clear()
for (entry in completedEntries) {
if (entry.isDone && (entry.sumPoints ?: 0) > 0) {
buttonPoints[entry.questionnaireId] = entry.sumPoints ?: 0
if (entry.questionnaireId.contains("questionnaire_3_integration_index", ignoreCase = true)) {
INTEGRATION_INDEX_POINTS = entry.sumPoints
}
}
}
updateButtonTexts()
val rhsPoints = buttonPoints.entries.firstOrNull { it.key.contains("questionnaire_2_rhs", ignoreCase = true) }?.value
var nextIndex = (completedIndexes.lastOrNull() ?: -1) + 1
while (nextIndex < questionnaireEntries.size) {
val entry = questionnaireEntries[nextIndex]
val condition = entry.condition
if (condition != null) {
val answers = MyApp.database.answerDao()
.getAnswersForClientAndQuestionnaire(clientCode, condition.questionnaire)
val relevantAnswer = answers.find {
it.questionId.endsWith(condition.questionId)
}
val answerValue = relevantAnswer?.answerValue ?: ""
val conditionMet = when (condition.operator) {
"!=" -> answerValue != condition.value
"==" -> answerValue == condition.value
else -> true // fallback: zeige Fragebogen
}
if (conditionMet) break // Bedingung erfüllt → anzeigen
else nextIndex++ // überspringen
} else {
break // keine Bedingung → anzeigen
}
}
if (nextIndex >= questionnaireEntries.size) {
setButtonsEnabled(emptyList())
val message = LanguageManager.getText(languageID, "questionnaires_finished")
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
} else {
val nextFileName = questionnaireEntries[nextIndex].file
val nextButton = questionnaireFiles.entries.firstOrNull { it.value == nextFileName }?.key
setButtonsEnabled(listOfNotNull(nextButton))
}
}
private fun resetToStartState() {
GlobalValues.LAST_CLIENT_CODE = null
editText.setText("")
setButtonsEnabled(listOf(dynamicButtons.firstOrNull()).filterNotNull())
}
private fun updateButtonTexts() {
questionnaireFiles.forEach { (button, fileName) ->
val key = fileName.substringAfter("questionnaire_").substringAfter("_").removeSuffix(".json")
var buttonText = LanguageManager.getText(languageID, key)
val matchedEntry = buttonPoints.entries.firstOrNull { fileName.contains(it.key, ignoreCase = true) }
val points = matchedEntry?.value ?: 0
if (points > 0) {
buttonText += " (${points} P)"
}
button.text = buttonText
// Farbgebung je nach Punktzahl
when {
points in 1..12 -> button.setBackgroundColor(Color.parseColor("#4CAF50")) // Grün
points in 13..36 -> button.setBackgroundColor(Color.parseColor("#FFEB3B")) // Gelb
points in 37..100 -> button.setBackgroundColor(Color.parseColor("#F44336")) // Rot
else -> button.setBackgroundColor(Color.parseColor("#E0E0E0")) // Standardgrau bei 0
}
}
buttonLoad.text = LanguageManager.getText(languageID, "load")
}
private fun setButtonsEnabled(enabledButtons: List<Button>) {
questionnaireFiles.keys.forEach { button ->
val fileName = questionnaireFiles[button] ?: ""
val shouldDisable = fileName.contains("questionnaire_5_final_interview.json", ignoreCase = true) &&
buttonPoints.entries.firstOrNull { it.key.contains("questionnaire_2_rhs", ignoreCase = true) }?.value in 13..36
button.isEnabled = enabledButtons.contains(button) && !shouldDisable
button.alpha = if (button.isEnabled) 1.0f else 0.5f
}
}
private fun startQuestionnaireForButton(button: Button) {
val fileName = questionnaireFiles[button] ?: return
val questionnaire = QuestionnaireGeneric(fileName)
startQuestionnaire(questionnaire)
}
private fun startQuestionnaire(questionnaire: QuestionnaireBase<*>) {
activity.startQuestionnaire(questionnaire, languageID)
}
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}")
}
// ===== PDF ERSTELLUNG =====
val pdfDocument = PdfDocument()
val pageWidth = 595
val pageHeight = 842
val paint = Paint().apply { textSize = 12f }
// ===== CSV AUFBAU =====
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
// Header PDF
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)
//if (questionKey = "counsultation_decision" and answer.answerValue == "red")
val answerText = rawAnswerText.trim().removePrefix("[").removeSuffix("]")
// PDF
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
// CSV
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)
}
// ==== CSV LOG AUSGABE ====
Log.d("CSV_OUTPUT", "Generated CSV:\n${csvBuilder.toString()}")
val pdfFileName = "DatabaseOutput_${actualClientCode}.pdf"
val csvFileName = "DatabaseOutput_${actualClientCode}.csv"
val resolver = activity.contentResolver
// Bestehende Dateien löschen
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)
// PDF und CSV speichern
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
}
}