diff --git a/.idea/deviceManager.xml b/.idea/deviceManager.xml new file mode 100644 index 0000000..91f9558 --- /dev/null +++ b/.idea/deviceManager.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/dano/test1/MainActivity.kt b/app/src/main/java/com/dano/test1/MainActivity.kt index fe96e8b..99646fe 100644 --- a/app/src/main/java/com/dano/test1/MainActivity.kt +++ b/app/src/main/java/com/dano/test1/MainActivity.kt @@ -13,7 +13,11 @@ import android.widget.ProgressBar import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity -import java.io.File +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 class MainActivity : AppCompatActivity() { diff --git a/app/src/main/java/com/dano/test1/AppDatabase.kt b/app/src/main/java/com/dano/test1/data/AppDatabase.kt similarity index 100% rename from app/src/main/java/com/dano/test1/AppDatabase.kt rename to app/src/main/java/com/dano/test1/data/AppDatabase.kt diff --git a/app/src/main/java/com/dano/test1/Daos.kt b/app/src/main/java/com/dano/test1/data/Daos.kt similarity index 100% rename from app/src/main/java/com/dano/test1/Daos.kt rename to app/src/main/java/com/dano/test1/data/Daos.kt diff --git a/app/src/main/java/com/dano/test1/Entities.kt b/app/src/main/java/com/dano/test1/data/Entities.kt similarity index 100% rename from app/src/main/java/com/dano/test1/Entities.kt rename to app/src/main/java/com/dano/test1/data/Entities.kt diff --git a/app/src/main/java/com/dano/test1/ExcelExportService.kt b/app/src/main/java/com/dano/test1/data/ExcelExportService.kt similarity index 96% rename from app/src/main/java/com/dano/test1/ExcelExportService.kt rename to app/src/main/java/com/dano/test1/data/ExcelExportService.kt index 744f8af..2b0536d 100644 --- a/app/src/main/java/com/dano/test1/ExcelExportService.kt +++ b/app/src/main/java/com/dano/test1/data/ExcelExportService.kt @@ -1,4 +1,4 @@ -package com.dano.test1 +package com.dano.test1.data import android.content.ContentValues import android.content.Context @@ -7,8 +7,12 @@ import android.net.Uri import android.os.Build import android.os.Environment import android.provider.MediaStore +import com.dano.test1.LanguageManager +import com.dano.test1.MyApp import org.apache.poi.ss.usermodel.Row import org.apache.poi.xssf.usermodel.XSSFWorkbook +import java.io.ByteArrayOutputStream +import java.io.File /* Aufgabe: @@ -83,7 +87,7 @@ class ExcelExportService( } } - val bytes = java.io.ByteArrayOutputStream().use { bos -> + val bytes = ByteArrayOutputStream().use { bos -> wb.write(bos); bos.toByteArray() } wb.close() @@ -112,7 +116,7 @@ class ExcelExportService( } else { val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) if (!downloadsDir.exists()) downloadsDir.mkdirs() - val outFile = java.io.File(downloadsDir, filename) + val outFile = File(downloadsDir, filename) outFile.writeBytes(bytes) MediaScannerConnection.scanFile( context, @@ -175,4 +179,4 @@ class ExcelExportService( for (key in candidates) localizeEnglishNoBrackets(key)?.let { return it } return raw } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/dano/test1/HeaderOrderRepository.kt b/app/src/main/java/com/dano/test1/data/HeaderOrderRepository.kt similarity index 86% rename from app/src/main/java/com/dano/test1/HeaderOrderRepository.kt rename to app/src/main/java/com/dano/test1/data/HeaderOrderRepository.kt index a9df8ea..b42db5c 100644 --- a/app/src/main/java/com/dano/test1/HeaderOrderRepository.kt +++ b/app/src/main/java/com/dano/test1/data/HeaderOrderRepository.kt @@ -1,8 +1,11 @@ -package com.dano.test1 +package com.dano.test1.data import android.content.Context import android.util.Log import android.widget.Toast +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 import org.json.JSONArray import java.nio.charset.Charset @@ -63,16 +66,16 @@ class HeaderOrderRepository( for (i in first until last) { val cell = row.getCell(i) ?: continue val value = when (cell.cellType) { - org.apache.poi.ss.usermodel.CellType.STRING -> cell.stringCellValue - org.apache.poi.ss.usermodel.CellType.NUMERIC -> - if (org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) + CellType.STRING -> cell.stringCellValue + CellType.NUMERIC -> + if (DateUtil.isCellDateFormatted(cell)) cell.dateCellValue.time.toString() else { val n = cell.numericCellValue if (n % 1.0 == 0.0) n.toLong().toString() else n.toString() } - org.apache.poi.ss.usermodel.CellType.BOOLEAN -> cell.booleanCellValue.toString() - org.apache.poi.ss.usermodel.CellType.FORMULA -> cell.richStringCellValue.string + CellType.BOOLEAN -> cell.booleanCellValue.toString() + CellType.FORMULA -> cell.richStringCellValue.string else -> "" }.trim() diff --git a/app/src/main/java/com/dano/test1/DatabaseDownloader.kt b/app/src/main/java/com/dano/test1/network/DatabaseDownloader.kt similarity index 97% rename from app/src/main/java/com/dano/test1/DatabaseDownloader.kt rename to app/src/main/java/com/dano/test1/network/DatabaseDownloader.kt index 39aa418..178c5d9 100644 --- a/app/src/main/java/com/dano/test1/DatabaseDownloader.kt +++ b/app/src/main/java/com/dano/test1/network/DatabaseDownloader.kt @@ -1,7 +1,8 @@ -package com.dano.test1 +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 diff --git a/app/src/main/java/com/dano/test1/DatabaseUploader.kt b/app/src/main/java/com/dano/test1/network/DatabaseUploader.kt similarity index 99% rename from app/src/main/java/com/dano/test1/DatabaseUploader.kt rename to app/src/main/java/com/dano/test1/network/DatabaseUploader.kt index b054f7c..09814e9 100644 --- a/app/src/main/java/com/dano/test1/DatabaseUploader.kt +++ b/app/src/main/java/com/dano/test1/network/DatabaseUploader.kt @@ -1,4 +1,4 @@ -package com.dano.test1 +package com.dano.test1.network import android.content.Context import android.database.sqlite.SQLiteDatabase @@ -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 diff --git a/app/src/main/java/com/dano/test1/LoginManager.kt b/app/src/main/java/com/dano/test1/network/LoginManager.kt similarity index 95% rename from app/src/main/java/com/dano/test1/LoginManager.kt rename to app/src/main/java/com/dano/test1/network/LoginManager.kt index ce7b22d..8e3732f 100644 --- a/app/src/main/java/com/dano/test1/LoginManager.kt +++ b/app/src/main/java/com/dano/test1/network/LoginManager.kt @@ -1,9 +1,9 @@ -package com.dano.test1 +package com.dano.test1.network import android.app.AlertDialog import android.content.Context +import android.text.InputType import android.util.Log -import android.view.LayoutInflater import android.widget.EditText import android.widget.LinearLayout import android.widget.Toast @@ -96,13 +96,13 @@ object LoginManager { } val etNew = EditText(context).apply { hint = "Neues Passwort" - inputType = android.text.InputType.TYPE_CLASS_TEXT or - android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD + inputType = InputType.TYPE_CLASS_TEXT or + InputType.TYPE_TEXT_VARIATION_PASSWORD } val etRepeat = EditText(context).apply { hint = "Neues Passwort (wiederholen)" - inputType = android.text.InputType.TYPE_CLASS_TEXT or - android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD + inputType = InputType.TYPE_CLASS_TEXT or + InputType.TYPE_TEXT_VARIATION_PASSWORD } container.addView(etNew) container.addView(etRepeat) diff --git a/app/src/main/java/com/dano/test1/NetworkUtilis.kt b/app/src/main/java/com/dano/test1/network/NetworkUtilis.kt similarity index 97% rename from app/src/main/java/com/dano/test1/NetworkUtilis.kt rename to app/src/main/java/com/dano/test1/network/NetworkUtilis.kt index a59405a..0a2bb0e 100644 --- a/app/src/main/java/com/dano/test1/NetworkUtilis.kt +++ b/app/src/main/java/com/dano/test1/network/NetworkUtilis.kt @@ -1,4 +1,4 @@ -package com.dano.test1 +package com.dano.test1.network import android.content.Context import android.net.ConnectivityManager diff --git a/app/src/main/java/com/dano/test1/TokenStore.kt b/app/src/main/java/com/dano/test1/network/TokenStore.kt similarity index 98% rename from app/src/main/java/com/dano/test1/TokenStore.kt rename to app/src/main/java/com/dano/test1/network/TokenStore.kt index 44b950a..86a8d1f 100644 --- a/app/src/main/java/com/dano/test1/TokenStore.kt +++ b/app/src/main/java/com/dano/test1/network/TokenStore.kt @@ -1,4 +1,4 @@ -package com.dano.test1 +package com.dano.test1.network import android.content.Context diff --git a/app/src/main/java/com/dano/test1/QuestionHandler.kt b/app/src/main/java/com/dano/test1/questionnaire/QuestionHandler.kt similarity index 80% rename from app/src/main/java/com/dano/test1/QuestionHandler.kt rename to app/src/main/java/com/dano/test1/questionnaire/QuestionHandler.kt index 63f8d51..9660676 100644 --- a/app/src/main/java/com/dano/test1/QuestionHandler.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/QuestionHandler.kt @@ -1,4 +1,4 @@ -package com.dano.test1 +package com.dano.test1.questionnaire import android.view.View interface QuestionHandler { diff --git a/app/src/main/java/com/dano/test1/QuestionnaireBase.kt b/app/src/main/java/com/dano/test1/questionnaire/QuestionnaireBase.kt similarity index 90% rename from app/src/main/java/com/dano/test1/QuestionnaireBase.kt rename to app/src/main/java/com/dano/test1/questionnaire/QuestionnaireBase.kt index f97b609..466f6d7 100644 --- a/app/src/main/java/com/dano/test1/QuestionnaireBase.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/QuestionnaireBase.kt @@ -1,10 +1,23 @@ -package com.dano.test1 +package com.dano.test1.questionnaire +import android.R import android.app.Activity import android.util.Log import android.view.View import android.widget.* +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.* @@ -61,8 +74,8 @@ abstract class QuestionnaireBase { } protected fun setupSpinner(spinner: Spinner, spinnerValues: List, selectedValue: Any?) { - val adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, spinnerValues).apply { - setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + val adapter = ArrayAdapter(context, R.layout.simple_spinner_item, spinnerValues).apply { + setDropDownViewResource(R.layout.simple_spinner_dropdown_item) } spinner.adapter = adapter selectedValue?.let { value -> @@ -75,7 +88,7 @@ abstract class QuestionnaireBase { protected fun navigateTo(layoutResId: Int, setup: (View) -> Unit) { context.setContentView(layoutResId) - val rootView = context.findViewById(android.R.id.content) + val rootView = context.findViewById(R.id.content) setup(rootView) } @@ -85,7 +98,7 @@ abstract class QuestionnaireBase { protected fun showEmptyScreen() { navigateTo(getLayoutResId("empty")) { - setupPrevButton(R.id.Qprev) { goToPreviousQuestion() } + setupPrevButton(com.dano.test1.R.id.Qprev) { goToPreviousQuestion() } } } diff --git a/app/src/main/java/com/dano/test1/QuestionnaireGeneric.kt b/app/src/main/java/com/dano/test1/questionnaire/QuestionnaireGeneric.kt similarity index 91% rename from app/src/main/java/com/dano/test1/QuestionnaireGeneric.kt rename to app/src/main/java/com/dano/test1/questionnaire/QuestionnaireGeneric.kt index 36f1c03..3e95693 100644 --- a/app/src/main/java/com/dano/test1/QuestionnaireGeneric.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/QuestionnaireGeneric.kt @@ -1,6 +1,8 @@ -package com.dano.test1 +package com.dano.test1.questionnaire import android.widget.Button +import com.dano.test1.LocalizationHelper +import com.dano.test1.R open class QuestionnaireGeneric(private val questionnaireFileName: String) : QuestionnaireBase() { diff --git a/app/src/main/java/com/dano/test1/QuestionnaireItem.kt b/app/src/main/java/com/dano/test1/questionnaire/QuestionnaireItem.kt similarity index 99% rename from app/src/main/java/com/dano/test1/QuestionnaireItem.kt rename to app/src/main/java/com/dano/test1/questionnaire/QuestionnaireItem.kt index 1710b22..bc15005 100644 --- a/app/src/main/java/com/dano/test1/QuestionnaireItem.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/QuestionnaireItem.kt @@ -1,4 +1,4 @@ -package com.dano.test1 +package com.dano.test1.questionnaire data class Option( val key: String, // Must always be set diff --git a/app/src/main/java/com/dano/test1/HandlerClientCoachCode.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerClientCoachCode.kt similarity index 78% rename from app/src/main/java/com/dano/test1/HandlerClientCoachCode.kt rename to app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerClientCoachCode.kt index 3103ba9..e4d0525 100644 --- a/app/src/main/java/com/dano/test1/HandlerClientCoachCode.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerClientCoachCode.kt @@ -1,9 +1,15 @@ -package com.dano.test1 +package com.dano.test1.questionnaire.handlers import android.view.View import android.widget.* -import android.util.TypedValue -import androidx.core.widget.TextViewCompat +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.network.TokenStore +import com.dano.test1.utils.ViewUtils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -38,10 +44,10 @@ class HandlerClientCoachCode( questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: "" - setTextSizePercentOfScreenHeight(titleTextView, 0.03f) - setTextSizePercentOfScreenHeight(questionTextView, 0.03f) - setTextSizePercentOfScreenHeight(clientCodeField, 0.025f) - setTextSizePercentOfScreenHeight(coachCodeField, 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 @@ -57,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 ?: "") @@ -72,13 +78,6 @@ class HandlerClientCoachCode( } } - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = layout.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) - } - private fun onNextClicked(clientCodeField: EditText, coachCodeField: EditText) { val loadedClientCode = GlobalValues.LOADED_CLIENT_CODE @@ -143,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() } diff --git a/app/src/main/java/com/dano/test1/HandlerClientNotSigned.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerClientNotSigned.kt similarity index 92% rename from app/src/main/java/com/dano/test1/HandlerClientNotSigned.kt rename to app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerClientNotSigned.kt index 34b91f8..3fbb290 100644 --- a/app/src/main/java/com/dano/test1/HandlerClientNotSigned.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerClientNotSigned.kt @@ -1,7 +1,11 @@ -package com.dano.test1 +package com.dano.test1.questionnaire.handlers import android.view.View import android.widget.* +import com.dano.test1.LanguageManager +import com.dano.test1.questionnaire.QuestionHandler +import com.dano.test1.questionnaire.QuestionItem +import com.dano.test1.R /* Zweck: diff --git a/app/src/main/java/com/dano/test1/HandlerDateSpinner.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerDateSpinner.kt similarity index 66% rename from app/src/main/java/com/dano/test1/HandlerDateSpinner.kt rename to app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerDateSpinner.kt index 55ceede..3823a9d 100644 --- a/app/src/main/java/com/dano/test1/HandlerDateSpinner.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerDateSpinner.kt @@ -1,15 +1,21 @@ -package com.dano.test1 +package com.dano.test1.questionnaire.handlers import android.content.Context import android.view.View -import android.view.ViewGroup import android.widget.* import kotlinx.coroutines.* import java.text.SimpleDateFormat import java.util.* -import android.util.TypedValue -import androidx.core.widget.TextViewCompat -import android.widget.AbsListView +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: @@ -52,12 +58,11 @@ class HandlerDateSpinner( textView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: "" // Schriftgrößen pro Bildschirmhöhe - setTextSizePercentOfScreenHeight(textView, 0.03f) // oben - setTextSizePercentOfScreenHeight(questionTextView, 0.03f) // frage - setTextSizePercentOfScreenHeight(labelDay, 0.025f) - setTextSizePercentOfScreenHeight(labelMonth, 0.025f) - setTextSizePercentOfScreenHeight(labelYear, 0.025f) - // + 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 +80,9 @@ class HandlerDateSpinner( val defaultYear = savedYear ?: today.get(Calendar.YEAR) // Spinner responsiv aufsetzen (Schrift + Zeilenhöhe ohne Abschneiden) - setupSpinner(spinnerDay, days, defaultDay) - setupSpinner(spinnerMonth, months, defaultMonth) - setupSpinner(spinnerYear, years, defaultYear) + 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 ?: "") @@ -207,71 +212,4 @@ class HandlerDateSpinner( return sdf.parse(dateString) } - // Textgröße prozentual zur Bildschirmhöhe (in sp) - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = (view.context ?: layout.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) - } - - // Spinner-Adapter: Schrift & Zeilenhöhe dynamisch, kein Abschneiden - private fun setupSpinner(spinner: Spinner, items: List, defaultSelection: 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) // ~2.75% der Bildschirmhöhe - val textPx = pxFromSp(textSp) - val vPadPx = (textPx * 0.50f).toInt() // vertikales Padding - val rowHeight = (textPx * 2.20f + 2 * vPadPx).toInt() // feste Zeilenhöhe - - val adapter = object : ArrayAdapter(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 android.view.Gravity.HORIZONTAL_GRAVITY_MASK) or android.view.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() - - defaultSelection?.let { - val index = items.indexOf(it) - if (index >= 0) spinner.setSelection(index) - } - } } diff --git a/app/src/main/java/com/dano/test1/HandlerGlassScaleQuestion.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerGlassScaleQuestion.kt similarity index 94% rename from app/src/main/java/com/dano/test1/HandlerGlassScaleQuestion.kt rename to app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerGlassScaleQuestion.kt index 503b757..32d9825 100644 --- a/app/src/main/java/com/dano/test1/HandlerGlassScaleQuestion.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerGlassScaleQuestion.kt @@ -1,11 +1,16 @@ -package com.dano.test1 +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 androidx.core.widget.TextViewCompat +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.* /* @@ -67,8 +72,8 @@ class HandlerGlassScaleQuestion( titleTv.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: "" questionTv.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: "" - setTextSizePercentOfScreenHeight(titleTv, 0.03f) - setTextSizePercentOfScreenHeight(questionTv, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(titleTv, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(questionTv, 0.03f) // Header Icons val header = layout.findViewById(R.id.glass_header) @@ -150,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(this, 0.022f) + ViewUtils.setTextSizePercentOfScreenHeight(this, 0.022f) } row.addView(symptomText) @@ -277,10 +282,4 @@ class HandlerGlassScaleQuestion( else -> null } - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = (view.context ?: layout.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) - } } \ No newline at end of file diff --git a/app/src/main/java/com/dano/test1/HandlerLastPage.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerLastPage.kt similarity index 89% rename from app/src/main/java/com/dano/test1/HandlerLastPage.kt rename to app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerLastPage.kt index 55c28d9..a4c6e20 100644 --- a/app/src/main/java/com/dano/test1/HandlerLastPage.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerLastPage.kt @@ -1,12 +1,18 @@ -package com.dano.test1 +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 android.widget.TextView import androidx.core.widget.TextViewCompat +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 /* @@ -58,8 +64,8 @@ class HandlerLastPage( applyResponsiveTextSizing(finishBtn) // Überschriften responsiv skalieren (wie zuvor) - setTextSizePercentOfScreenHeight(titleTv, 0.03f) - setTextSizePercentOfScreenHeight(questionTv, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(titleTv, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(questionTv, 0.03f) // Buttons prevBtn.setOnClickListener { goToPreviousQuestion() } @@ -128,14 +134,6 @@ class HandlerLastPage( } // ---------------------------------------------------------------- - // Helper: Textgröße prozentual zur Bildschirmhöhe setzen (in sp) - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = (view.context ?: layout.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) - } - private fun sumPoints(): Int = answers.filterKeys { it.endsWith("_points") } .values.mapNotNull { it as? Int } diff --git a/app/src/main/java/com/dano/test1/HandlerMultiCheckboxQuestion.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerMultiCheckboxQuestion.kt similarity index 93% rename from app/src/main/java/com/dano/test1/HandlerMultiCheckboxQuestion.kt rename to app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerMultiCheckboxQuestion.kt index e24aa26..d4721e5 100644 --- a/app/src/main/java/com/dano/test1/HandlerMultiCheckboxQuestion.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerMultiCheckboxQuestion.kt @@ -1,11 +1,18 @@ -package com.dano.test1 +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 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: @@ -38,8 +45,8 @@ class HandlerMultiCheckboxQuestion( questionTitle.text = this.question.question?.let { LanguageManager.getText(languageID, it) } ?: "" // Textgrößen pro Bildschirmhöhe (wie bei deinen anderen Handlern) - setTextSizePercentOfScreenHeight(questionTextView, 0.03f) // Überschrift - setTextSizePercentOfScreenHeight(questionTitle, 0.03f) // Frage + ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(questionTitle, 0.03f) container.removeAllViews() @@ -204,10 +211,4 @@ class HandlerMultiCheckboxQuestion( } } - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = (view.context ?: layout.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) - } } diff --git a/app/src/main/java/com/dano/test1/HandlerRadioQuestion.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerRadioQuestion.kt similarity index 85% rename from app/src/main/java/com/dano/test1/HandlerRadioQuestion.kt rename to app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerRadioQuestion.kt index 59bf3a0..c6431d5 100644 --- a/app/src/main/java/com/dano/test1/HandlerRadioQuestion.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerRadioQuestion.kt @@ -1,12 +1,17 @@ -package com.dano.test1 +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 android.util.TypedValue -import androidx.core.widget.TextViewCompat // <— hinzugefügt +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,11 +46,8 @@ class HandlerRadioQuestion( Html.fromHtml(LanguageManager.getText(languageID, it), Html.FROM_HTML_MODE_LEGACY) } ?: "" - // - // Titel/Frage: 3% der Bildschirmhöhe - setTextSizePercentOfScreenHeight(questionTextView, 0.03f) - setTextSizePercentOfScreenHeight(questionTitle, 0.03f) - // =================================================== + ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(questionTitle, 0.03f) radioGroup.removeAllViews() @@ -55,7 +57,7 @@ class HandlerRadioQuestion( tag = option.key // RadioButton-Text analog zu EditTexts: 2.5% der Bildschirmhöhe - setTextSizePercentOfScreenHeight(this, 0.025f) + ViewUtils.setTextSizePercentOfScreenHeight(this, 0.025f) layoutParams = RadioGroup.LayoutParams( RadioGroup.LayoutParams.MATCH_PARENT, @@ -137,15 +139,6 @@ class HandlerRadioQuestion( } } - // setzt Textgröße prozentual zur Bildschirmhöhe (in sp) - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = (view.context ?: layout.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) - } - // ———————————————————————————————————————————————————————————————— - private fun restorePreviousAnswer(radioGroup: RadioGroup) { question.question?.let { questionKey -> val savedAnswer = answers[questionKey] as? String diff --git a/app/src/main/java/com/dano/test1/HandlerStringSpinner.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerStringSpinner.kt similarity index 51% rename from app/src/main/java/com/dano/test1/HandlerStringSpinner.kt rename to app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerStringSpinner.kt index b101569..aad8f74 100644 --- a/app/src/main/java/com/dano/test1/HandlerStringSpinner.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerStringSpinner.kt @@ -1,13 +1,17 @@ -package com.dano.test1 +package com.dano.test1.questionnaire.handlers import android.content.Context import android.view.View -import android.view.ViewGroup import android.widget.* import kotlinx.coroutines.* -import android.util.TypedValue -import android.widget.TextView -import androidx.core.widget.TextViewCompat +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.utils.ViewUtils /* Zweck: @@ -42,9 +46,9 @@ class HandlerStringSpinner( questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: "" textView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: "" - // Textgrößen prozentual zur Bildschirmhöhe (wie im HandlerRadioQuestion) - setTextSizePercentOfScreenHeight(textView, 0.03f) - setTextSizePercentOfScreenHeight(questionTextView, 0.03f) + // Textgrößen prozentual zur Bildschirmhöhe + ViewUtils.setTextSizePercentOfScreenHeight(textView, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f) val options = buildOptionsList() @@ -52,7 +56,7 @@ class HandlerStringSpinner( val savedSelection = question.question?.let { answers[it] as? String } // Spinner aufsetzen - setupSpinner(spinner, options, savedSelection) + ViewUtils.setupResponsiveSpinner(context, spinner, options, savedSelection) // Falls noch keine Antwort im Map: aus DB laden val answerMapKey = question.question ?: (question.id ?: "") @@ -119,73 +123,4 @@ class HandlerStringSpinner( } } - // Textgröße prozentual zur Bildschirmhöhe setzen und AutoSize deaktivieren - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = (view.context ?: layout.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) - } - - // Spinner-Adapter mit dynamischer Schrift & stabiler Dropdown-Zeilenhöhe (kein Abschneiden) - private fun setupSpinner(spinner: Spinner, items: List, 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() - - // Schrift & abgeleitete Höhen (wie beim Value-Spinner-Fix) - val textSp = spFromScreenHeight(0.0275f) // ~2.75% der Bildschirmhöhe - val textPx = pxFromSp(textSp) - val vPadPx = (textPx * 0.50f).toInt() // vertikales Padding - val rowHeight = (textPx * 2.20f + 2 * vPadPx).toInt() // feste Zeilenhöhe, verhindert Abschneiden - - val adapter = object : ArrayAdapter(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 android.view.Gravity.HORIZONTAL_GRAVITY_MASK) or android.view.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) // ausgewählte Ansicht - 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) // Dropdown-Zeilen: Höhe erzwingen - return v - } - } - - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - spinner.adapter = adapter - - // Spinner selbst ausreichend hoch machen - 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) - } - } } diff --git a/app/src/main/java/com/dano/test1/HandlerValueSpinner.kt b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerValueSpinner.kt similarity index 55% rename from app/src/main/java/com/dano/test1/HandlerValueSpinner.kt rename to app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerValueSpinner.kt index d831d64..98eb66e 100644 --- a/app/src/main/java/com/dano/test1/HandlerValueSpinner.kt +++ b/app/src/main/java/com/dano/test1/questionnaire/handlers/HandlerValueSpinner.kt @@ -1,12 +1,16 @@ -package com.dano.test1 +package com.dano.test1.questionnaire.handlers import android.content.Context import android.view.View -import android.view.ViewGroup import android.widget.* import kotlinx.coroutines.* -import android.util.TypedValue -import androidx.core.widget.TextViewCompat // <- NEU +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,10 +47,8 @@ class HandlerValueSpinner( questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: "" textView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: "" -// Schriftgrößen wie im HandlerRadioQuestion - // Titel/Frage: 3% der Bildschirmhöhe - setTextSizePercentOfScreenHeight(textView, 0.03f) - setTextSizePercentOfScreenHeight(questionTextView, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(textView, 0.03f) + ViewUtils.setTextSizePercentOfScreenHeight(questionTextView, 0.03f) val prompt = LanguageManager.getText(languageID, "choose_answer") val spinnerItems: List = listOf(prompt) + if (question.range != null) { @@ -56,7 +58,7 @@ class HandlerValueSpinner( } val savedValue = question.question?.let { answers[it] as? String } - setupSpinner(spinner, spinnerItems, savedValue) + ViewUtils.setupResponsiveSpinner(context, spinner, spinnerItems, savedValue) //DB-Abfrage falls noch keine Antwort im Map existiert val answerMapKey = question.question ?: (question.id ?: "") @@ -127,72 +129,4 @@ class HandlerValueSpinner( } } - // setzt Textgröße prozentual zur Bildschirmhöhe (in sp) - private fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { - val dm = (view.context ?: layout.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) - } - - private fun setupSpinner(spinner: Spinner, items: List, 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() - - // Schrift & abgeleitete Höhen - val textSp = spFromScreenHeight(0.0275f) // ~2.75% der Bildschirmhöhe - val textPx = pxFromSp(textSp) - val vPadPx = (textPx * 0.50f).toInt() // vertikales Padding - val rowHeight = (textPx * 2.20f + 2 * vPadPx).toInt() // feste Zeilenhöhe - - val adapter = object : ArrayAdapter(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 android.view.Gravity.HORIZONTAL_GRAVITY_MASK) or android.view.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) // ausgewählte Ansicht - 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) // Dropdown-Zeilen: Höhe erzwingen - return v - } - } - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - spinner.adapter = adapter - - // Spinner selbst ausreichend hoch machen - 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) - } - } - } diff --git a/app/src/main/java/com/dano/test1/CountriesSpinner.kt b/app/src/main/java/com/dano/test1/ui/CountriesSpinner.kt similarity index 98% rename from app/src/main/java/com/dano/test1/CountriesSpinner.kt rename to app/src/main/java/com/dano/test1/ui/CountriesSpinner.kt index 439d171..4350e7d 100644 --- a/app/src/main/java/com/dano/test1/CountriesSpinner.kt +++ b/app/src/main/java/com/dano/test1/ui/CountriesSpinner.kt @@ -1,4 +1,6 @@ -package com.dano.test1 +package com.dano.test1.ui + +import com.dano.test1.LanguageManager object Countries { fun getAllCountries(languageID: String): List { diff --git a/app/src/main/java/com/dano/test1/DatabaseButtonHandler.kt b/app/src/main/java/com/dano/test1/ui/DatabaseButtonHandler.kt similarity index 97% rename from app/src/main/java/com/dano/test1/DatabaseButtonHandler.kt rename to app/src/main/java/com/dano/test1/ui/DatabaseButtonHandler.kt index ce50a2b..f4a0a74 100644 --- a/app/src/main/java/com/dano/test1/DatabaseButtonHandler.kt +++ b/app/src/main/java/com/dano/test1/ui/DatabaseButtonHandler.kt @@ -1,13 +1,21 @@ -package com.dano.test1 +package com.dano.test1.ui +import android.graphics.Color +import android.graphics.Typeface import android.util.Log import android.view.View 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.* -import kotlin.math.roundToInt import org.json.JSONArray class DatabaseButtonHandler( @@ -361,7 +369,7 @@ class DatabaseButtonHandler( val row = TableRow(activity).apply { isClickable = true isFocusable = true - setBackgroundColor(android.graphics.Color.TRANSPARENT) + setBackgroundColor(Color.TRANSPARENT) setOnClickListener { onClick() } } cells.forEachIndexed { index, text -> @@ -396,7 +404,7 @@ class DatabaseButtonHandler( this.text = text setPadding(dp(12), dp(10), dp(12), dp(10)) textSize = 16f - setTypeface(typeface, android.graphics.Typeface.BOLD) + setTypeface(typeface, Typeface.BOLD) } private fun makeBodyCell( @@ -412,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 requireView(id: Int, name: String): T { val v = activity.findViewById(id) diff --git a/app/src/main/java/com/dano/test1/EditButtonHandler.kt b/app/src/main/java/com/dano/test1/ui/EditButtonHandler.kt similarity index 95% rename from app/src/main/java/com/dano/test1/EditButtonHandler.kt rename to app/src/main/java/com/dano/test1/ui/EditButtonHandler.kt index f0fa422..af170ea 100644 --- a/app/src/main/java/com/dano/test1/EditButtonHandler.kt +++ b/app/src/main/java/com/dano/test1/ui/EditButtonHandler.kt @@ -1,10 +1,14 @@ -package com.dano.test1 +package com.dano.test1.ui import android.widget.Button import android.widget.EditText import android.widget.Toast +import com.dano.test1.LanguageManager +import com.dano.test1.MainActivity +import com.dano.test1.MyApp import kotlinx.coroutines.* import com.dano.test1.data.CompletedQuestionnaire +import com.dano.test1.questionnaire.GlobalValues class EditButtonHandler( private val activity: MainActivity, diff --git a/app/src/main/java/com/dano/test1/HandlerOpeningScreen.kt b/app/src/main/java/com/dano/test1/ui/HandlerOpeningScreen.kt similarity index 95% rename from app/src/main/java/com/dano/test1/HandlerOpeningScreen.kt rename to app/src/main/java/com/dano/test1/ui/HandlerOpeningScreen.kt index f3909f8..d6f86c3 100644 --- a/app/src/main/java/com/dano/test1/HandlerOpeningScreen.kt +++ b/app/src/main/java/com/dano/test1/ui/HandlerOpeningScreen.kt @@ -1,19 +1,33 @@ -package com.dano.test1 +package com.dano.test1.ui +import android.content.Context import android.content.res.ColorStateList import android.graphics.Color +import android.graphics.drawable.GradientDrawable import android.os.Handler import android.os.Looper import android.util.TypedValue import android.view.Gravity import android.view.View import android.widget.* +import com.dano.test1.LanguageManager +import com.dano.test1.MainActivity +import com.dano.test1.R +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 +import com.dano.test1.questionnaire.QuestionnaireGeneric import com.google.android.material.button.MaterialButton import org.json.JSONArray 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 @@ -559,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 @@ -651,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) { @@ -682,22 +687,22 @@ class HandlerOpeningScreen(private val activity: MainActivity) { - private fun showRedToast(ctx: android.content.Context, message: String) { - val tv = android.widget.TextView(ctx).apply { + private fun showRedToast(ctx: Context, message: String) { + val tv = TextView(ctx).apply { text = message - setTextColor(android.graphics.Color.WHITE) + setTextColor(Color.WHITE) textSize = 16f setPadding(32, 20, 32, 20) - background = android.graphics.drawable.GradientDrawable().apply { - shape = android.graphics.drawable.GradientDrawable.RECTANGLE + background = GradientDrawable().apply { + shape = GradientDrawable.RECTANGLE cornerRadius = 24f - setColor(android.graphics.Color.parseColor("#D32F2F")) // kräftiges Rot + setColor(Color.parseColor("#D32F2F")) // kräftiges Rot } } - android.widget.Toast(ctx).apply { - duration = android.widget.Toast.LENGTH_LONG + Toast(ctx).apply { + duration = Toast.LENGTH_LONG view = tv - setGravity(android.view.Gravity.TOP or android.view.Gravity.CENTER_HORIZONTAL, 0, 120) + setGravity(Gravity.TOP or Gravity.CENTER_HORIZONTAL, 0, 120) }.show() } diff --git a/app/src/main/java/com/dano/test1/LoadButtonHandler.kt b/app/src/main/java/com/dano/test1/ui/LoadButtonHandler.kt similarity index 96% rename from app/src/main/java/com/dano/test1/LoadButtonHandler.kt rename to app/src/main/java/com/dano/test1/ui/LoadButtonHandler.kt index dd2b699..8cde1c2 100644 --- a/app/src/main/java/com/dano/test1/LoadButtonHandler.kt +++ b/app/src/main/java/com/dano/test1/ui/LoadButtonHandler.kt @@ -1,10 +1,15 @@ -package com.dano.test1 +package com.dano.test1.ui import android.widget.Button import android.widget.EditText import android.widget.Toast +import com.dano.test1.LanguageManager +import com.dano.test1.MainActivity +import com.dano.test1.MyApp import kotlinx.coroutines.* import com.dano.test1.data.CompletedQuestionnaire +import com.dano.test1.questionnaire.GlobalValues +import com.dano.test1.questionnaire.QuestionItem class LoadButtonHandler( private val activity: MainActivity, diff --git a/app/src/main/java/com/dano/test1/MonthsSpinner.kt b/app/src/main/java/com/dano/test1/ui/MonthsSpinner.kt similarity index 93% rename from app/src/main/java/com/dano/test1/MonthsSpinner.kt rename to app/src/main/java/com/dano/test1/ui/MonthsSpinner.kt index 8d30a82..1e25bf7 100644 --- a/app/src/main/java/com/dano/test1/MonthsSpinner.kt +++ b/app/src/main/java/com/dano/test1/ui/MonthsSpinner.kt @@ -1,4 +1,6 @@ -package com.dano.test1 +package com.dano.test1.ui + +import com.dano.test1.LanguageManager data class Month(val name: String) { override fun toString(): String = name diff --git a/app/src/main/java/com/dano/test1/SaveButtonHandler.kt b/app/src/main/java/com/dano/test1/ui/SaveButtonHandler.kt similarity index 82% rename from app/src/main/java/com/dano/test1/SaveButtonHandler.kt rename to app/src/main/java/com/dano/test1/ui/SaveButtonHandler.kt index 47812fc..4077eb7 100644 --- a/app/src/main/java/com/dano/test1/SaveButtonHandler.kt +++ b/app/src/main/java/com/dano/test1/ui/SaveButtonHandler.kt @@ -1,12 +1,21 @@ -package com.dano.test1 +package com.dano.test1.ui +import android.content.ActivityNotFoundException +import android.content.ContentUris +import android.content.ContentValues +import android.content.Intent import android.graphics.Canvas import android.graphics.Paint import android.graphics.pdf.PdfDocument +import android.provider.MediaStore import android.util.Log import android.widget.Button import android.widget.EditText import android.widget.Toast +import com.dano.test1.LanguageManager +import com.dano.test1.MainActivity +import com.dano.test1.MyApp +import com.dano.test1.questionnaire.GlobalValues import kotlinx.coroutines.* class SaveButtonHandler( @@ -105,19 +114,19 @@ class SaveButtonHandler( 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 projection = arrayOf(MediaStore.MediaColumns._ID) + val selection = "${MediaStore.MediaColumns.DISPLAY_NAME} = ?" val selectionArgs = arrayOf(name) val query = resolver.query( - android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI, + 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 idColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID) val id = cursor.getLong(idColumn) - val deleteUri = android.content.ContentUris.withAppendedId( - android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI, id + val deleteUri = ContentUris.withAppendedId( + MediaStore.Downloads.EXTERNAL_CONTENT_URI, id ) resolver.delete(deleteUri, null, null) } @@ -129,20 +138,20 @@ class SaveButtonHandler( 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/") + MediaStore.Downloads.EXTERNAL_CONTENT_URI, + ContentValues().apply { + put(MediaStore.MediaColumns.DISPLAY_NAME, pdfFileName) + put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf") + put(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/") + MediaStore.Downloads.EXTERNAL_CONTENT_URI, + ContentValues().apply { + put(MediaStore.MediaColumns.DISPLAY_NAME, csvFileName) + put(MediaStore.MediaColumns.MIME_TYPE, "text/csv") + put(MediaStore.MediaColumns.RELATIVE_PATH, "Download/") } ) @@ -162,13 +171,13 @@ class SaveButtonHandler( Toast.makeText(activity, msg, Toast.LENGTH_LONG).show() pdfUri?.let { - val intent = android.content.Intent(android.content.Intent.ACTION_VIEW).apply { + val intent = Intent(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) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NO_HISTORY) } try { activity.startActivity(intent) - } catch (e: android.content.ActivityNotFoundException) { + } catch (e: ActivityNotFoundException) { val noViewer = LanguageManager.getText(languageIDProvider(), "no_pdf_viewer") Toast.makeText(activity, noViewer, Toast.LENGTH_SHORT).show() } diff --git a/app/src/main/java/com/dano/test1/AES256Helper.kt b/app/src/main/java/com/dano/test1/utils/AES256Helper.kt similarity index 100% rename from app/src/main/java/com/dano/test1/AES256Helper.kt rename to app/src/main/java/com/dano/test1/utils/AES256Helper.kt diff --git a/app/src/main/java/com/dano/test1/LanguageManager.kt b/app/src/main/java/com/dano/test1/utils/LanguageManager.kt similarity index 99% rename from app/src/main/java/com/dano/test1/LanguageManager.kt rename to app/src/main/java/com/dano/test1/utils/LanguageManager.kt index f8e2415..619040e 100644 --- a/app/src/main/java/com/dano/test1/LanguageManager.kt +++ b/app/src/main/java/com/dano/test1/utils/LanguageManager.kt @@ -1,6 +1,8 @@ package com.dano.test1 -import java.util.Calendar +import com.dano.test1.questionnaire.MAX_VALUE_AGE +import com.dano.test1.questionnaire.MAX_VALUE_YEAR +import com.dano.test1.ui.RHS_POINTS object LanguageManager { fun getTextFormatted(languageId: String, key: String, vararg args: Any): String { @@ -61,14 +63,14 @@ object LanguageManager { "once" to "einmal", "year_after_2000" to "Das Jahr muss nach 2000 liegen!", "year_after_departure" to "Das Jahr muss nach dem Verlassen des Herkunftslandes liegen!", - "year_max" to "Das Jahr muss kleiner oder gleich $MAX_VALUE_YEAR sein!", + "year_max" to "Das Jahr muss kleiner oder gleich ${MAX_VALUE_YEAR} sein!", "data_final_warning" to "Wichtig: Die Daten können nach dem Abschluss nicht mehr verändert oder bearbeitet werden!", "multiple_times" to "mehrmals", "more_than_15_years" to "mehr als 15 Jahre", "no" to "Nein", "no_answer" to "keine Angabe", "other_country" to "anderes Land", - "value_must_be_less_equal_max" to "Der Wert muss kleiner oder gleich $MAX_VALUE_AGE sein!", + "value_must_be_less_equal_max" to "Der Wert muss kleiner oder gleich ${MAX_VALUE_AGE} sein!", "value_between_1_and_15" to "Der Wert muss zwischen 1 und 15 liegen!", "invalid_month" to "Ungültige Monatsangabe!", "invalid_year" to "Ungültige Jahresangabe!", @@ -438,14 +440,14 @@ object LanguageManager { "once" to "once", "year_after_2000" to "The year must be after 2000!", "year_after_departure" to "The year must be after leaving the country of origin!", - "year_max" to "The year must be less than or equal to $MAX_VALUE_YEAR!", + "year_max" to "The year must be less than or equal to ${MAX_VALUE_YEAR}!", "data_final_warning" to "Important: The data cannot be changed or edited after completion!", "multiple_times" to "multiple times", "more_than_15_years" to "more than 15 years", "no" to "No", "no_answer" to "No answer", "other_country" to "Other country", - "value_must_be_less_equal_max" to "The value must be less than or equal to $MAX_VALUE_AGE!", + "value_must_be_less_equal_max" to "The value must be less than or equal to ${MAX_VALUE_AGE}!", "value_between_1_and_15" to "The value must be between 1 and 15!", "invalid_month" to "Invalid month!", "invalid_year" to "Invalid year!", @@ -814,14 +816,14 @@ object LanguageManager { "once" to "une fois", "year_after_2000" to "L’année doit être après 2000 !", "year_after_departure" to "L’année doit être après le départ du pays d’origine !", - "year_max" to "L’année doit être inférieure ou égale à $MAX_VALUE_YEAR !", + "year_max" to "L’année doit être inférieure ou égale à ${MAX_VALUE_YEAR} !", "data_final_warning" to "Important : Les données ne peuvent plus être modifiées ou éditées après la validation !", "multiple_times" to "plusieurs fois", "more_than_15_years" to "plus de 15 ans", "no" to "Non", "no_answer" to "pas de réponse", "other_country" to "autre pays", - "value_must_be_less_equal_max" to "La valeur doit être inférieure ou égale à $MAX_VALUE_AGE !", + "value_must_be_less_equal_max" to "La valeur doit être inférieure ou égale à ${MAX_VALUE_AGE} !", "value_between_1_and_15" to "La valeur doit être comprise entre 1 et 15 !", "invalid_month" to "Mois invalide !", "invalid_year" to "Année invalide !", @@ -1194,14 +1196,14 @@ object LanguageManager { "once" to "один раз", "year_after_2000" to "Год должен быть после 2000!", "year_after_departure" to "Год должен быть после даты выезда из страны происхождения!", - "year_max" to "Год должен быть меньше или равен $MAX_VALUE_YEAR!", + "year_max" to "Год должен быть меньше или равен ${MAX_VALUE_YEAR}!", "data_final_warning" to "Внимание: Данные нельзя изменять после завершения!", "multiple_times" to "несколько раз", "more_than_15_years" to "более 15 лет", "no" to "Нет", "no_answer" to "без ответа", "other_country" to "другая страна", - "value_must_be_less_equal_max" to "Значение должно быть меньше или равно $MAX_VALUE_AGE!", + "value_must_be_less_equal_max" to "Значение должно быть меньше или равно ${MAX_VALUE_AGE}!", "value_between_1_and_15" to "Значение должно быть между 1 и 15!", "invalid_month" to "Недопустимый месяц!", "invalid_year" to "Недопустимый год!", @@ -1570,14 +1572,14 @@ object LanguageManager { "once" to "один раз", "year_after_2000" to "Рік має бути після 2000!", "year_after_departure" to "Рік має бути після виїзду з країни походження!", - "year_max" to "Рік має бути меншим або рівним $MAX_VALUE_YEAR!", + "year_max" to "Рік має бути меншим або рівним ${MAX_VALUE_YEAR}!", "data_final_warning" to "Важливо: Дані після завершення не можна змінити або редагувати!", "multiple_times" to "багато разів", "more_than_15_years" to "більше 15 років", "no" to "Ні", "no_answer" to "немає відповіді", "other_country" to "інша країна", - "value_must_be_less_equal_max" to "Значення має бути меншим або рівним $MAX_VALUE_AGE!", + "value_must_be_less_equal_max" to "Значення має бути меншим або рівним ${MAX_VALUE_AGE}!", "value_between_1_and_15" to "Значення має бути від 1 до 15!", "invalid_month" to "Неправильний місяць!", "invalid_year" to "Неправильний рік!", @@ -1950,14 +1952,14 @@ object LanguageManager { "once" to "bir kez", "year_after_2000" to "Yıl 2000’den sonra olmalıdır!", "year_after_departure" to "Yıl, menşe ülkeyi terk ettikten sonra olmalıdır!", - "year_max" to "Yıl $MAX_VALUE_YEAR’den küçük veya ona eşit olmalıdır!", + "year_max" to "Yıl ${MAX_VALUE_YEAR}’den küçük veya ona eşit olmalıdır!", "data_final_warning" to "Önemli: Veriler tamamlandıktan sonra değiştirilemez veya düzenlenemez!", "multiple_times" to "birden fazla kez", "more_than_15_years" to "15 yıldan fazla", "no" to "Hayır", "no_answer" to "Cevap yok", "other_country" to "diğer ülke", - "value_must_be_less_equal_max" to "Değer $MAX_VALUE_AGE’den küçük veya ona eşit olmalıdır!", + "value_must_be_less_equal_max" to "Değer ${MAX_VALUE_AGE}’den küçük veya ona eşit olmalıdır!", "value_between_1_and_15" to "Değer 1 ile 15 arasında olmalıdır!", "invalid_month" to "Geçersiz ay girişi!", "invalid_year" to "Geçersiz yıl girişi!", @@ -2330,14 +2332,14 @@ object LanguageManager { "once" to "jeden raz", "year_after_2000" to "Rok musi być po 2000!", "year_after_departure" to "Rok musi być po opuszczeniu kraju pochodzenia!", - "year_max" to "Rok musi być mniejszy lub równy $MAX_VALUE_YEAR!", + "year_max" to "Rok musi być mniejszy lub równy ${MAX_VALUE_YEAR}!", "data_final_warning" to "Ważne: Po zakończeniu dane nie mogą być zmienione ani edytowane!", "multiple_times" to "kilka razy", "more_than_15_years" to "więcej niż 15 lat", "no" to "Nie", "no_answer" to "brak odpowiedzi", "other_country" to "inny kraj", - "value_must_be_less_equal_max" to "Wartość musi być mniejsza lub równa $MAX_VALUE_AGE!", + "value_must_be_less_equal_max" to "Wartość musi być mniejsza lub równa ${MAX_VALUE_AGE}!", "value_between_1_and_15" to "Wartość musi być między 1 a 15!", "invalid_month" to "Nieprawidłowy miesiąc!", "invalid_year" to "Nieprawidłowy rok!", @@ -2710,14 +2712,14 @@ object LanguageManager { "once" to "مرة واحدة", "year_after_2000" to "يجب أن تكون السنة بعد 2000!", "year_after_departure" to "يجب أن تكون السنة بعد مغادرة بلد المنشأ!", - "year_max" to "يجب أن تكون السنة أقل من أو تساوي $MAX_VALUE_YEAR!", + "year_max" to "يجب أن تكون السنة أقل من أو تساوي ${MAX_VALUE_YEAR}!", "data_final_warning" to "هام: لا يمكن تعديل البيانات بعد الانتهاء!", "multiple_times" to "عدة مرات", "more_than_15_years" to "أكثر من 15 سنة", "no" to "لا", "no_answer" to "لا يوجد إجابة", "other_country" to "بلد آخر", - "value_must_be_less_equal_max" to "يجب أن تكون القيمة أقل من أو تساوي $MAX_VALUE_AGE!", + "value_must_be_less_equal_max" to "يجب أن تكون القيمة أقل من أو تساوي ${MAX_VALUE_AGE}!", "value_between_1_and_15" to "يجب أن تكون القيمة بين 1 و15!", "invalid_month" to "شهر غير صالح!", "invalid_year" to "سنة غير صالحة!", @@ -3090,14 +3092,14 @@ object LanguageManager { "once" to "o dată", "year_after_2000" to "Anul trebuie să fie după 2000!", "year_after_departure" to "Anul trebuie să fie după plecarea din țara de origine!", - "year_max" to "Anul trebuie să fie mai mic sau egal cu $MAX_VALUE_YEAR!", + "year_max" to "Anul trebuie să fie mai mic sau egal cu ${MAX_VALUE_YEAR}!", "data_final_warning" to "Important: Datele nu mai pot fi modificate după finalizare!", "multiple_times" to "de mai multe ori", "more_than_15_years" to "mai mult de 15 ani", "no" to "Nu", "no_answer" to "fără răspuns", "other_country" to "altă țară", - "value_must_be_less_equal_max" to "Valoarea trebuie să fie mai mică sau egală cu $MAX_VALUE_AGE!", + "value_must_be_less_equal_max" to "Valoarea trebuie să fie mai mică sau egală cu ${MAX_VALUE_AGE}!", "value_between_1_and_15" to "Valoarea trebuie să fie între 1 și 15!", "invalid_month" to "Lună invalidă!", "invalid_year" to "An invalid!", @@ -3470,14 +3472,14 @@ object LanguageManager { "once" to "una vez", "year_after_2000" to "¡El año debe ser posterior al 2000!", "year_after_departure" to "¡El año debe ser posterior a la salida de su país de origen!", - "year_max" to "¡El año debe ser menor o igual que $MAX_VALUE_YEAR!", + "year_max" to "¡El año debe ser menor o igual que ${MAX_VALUE_YEAR}!", "data_final_warning" to "Importante: Después de finalizar, los datos no podrán cambiarse o editarse.", "multiple_times" to "varias veces", "more_than_15_years" to "más de 15 años", "no" to "No", "no_answer" to "sin respuesta", "other_country" to "otro país", - "value_must_be_less_equal_max" to "¡El valor debe ser menor o igual que $MAX_VALUE_AGE!", + "value_must_be_less_equal_max" to "¡El valor debe ser menor o igual que ${MAX_VALUE_AGE}!", "value_between_1_and_15" to "¡El valor debe estar entre 1 y 15!", "invalid_month" to "¡Mes no válido!", "invalid_year" to "¡Año no válido!", diff --git a/app/src/main/java/com/dano/test1/LocalizationHelper.kt b/app/src/main/java/com/dano/test1/utils/LocalizationHelper.kt similarity index 100% rename from app/src/main/java/com/dano/test1/LocalizationHelper.kt rename to app/src/main/java/com/dano/test1/utils/LocalizationHelper.kt diff --git a/app/src/main/java/com/dano/test1/utils/ViewUtils.kt b/app/src/main/java/com/dano/test1/utils/ViewUtils.kt new file mode 100644 index 0000000..c551921 --- /dev/null +++ b/app/src/main/java/com/dano/test1/utils/ViewUtils.kt @@ -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 setupResponsiveSpinner(context: Context, spinner: Spinner, items: List, 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(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() +}