Compare commits

..

3 Commits

Author SHA1 Message Date
67bbc3ea06 reverted refactor change to function 2026-03-02 13:25:55 +01:00
b95977e28d created shared functions in ViewUtils.kt 2026-03-02 13:20:51 +01:00
cc89c77186 directory refactor 2026-03-02 13:08:25 +01:00
32 changed files with 295 additions and 241 deletions

13
.idea/deviceManager.xml generated Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
</list>
</option>
</component>
</project>

View File

@ -13,12 +13,11 @@ import android.widget.ProgressBar
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.dano.test1.auth.LoginManager
import com.dano.test1.auth.TokenStore
import com.dano.test1.network.DatabaseDownloader
import com.dano.test1.network.LoginManager
import com.dano.test1.network.TokenStore
import com.dano.test1.questionnaire.QuestionnaireBase
import com.dano.test1.ui.HandlerOpeningScreen
import com.dano.test1.util.LanguageManager
class MainActivity : AppCompatActivity() {

View File

@ -1,4 +1,4 @@
package com.dano.test1.export
package com.dano.test1.data
import android.content.ContentValues
import android.content.Context
@ -7,7 +7,7 @@ import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import com.dano.test1.util.LanguageManager
import com.dano.test1.LanguageManager
import com.dano.test1.MyApp
import org.apache.poi.ss.usermodel.Row
import org.apache.poi.xssf.usermodel.XSSFWorkbook
@ -41,8 +41,8 @@ class ExcelExportService(
val orderedIds = headerRepo.loadOrderedIds()
if (orderedIds.isEmpty()) return null
val clients = MyApp.Companion.database.clientDao().getAllClients()
val questionnaires = MyApp.Companion.database.questionnaireDao().getAll()
val clients = MyApp.database.clientDao().getAllClients()
val questionnaires = MyApp.database.questionnaireDao().getAll()
val questionnaireIdSet = questionnaires.map { it.id }.toSet()
val wb = XSSFWorkbook()
@ -71,9 +71,9 @@ class ExcelExportService(
var c = 0
row.createCell(c++).setCellValue((rowIdx + 1).toDouble())
val completedForClient = MyApp.Companion.database.completedQuestionnaireDao().getAllForClient(client.clientCode)
val completedForClient = MyApp.database.completedQuestionnaireDao().getAllForClient(client.clientCode)
val statusMap = completedForClient.associate { it.questionnaireId to it.isDone }
val answers = MyApp.Companion.database.answerDao().getAnswersForClient(client.clientCode)
val answers = MyApp.database.answerDao().getAnswersForClient(client.clientCode)
val answerMap = answers.associate { it.questionId to it.answerValue }
orderedIds.forEach { id ->
@ -179,4 +179,4 @@ class ExcelExportService(
for (key in candidates) localizeEnglishNoBrackets(key)?.let { return it }
return raw
}
}
}

View File

@ -1,9 +1,9 @@
package com.dano.test1.export
package com.dano.test1.data
import android.content.Context
import android.util.Log
import android.widget.Toast
import com.dano.test1.util.LanguageManager
import com.dano.test1.LanguageManager
import org.apache.poi.ss.usermodel.CellType
import org.apache.poi.ss.usermodel.DateUtil
import org.apache.poi.xssf.usermodel.XSSFWorkbook

View File

@ -2,6 +2,7 @@ package com.dano.test1.network
import android.content.Context
import android.util.Log
import com.dano.test1.AES256Helper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

View File

@ -8,6 +8,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import android.database.Cursor
import com.dano.test1.AES256Helper
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.asRequestBody

View File

@ -1,4 +1,4 @@
package com.dano.test1.auth
package com.dano.test1.network
import android.app.AlertDialog
import android.content.Context

View File

@ -1,4 +1,4 @@
package com.dano.test1.auth
package com.dano.test1.network
import android.content.Context

View File

@ -5,10 +5,19 @@ import android.app.Activity
import android.util.Log
import android.view.View
import android.widget.*
import com.dano.test1.util.LanguageManager
import com.dano.test1.LanguageManager
import com.dano.test1.MainActivity
import com.dano.test1.MyApp
import com.dano.test1.data.*
import com.dano.test1.questionnaire.handlers.HandlerClientCoachCode
import com.dano.test1.questionnaire.handlers.HandlerClientNotSigned
import com.dano.test1.questionnaire.handlers.HandlerDateSpinner
import com.dano.test1.questionnaire.handlers.HandlerGlassScaleQuestion
import com.dano.test1.questionnaire.handlers.HandlerLastPage
import com.dano.test1.questionnaire.handlers.HandlerMultiCheckboxQuestion
import com.dano.test1.questionnaire.handlers.HandlerRadioQuestion
import com.dano.test1.questionnaire.handlers.HandlerStringSpinner
import com.dano.test1.questionnaire.handlers.HandlerValueSpinner
import com.google.gson.Gson
import com.google.gson.JsonParser
import kotlinx.coroutines.*
@ -176,7 +185,7 @@ abstract class QuestionnaireBase<T> {
suspend fun saveAnswersToDatabase(answers: Map<String, Any>, questionnaireId: String) {
Log.d("AnswersMap", answers.toString())
val db = MyApp.Companion.database
val db = MyApp.database
val clientCode = answers["client_code"] as? String ?: return

View File

@ -1,7 +1,7 @@
package com.dano.test1.questionnaire
import android.widget.Button
import com.dano.test1.util.LocalizationHelper
import com.dano.test1.LocalizationHelper
import com.dano.test1.R
open class QuestionnaireGeneric(private val questionnaireFileName: String) : QuestionnaireBase<Unit>() {

View File

@ -1,12 +1,15 @@
package com.dano.test1.questionnaire
package com.dano.test1.questionnaire.handlers
import android.view.View
import android.widget.*
import com.dano.test1.util.LanguageManager
import com.dano.test1.util.setTextSizePercentOfScreenHeight
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.LanguageManager
import com.dano.test1.MyApp
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.R
import com.dano.test1.auth.TokenStore
import com.dano.test1.network.TokenStore
import com.dano.test1.utils.ViewUtils
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -41,10 +44,10 @@ class HandlerClientCoachCode(
questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
titleTextView.setTextSizePercentOfScreenHeight(0.03f)
questionTextView.setTextSizePercentOfScreenHeight(0.03f)
clientCodeField.setTextSizePercentOfScreenHeight(0.025f)
coachCodeField.setTextSizePercentOfScreenHeight(0.025f)
ViewUtils.setTextSizePercentOfScreenHeight(titleTextView, 0.03f)
ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f)
ViewUtils.setTextSizePercentOfScreenHeight(clientCodeField, 0.025f)
ViewUtils.setTextSizePercentOfScreenHeight(coachCodeField, 0.025f)
// Client-Code: nur verwenden, wenn bereits geladen
val loadedClientCode = GlobalValues.LOADED_CLIENT_CODE
@ -60,7 +63,7 @@ class HandlerClientCoachCode(
val coachFromLogin = TokenStore.getUsername(layout.context)
if (!coachFromLogin.isNullOrBlank()) {
coachCodeField.setText(coachFromLogin)
lockCoachField(coachCodeField) // optisch & technisch gesperrt
ViewUtils.lockEditField(coachCodeField) // optisch & technisch gesperrt
} else {
// Falls (theoretisch) kein Login-Username vorhanden ist, verhalten wie bisher
coachCodeField.setText(answers["coach_code"] as? String ?: "")
@ -93,7 +96,7 @@ class HandlerClientCoachCode(
val dbExistedBefore = dbPath.exists()
CoroutineScope(Dispatchers.IO).launch {
val existingClient = MyApp.Companion.database.clientDao().getClientByCode(clientCode)
val existingClient = MyApp.database.clientDao().getClientByCode(clientCode)
withContext(Dispatchers.Main) {
if (existingClient != null && clientCodeField.isEnabled) {
@ -104,7 +107,7 @@ class HandlerClientCoachCode(
goToNextQuestion()
if (!dbExistedBefore) {
MyApp.Companion.database.close()
MyApp.database.close()
dbPath.delete()
val journalFile = layout.context.getDatabasePath("questionnaire_database-journal")
journalFile.delete()
@ -139,19 +142,4 @@ class HandlerClientCoachCode(
// Not used
}
private fun lockCoachField(field: EditText) {
field.isFocusable = false
field.isFocusableInTouchMode = false
field.isCursorVisible = false
field.keyListener = null
field.isLongClickable = false
field.isClickable = false
field.setBackgroundResource(R.drawable.bg_field_locked)
field.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_lock_24, 0)
field.compoundDrawablePadding = dp(8)
field.alpha = 0.95f
}
private fun dp(v: Int): Int =
(v * layout.resources.displayMetrics.density).toInt()
}

View File

@ -1,8 +1,10 @@
package com.dano.test1.questionnaire
package com.dano.test1.questionnaire.handlers
import android.view.View
import android.widget.*
import com.dano.test1.util.LanguageManager
import com.dano.test1.LanguageManager
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.R
/*

View File

@ -1,4 +1,4 @@
package com.dano.test1.questionnaire
package com.dano.test1.questionnaire.handlers
import android.content.Context
import android.view.View
@ -6,13 +6,16 @@ import android.widget.*
import kotlinx.coroutines.*
import java.text.SimpleDateFormat
import java.util.*
import com.dano.test1.util.LanguageManager
import com.dano.test1.util.setTextSizePercentOfScreenHeight
import com.dano.test1.util.setupSpinner
import com.dano.test1.util.Month
import com.dano.test1.util.Months
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.LanguageManager
import com.dano.test1.questionnaire.MAX_VALUE_YEAR
import com.dano.test1.ui.Month
import com.dano.test1.ui.Months
import com.dano.test1.MyApp
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
/*
Zweck:
@ -54,11 +57,12 @@ class HandlerDateSpinner(
questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
textView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
textView.setTextSizePercentOfScreenHeight(0.03f)
questionTextView.setTextSizePercentOfScreenHeight(0.03f)
labelDay.setTextSizePercentOfScreenHeight(0.025f)
labelMonth.setTextSizePercentOfScreenHeight(0.025f)
labelYear.setTextSizePercentOfScreenHeight(0.025f)
// Schriftgrößen pro Bildschirmhöhe
ViewUtils.setTextSizePercentOfScreenHeight(textView, 0.03f)
ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f)
ViewUtils.setTextSizePercentOfScreenHeight(labelDay, 0.025f)
ViewUtils.setTextSizePercentOfScreenHeight(labelMonth, 0.025f)
ViewUtils.setTextSizePercentOfScreenHeight(labelYear, 0.025f)
// gespeicherte Antwort (YYYY-MM-DD) lesen
val (savedYear, savedMonthIndex, savedDay) = question.question?.let {
@ -75,9 +79,10 @@ class HandlerDateSpinner(
?: months[today.get(Calendar.MONTH)]
val defaultYear = savedYear ?: today.get(Calendar.YEAR)
spinnerDay.setupSpinner(days, defaultDay)
spinnerMonth.setupSpinner(months, defaultMonth)
spinnerYear.setupSpinner(years, defaultYear)
// Spinner responsiv aufsetzen (Schrift + Zeilenhöhe ohne Abschneiden)
ViewUtils.setupResponsiveSpinner(context, spinnerDay, days, defaultDay)
ViewUtils.setupResponsiveSpinner(context, spinnerMonth, months, defaultMonth)
ViewUtils.setupResponsiveSpinner(context, spinnerYear, years, defaultYear)
// DB-Abfrage, falls noch nicht im answers-Map
val answerMapKey = question.question ?: (question.id ?: "")
@ -87,7 +92,7 @@ class HandlerDateSpinner(
val clientCode = GlobalValues.LAST_CLIENT_CODE
if (clientCode.isNullOrBlank()) return@launch
val allAnswersForClient = MyApp.Companion.database.answerDao().getAnswersForClient(clientCode)
val allAnswersForClient = MyApp.database.answerDao().getAnswersForClient(clientCode)
val myQuestionId = questionnaireMeta + "-" + question.question
val dbAnswer = allAnswersForClient.find { it.questionId == myQuestionId }?.answerValue

View File

@ -1,14 +1,16 @@
package com.dano.test1.questionnaire
package com.dano.test1.questionnaire.handlers
import android.content.Context
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.widget.*
import com.dano.test1.util.LanguageManager
import com.dano.test1.util.setTextSizePercentOfScreenHeight
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.LanguageManager
import com.dano.test1.MyApp
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
import kotlinx.coroutines.*
/*
@ -70,8 +72,8 @@ class HandlerGlassScaleQuestion(
titleTv.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
questionTv.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
titleTv.setTextSizePercentOfScreenHeight(0.03f)
questionTv.setTextSizePercentOfScreenHeight(0.03f)
ViewUtils.setTextSizePercentOfScreenHeight(titleTv, 0.03f)
ViewUtils.setTextSizePercentOfScreenHeight(questionTv, 0.03f)
// Header Icons
val header = layout.findViewById<LinearLayout>(R.id.glass_header)
@ -105,7 +107,7 @@ class HandlerGlassScaleQuestion(
CoroutineScope(Dispatchers.IO).launch {
try {
val clientCode = GlobalValues.LAST_CLIENT_CODE ?: return@launch
val allAnswersForClient = MyApp.Companion.database.answerDao().getAnswersForClient(clientCode)
val allAnswersForClient = MyApp.database.answerDao().getAnswersForClient(clientCode)
val answerMap = allAnswersForClient.associateBy({ it.questionId }, { it.answerValue })
withContext(Dispatchers.Main) {
@ -153,7 +155,7 @@ class HandlerGlassScaleQuestion(
text = LanguageManager.getText(languageID, symptomKey)
layoutParams = TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT, 4f)
setPadding(4, 16, 4, 16)
setTextSizePercentOfScreenHeight(0.022f)
ViewUtils.setTextSizePercentOfScreenHeight(this, 0.022f)
}
row.addView(symptomText)

View File

@ -1,15 +1,18 @@
package com.dano.test1.questionnaire
package com.dano.test1.questionnaire.handlers
import android.util.TypedValue
import android.view.View
import android.widget.*
import android.text.Html
import kotlinx.coroutines.*
import android.util.TypedValue
import androidx.core.widget.TextViewCompat
import com.dano.test1.util.LanguageManager
import com.dano.test1.util.setTextSizePercentOfScreenHeight
import kotlinx.coroutines.*
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.LanguageManager
import com.dano.test1.MainActivity
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
import com.google.android.material.button.MaterialButton
/*
@ -60,8 +63,9 @@ class HandlerLastPage(
finishBtn.isAllCaps = false
applyResponsiveTextSizing(finishBtn)
titleTv.setTextSizePercentOfScreenHeight(0.03f)
questionTv.setTextSizePercentOfScreenHeight(0.03f)
// Überschriften responsiv skalieren (wie zuvor)
ViewUtils.setTextSizePercentOfScreenHeight(titleTv, 0.03f)
ViewUtils.setTextSizePercentOfScreenHeight(questionTv, 0.03f)
// Buttons
prevBtn.setOnClickListener { goToPreviousQuestion() }

View File

@ -1,15 +1,18 @@
package com.dano.test1.questionnaire
package com.dano.test1.questionnaire.handlers
import android.content.Context
import android.util.TypedValue
import android.view.View
import android.widget.*
import kotlinx.coroutines.*
import android.util.TypedValue
import androidx.core.widget.TextViewCompat
import com.dano.test1.util.LanguageManager
import com.dano.test1.util.setTextSizePercentOfScreenHeight
import kotlinx.coroutines.*
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.LanguageManager
import com.dano.test1.MyApp
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
/*
Zweck:
@ -41,8 +44,9 @@ class HandlerMultiCheckboxQuestion(
questionTextView.text = this.question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
questionTitle.text = this.question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
questionTextView.setTextSizePercentOfScreenHeight(0.03f)
questionTitle.setTextSizePercentOfScreenHeight(0.03f)
// Textgrößen pro Bildschirmhöhe (wie bei deinen anderen Handlern)
ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f)
ViewUtils.setTextSizePercentOfScreenHeight(questionTitle, 0.03f)
container.removeAllViews()
@ -91,7 +95,7 @@ class HandlerMultiCheckboxQuestion(
val clientCode = GlobalValues.LAST_CLIENT_CODE
if (clientCode.isNullOrBlank()) return@launch
val allAnswersForClient = MyApp.Companion.database.answerDao().getAnswersForClient(clientCode)
val allAnswersForClient = MyApp.database.answerDao().getAnswersForClient(clientCode)
val myQuestionId = questionnaireMeta + "-" + question.question
val dbAnswer = allAnswersForClient.find { it.questionId == myQuestionId }?.answerValue

View File

@ -1,14 +1,17 @@
package com.dano.test1.questionnaire
package com.dano.test1.questionnaire.handlers
import android.content.Context
import android.view.View
import android.text.Html
import android.widget.*
import kotlinx.coroutines.*
import com.dano.test1.util.LanguageManager
import com.dano.test1.util.setTextSizePercentOfScreenHeight
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.LanguageManager
import com.dano.test1.MyApp
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
/*
Zweck:
@ -43,8 +46,8 @@ class HandlerRadioQuestion(
Html.fromHtml(LanguageManager.getText(languageID, it), Html.FROM_HTML_MODE_LEGACY)
} ?: ""
questionTextView.setTextSizePercentOfScreenHeight(0.03f)
questionTitle.setTextSizePercentOfScreenHeight(0.03f)
ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f)
ViewUtils.setTextSizePercentOfScreenHeight(questionTitle, 0.03f)
radioGroup.removeAllViews()
@ -53,7 +56,8 @@ class HandlerRadioQuestion(
text = LanguageManager.getText(languageID, option.key)
tag = option.key
setTextSizePercentOfScreenHeight(0.025f)
// RadioButton-Text analog zu EditTexts: 2.5% der Bildschirmhöhe
ViewUtils.setTextSizePercentOfScreenHeight(this, 0.025f)
layoutParams = RadioGroup.LayoutParams(
RadioGroup.LayoutParams.MATCH_PARENT,
@ -79,7 +83,7 @@ class HandlerRadioQuestion(
val clientCode = GlobalValues.LAST_CLIENT_CODE
if (clientCode.isNullOrBlank()) return@launch
val allAnswersForClient = MyApp.Companion.database.answerDao().getAnswersForClient(clientCode)
val allAnswersForClient = MyApp.database.answerDao().getAnswersForClient(clientCode)
val myQuestionId = questionnaireMeta + "-" + question.question
val dbAnswer = allAnswersForClient.find { it.questionId == myQuestionId }?.answerValue

View File

@ -1,15 +1,17 @@
package com.dano.test1.questionnaire
package com.dano.test1.questionnaire.handlers
import android.content.Context
import android.view.View
import android.widget.*
import kotlinx.coroutines.*
import com.dano.test1.util.LanguageManager
import com.dano.test1.util.setTextSizePercentOfScreenHeight
import com.dano.test1.util.setupSpinner
import com.dano.test1.ui.Countries
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.LanguageManager
import com.dano.test1.MyApp
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.R
import com.dano.test1.util.Countries
import com.dano.test1.utils.ViewUtils
/*
Zweck:
@ -44,15 +46,17 @@ class HandlerStringSpinner(
questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
textView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
textView.setTextSizePercentOfScreenHeight(0.03f)
questionTextView.setTextSizePercentOfScreenHeight(0.03f)
// Textgrößen prozentual zur Bildschirmhöhe
ViewUtils.setTextSizePercentOfScreenHeight(textView, 0.03f)
ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f)
val options = buildOptionsList()
// vorhandene Auswahl (falls vorhanden)
val savedSelection = question.question?.let { answers[it] as? String }
spinner.setupSpinner(options, savedSelection)
// Spinner aufsetzen
ViewUtils.setupResponsiveSpinner(context, spinner, options, savedSelection)
// Falls noch keine Antwort im Map: aus DB laden
val answerMapKey = question.question ?: (question.id ?: "")
@ -62,7 +66,7 @@ class HandlerStringSpinner(
val clientCode = GlobalValues.LAST_CLIENT_CODE
if (clientCode.isNullOrBlank()) return@launch
val allAnswersForClient = MyApp.Companion.database.answerDao().getAnswersForClient(clientCode)
val allAnswersForClient = MyApp.database.answerDao().getAnswersForClient(clientCode)
val myQuestionId = questionnaireMeta + "-" + question.question
val dbAnswer = allAnswersForClient.find { it.questionId == myQuestionId }?.answerValue

View File

@ -1,14 +1,16 @@
package com.dano.test1.questionnaire
package com.dano.test1.questionnaire.handlers
import android.content.Context
import android.view.View
import android.widget.*
import kotlinx.coroutines.*
import com.dano.test1.util.LanguageManager
import com.dano.test1.util.setTextSizePercentOfScreenHeight
import com.dano.test1.util.setupSpinner
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.LanguageManager
import com.dano.test1.MyApp
import com.dano.test1.questionnaire.QuestionHandler
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
/*
Zweck:
@ -45,8 +47,8 @@ class HandlerValueSpinner(
questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
textView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
textView.setTextSizePercentOfScreenHeight(0.03f)
questionTextView.setTextSizePercentOfScreenHeight(0.03f)
ViewUtils.setTextSizePercentOfScreenHeight(textView, 0.03f)
ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f)
val prompt = LanguageManager.getText(languageID, "choose_answer")
val spinnerItems: List<String> = listOf(prompt) + if (question.range != null) {
@ -56,7 +58,7 @@ class HandlerValueSpinner(
}
val savedValue = question.question?.let { answers[it] as? String }
spinner.setupSpinner(spinnerItems, savedValue)
ViewUtils.setupResponsiveSpinner(context, spinner, spinnerItems, savedValue)
//DB-Abfrage falls noch keine Antwort im Map existiert
val answerMapKey = question.question ?: (question.id ?: "")
@ -66,7 +68,7 @@ class HandlerValueSpinner(
val clientCode = GlobalValues.LAST_CLIENT_CODE
if (clientCode.isNullOrBlank()) return@launch
val allAnswersForClient = MyApp.Companion.database.answerDao().getAnswersForClient(clientCode)
val allAnswersForClient = MyApp.database.answerDao().getAnswersForClient(clientCode)
val myQuestionId = questionnaireMeta + "-" + question.question
val dbAnswer = allAnswersForClient.find { it.questionId == myQuestionId }?.answerValue

View File

@ -1,6 +1,6 @@
package com.dano.test1.util
package com.dano.test1.ui
import com.dano.test1.util.LanguageManager
import com.dano.test1.LanguageManager
object Countries {
fun getAllCountries(languageID: String): List<String> {

View File

@ -4,28 +4,19 @@ import android.graphics.Color
import android.graphics.Typeface
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.ProgressBar
import android.widget.TableLayout
import android.widget.TableRow
import android.widget.TextView
import android.widget.Toast
import com.dano.test1.export.ExcelExportService
import com.dano.test1.export.HeaderOrderRepository
import com.dano.test1.util.LanguageManager
import android.widget.*
import com.dano.test1.data.ExcelExportService
import com.dano.test1.utils.ViewUtils
import com.dano.test1.LanguageManager
import com.dano.test1.MainActivity
import com.dano.test1.MyApp
import com.dano.test1.R
import com.dano.test1.data.Client
import com.dano.test1.data.HeaderOrderRepository
import com.dano.test1.data.Question
import com.dano.test1.data.Questionnaire
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.*
import org.json.JSONArray
import kotlin.math.roundToInt
class DatabaseButtonHandler(
private val activity: MainActivity,
@ -76,7 +67,7 @@ class DatabaseButtonHandler(
uiScope.launch {
val clients: List<Client> = withContext(Dispatchers.IO) {
MyApp.Companion.database.clientDao().getAllClients()
MyApp.database.clientDao().getAllClients()
}
progress.visibility = View.GONE
@ -163,11 +154,9 @@ class DatabaseButtonHandler(
uiScope.launch {
val result = withContext(Dispatchers.IO) {
val allQuestionnairesDb = MyApp.Companion.database.questionnaireDao().getAll()
val completedForClient =
MyApp.Companion.database.completedQuestionnaireDao().getAllForClient(clientCode)
val allAnswersForClient =
MyApp.Companion.database.answerDao().getAnswersForClient(clientCode)
val allQuestionnairesDb = MyApp.database.questionnaireDao().getAll()
val completedForClient = MyApp.database.completedQuestionnaireDao().getAllForClient(clientCode)
val allAnswersForClient = MyApp.database.answerDao().getAnswersForClient(clientCode)
Triple(allQuestionnairesDb, completedForClient, allAnswersForClient)
}
@ -286,9 +275,8 @@ class DatabaseButtonHandler(
uiScope.launch {
val (questions, answersForClient) = withContext(Dispatchers.IO) {
val qs = MyApp.Companion.database.questionDao()
.getQuestionsForQuestionnaire(questionnaireId)
val ans = MyApp.Companion.database.answerDao()
val qs = MyApp.database.questionDao().getQuestionsForQuestionnaire(questionnaireId)
val ans = MyApp.database.answerDao()
.getAnswersForClientAndQuestionnaire(clientCode, questionnaireId)
qs to ans
}
@ -432,10 +420,7 @@ class DatabaseButtonHandler(
bgColor?.let { setBackgroundColor(it) }
}
private fun dp(value: Int): Int {
val density = activity.resources.displayMetrics.density
return (value * density).roundToInt()
}
private fun dp(value: Int): Int = ViewUtils.dp(activity, value)
private fun <T : View> requireView(id: Int, name: String): T {
val v = activity.findViewById<T>(id)
@ -482,4 +467,4 @@ class DatabaseButtonHandler(
} catch (_: Exception) {
emptyList()
}
}
}

View File

@ -3,7 +3,7 @@ package com.dano.test1.ui
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import com.dano.test1.util.LanguageManager
import com.dano.test1.LanguageManager
import com.dano.test1.MainActivity
import com.dano.test1.MyApp
import kotlinx.coroutines.*
@ -62,7 +62,7 @@ class EditButtonHandler(
}
val completedEntries: List<CompletedQuestionnaire> =
MyApp.Companion.database.completedQuestionnaireDao().getAllForClient(desiredCode)
MyApp.database.completedQuestionnaireDao().getAllForClient(desiredCode)
val completedFiles = completedEntries.filter { it.isDone }.map { it.questionnaireId.lowercase() }

View File

@ -10,13 +10,13 @@ import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.widget.*
import com.dano.test1.util.LanguageManager
import com.dano.test1.LanguageManager
import com.dano.test1.MainActivity
import com.dano.test1.R
import com.dano.test1.auth.LoginManager
import com.dano.test1.auth.TokenStore
import com.dano.test1.network.DatabaseUploader
import com.dano.test1.network.LoginManager
import com.dano.test1.network.NetworkUtils
import com.dano.test1.network.TokenStore
import com.dano.test1.questionnaire.GlobalValues
import com.dano.test1.questionnaire.QuestionItem
import com.dano.test1.questionnaire.QuestionnaireBase
@ -27,6 +27,7 @@ import org.json.JSONObject
import java.io.File
import java.util.concurrent.TimeUnit
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.dano.test1.utils.ViewUtils
var RHS_POINTS: Int? = null
@ -572,7 +573,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
}
}
private fun dp(v: Int): Int = (v * activity.resources.displayMetrics.density).toInt()
private fun dp(v: Int): Int = ViewUtils.dp(activity, v)
private fun isCompleted(button: Button): Boolean {
val fileName = questionnaireFiles[button] ?: return false
@ -664,16 +665,7 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
}
private fun lockCoachCodeField() {
coachEditText.isFocusable = false
coachEditText.isFocusableInTouchMode = false
coachEditText.isCursorVisible = false
coachEditText.keyListener = null
coachEditText.isLongClickable = false
coachEditText.isClickable = false
coachEditText.setBackgroundResource(R.drawable.bg_field_locked)
coachEditText.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_lock_24, 0)
coachEditText.compoundDrawablePadding = dp(8)
coachEditText.alpha = 0.95f
ViewUtils.lockEditField(coachEditText)
}
private fun applySessionAgeHighlight(ageMs: Long) {

View File

@ -3,7 +3,7 @@ package com.dano.test1.ui
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import com.dano.test1.util.LanguageManager
import com.dano.test1.LanguageManager
import com.dano.test1.MainActivity
import com.dano.test1.MyApp
import kotlinx.coroutines.*
@ -45,7 +45,7 @@ class LoadButtonHandler(
GlobalValues.LAST_CLIENT_CODE = clientCode
CoroutineScope(Dispatchers.IO).launch {
val client = MyApp.Companion.database.clientDao().getClientByCode(clientCode)
val client = MyApp.database.clientDao().getClientByCode(clientCode)
if (client == null) {
GlobalValues.LOADED_CLIENT_CODE = null
withContext(Dispatchers.Main) {
@ -85,7 +85,7 @@ class LoadButtonHandler(
}
}
is QuestionItem.Condition.QuestionCondition -> {
val answers = MyApp.Companion.database.answerDao().getAnswersForClientAndQuestionnaire(clientCode, condition.questionnaire)
val answers = MyApp.database.answerDao().getAnswersForClientAndQuestionnaire(clientCode, condition.questionnaire)
val relevant = answers.find { it.questionId.endsWith(condition.questionId, ignoreCase = true) }
val answerValue = relevant?.answerValue ?: ""
when (condition.operator) {
@ -102,7 +102,7 @@ class LoadButtonHandler(
}
if (!reqOk) return false
val q = condition.questionCheck ?: return true
val answers = MyApp.Companion.database.answerDao().getAnswersForClientAndQuestionnaire(clientCode, q.questionnaire)
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) {
@ -122,7 +122,7 @@ class LoadButtonHandler(
private suspend fun handleNormalLoad(clientCode: String) {
val completedEntries = withContext(Dispatchers.IO) {
MyApp.Companion.database.completedQuestionnaireDao().getAllForClient(clientCode)
MyApp.database.completedQuestionnaireDao().getAllForClient(clientCode)
}
buttonPoints.clear()

View File

@ -1,11 +1,13 @@
package com.dano.test1.util
package com.dano.test1.ui
import com.dano.test1.LanguageManager
data class Month(val name: String) {
override fun toString(): String = name
}
object Months {
fun getAllMonths(languageID: String): List<Month> {
fun getAllMonths(languageID: String): List<Any> {
return listOf(
Month(LanguageManager.getText(languageID, "january")),
Month(LanguageManager.getText(languageID, "february")),

View File

@ -12,7 +12,7 @@ import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import com.dano.test1.util.LanguageManager
import com.dano.test1.LanguageManager
import com.dano.test1.MainActivity
import com.dano.test1.MyApp
import com.dano.test1.questionnaire.GlobalValues
@ -44,7 +44,7 @@ class SaveButtonHandler(
private fun showCompletedQuestionnaires(clientCode: String) {
CoroutineScope(Dispatchers.IO).launch {
val actualClientCode = clientCode.removeSuffix("_database")
val completedEntries = MyApp.Companion.database.completedQuestionnaireDao().getAllForClient(actualClientCode)
val completedEntries = MyApp.database.completedQuestionnaireDao().getAllForClient(actualClientCode)
Log.d("PDF_DEBUG", "Completed entries for client $actualClientCode:")
for (entry in completedEntries) {
@ -74,7 +74,7 @@ class SaveButtonHandler(
canvas.drawText("Points: ${entry.sumPoints ?: "N/A"}", 20f, yPosition, paint)
yPosition += 30f
val answers = MyApp.Companion.database.answerDao().getAnswersForClientAndQuestionnaire(actualClientCode, entry.questionnaireId)
val answers = MyApp.database.answerDao().getAnswersForClientAndQuestionnaire(actualClientCode, entry.questionnaireId)
for (answer in answers) {
val questionKey = answer.questionId.substringAfter("-")

View File

@ -1,73 +0,0 @@
package com.dano.test1.util
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.AbsListView
import android.widget.ArrayAdapter
import android.widget.Spinner
import android.widget.TextView
import androidx.core.widget.TextViewCompat
fun TextView.setTextSizePercentOfScreenHeight(percentOfHeight: Float) {
val dm = context.resources.displayMetrics
val sp = (dm.heightPixels * percentOfHeight) / dm.scaledDensity
TextViewCompat.setAutoSizeTextTypeWithDefaults(this, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE)
setTextSize(TypedValue.COMPLEX_UNIT_SP, sp)
}
fun <T> Spinner.setupSpinner(items: List<T>, selectedItem: T?) {
val dm = context.resources.displayMetrics
fun spFromScreenHeight(percent: Float): Float = (dm.heightPixels * percent) / dm.scaledDensity
fun pxFromSp(sp: Float): Int = (sp * dm.scaledDensity).toInt()
val textSp = spFromScreenHeight(0.0275f)
val textPx = pxFromSp(textSp)
val vPadPx = (textPx * 0.50f).toInt()
val rowHeight = (textPx * 2.20f + 2 * vPadPx).toInt()
val adapter = object : ArrayAdapter<T>(context, android.R.layout.simple_spinner_item, items) {
private fun styleRow(tv: TextView, forceHeight: Boolean) {
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSp)
tv.includeFontPadding = true
tv.setLineSpacing(0f, 1.2f)
tv.gravity = (tv.gravity and Gravity.HORIZONTAL_GRAVITY_MASK) or Gravity.CENTER_VERTICAL
tv.setPadding(tv.paddingLeft, vPadPx, tv.paddingRight, vPadPx)
tv.minHeight = rowHeight
tv.isSingleLine = true
if (forceHeight) {
val lp = tv.layoutParams
if (lp == null || lp.height <= 0) {
tv.layoutParams = AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, rowHeight)
} else {
lp.height = rowHeight
}
}
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val v = super.getView(position, convertView, parent) as TextView
styleRow(v, forceHeight = false)
return v
}
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
val v = super.getDropDownView(position, convertView, parent) as TextView
styleRow(v, forceHeight = true)
return v
}
}
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
this.adapter = adapter
setPadding(paddingLeft, vPadPx, paddingRight, vPadPx)
minimumHeight = rowHeight
requestLayout()
selectedItem?.let {
val index = items.indexOf(it)
if (index >= 0) setSelection(index)
}
}

View File

@ -1,4 +1,4 @@
package com.dano.test1.network
package com.dano.test1
import java.io.File
import java.security.SecureRandom
@ -68,4 +68,4 @@ object AES256Helper {
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key, "AES"), IvParameterSpec(iv))
return cipher.doFinal(ct)
}
}
}

View File

@ -1,4 +1,4 @@
package com.dano.test1.util
package com.dano.test1
import com.dano.test1.questionnaire.MAX_VALUE_AGE
import com.dano.test1.questionnaire.MAX_VALUE_YEAR

View File

@ -1,4 +1,4 @@
package com.dano.test1.util
package com.dano.test1
import android.view.View
import android.view.ViewGroup

View File

@ -0,0 +1,110 @@
package com.dano.test1.utils
import android.content.Context
import android.util.TypedValue
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.AbsListView
import android.widget.ArrayAdapter
import android.widget.EditText
import android.widget.Spinner
import android.widget.TextView
import androidx.core.widget.TextViewCompat
import com.dano.test1.R
import kotlin.math.roundToInt
object ViewUtils {
/**
* Sets the text size of a TextView to a percentage of the screen height (in sp).
* Disables auto-sizing to prevent conflicts.
*/
fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) {
val dm = view.context.resources.displayMetrics
val sp = (dm.heightPixels * percentOfHeight) / dm.scaledDensity
TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE)
view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp)
}
/**
* Sets up a Spinner with a responsive, styled adapter.
* Font size and row height are derived from screen height to prevent clipping.
*/
fun <T> setupResponsiveSpinner(context: Context, spinner: Spinner, items: List<T>, selectedItem: T?) {
val dm = context.resources.displayMetrics
fun spFromScreenHeight(percent: Float): Float = (dm.heightPixels * percent) / dm.scaledDensity
fun pxFromSp(sp: Float): Int = (sp * dm.scaledDensity).toInt()
val textSp = spFromScreenHeight(0.0275f)
val textPx = pxFromSp(textSp)
val vPadPx = (textPx * 0.50f).toInt()
val rowHeight = (textPx * 2.20f + 2 * vPadPx).toInt()
val adapter = object : ArrayAdapter<T>(context, android.R.layout.simple_spinner_item, items) {
private fun styleRow(tv: TextView, forceHeight: Boolean) {
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, textSp)
tv.includeFontPadding = true
tv.setLineSpacing(0f, 1.2f)
tv.gravity = (tv.gravity and Gravity.HORIZONTAL_GRAVITY_MASK) or Gravity.CENTER_VERTICAL
tv.setPadding(tv.paddingLeft, vPadPx, tv.paddingRight, vPadPx)
tv.minHeight = rowHeight
tv.isSingleLine = true
if (forceHeight) {
val lp = tv.layoutParams
if (lp == null || lp.height <= 0) {
tv.layoutParams = AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT, rowHeight)
} else {
lp.height = rowHeight
}
}
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val v = super.getView(position, convertView, parent) as TextView
styleRow(v, forceHeight = false)
return v
}
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
val v = super.getDropDownView(position, convertView, parent) as TextView
styleRow(v, forceHeight = true)
return v
}
}
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = adapter
spinner.setPadding(spinner.paddingLeft, vPadPx, spinner.paddingRight, vPadPx)
spinner.minimumHeight = rowHeight
spinner.requestLayout()
selectedItem?.let {
val index = items.indexOf(it)
if (index >= 0) spinner.setSelection(index)
}
}
/**
* Locks an EditText field visually and functionally (e.g. for coach code fields).
*/
fun lockEditField(field: EditText, dpPadding: Int = 8) {
field.isFocusable = false
field.isFocusableInTouchMode = false
field.isCursorVisible = false
field.keyListener = null
field.isLongClickable = false
field.isClickable = false
field.setBackgroundResource(R.drawable.bg_field_locked)
field.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_lock_24, 0)
field.compoundDrawablePadding = dp(field.context, dpPadding)
field.alpha = 0.95f
}
/**
* Converts dp to pixels using the given context.
*/
fun dp(context: Context, value: Int): Int =
(value * context.resources.displayMetrics.density).roundToInt()
}