Compare commits

2 Commits

Author SHA1 Message Date
d48906bd3b added language selection to surveys 2026-03-24 12:34:33 +01:00
b6fea5be7a fixed responsive bugs 2026-03-24 11:47:41 +01:00
8 changed files with 157 additions and 19 deletions

View File

@ -4,10 +4,10 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-09-29T10:52:30.282144200Z"> <DropdownSelection timestamp="2026-03-24T11:30:25.894049082Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\danie\.android\avd\Medium_Phone.avd" /> <DeviceId pluginId="PhysicalDevice" identifier="serial=R52T605XE0L" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>

View File

@ -11,8 +11,11 @@ import android.widget.EditText
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.ProgressBar import android.widget.ProgressBar
import android.widget.Toast import android.widget.Toast
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.dano.test1.network.DatabaseDownloader import com.dano.test1.network.DatabaseDownloader
import com.dano.test1.network.LoginManager import com.dano.test1.network.LoginManager
import com.dano.test1.network.TokenStore import com.dano.test1.network.TokenStore
@ -36,6 +39,7 @@ class MainActivity : AppCompatActivity() {
private fun t(key: String): String = LanguageManager.getText(bootLanguageId, key) private fun t(key: String): String = LanguageManager.getText(bootLanguageId, key)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// === Offline-Start ermöglichen === // === Offline-Start ermöglichen ===
@ -205,6 +209,16 @@ class MainActivity : AppCompatActivity() {
} }
// --- /LIVE NETZSTATUS --- // --- /LIVE NETZSTATUS ---
override fun onContentChanged() {
super.onContentChanged()
val content = findViewById<View>(android.R.id.content) ?: return
ViewCompat.setOnApplyWindowInsetsListener(content) { v, insets ->
val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(bars.left, bars.top, bars.right, bars.bottom)
WindowInsetsCompat.CONSUMED
}
}
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
} }

View File

@ -47,6 +47,9 @@ class QuestionnaireAllInOne(private val questionnaireFileName: String) : Questio
} }
private fun buildAllInOneUi() { private fun buildAllInOneUi() {
sections.clear()
setupComplete = false
context.setContentView(R.layout.questionnaire_all_in_one) context.setContentView(R.layout.questionnaire_all_in_one)
container = context.findViewById(R.id.questionContainer) container = context.findViewById(R.id.questionContainer)
@ -64,6 +67,8 @@ class QuestionnaireAllInOne(private val questionnaireFileName: String) : Questio
(context as? MainActivity)?.finishQuestionnaire() (context as? MainActivity)?.finishQuestionnaire()
} }
setupLanguageSpinner(context.findViewById(R.id.langSpinner))
val inflater = LayoutInflater.from(context) val inflater = LayoutInflater.from(context)
for ((idx, question) in questions.withIndex()) { for ((idx, question) in questions.withIndex()) {
@ -101,6 +106,33 @@ class QuestionnaireAllInOne(private val questionnaireFileName: String) : Questio
} }
} }
private fun setupLanguageSpinner(spinner: Spinner) {
val adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, LANGUAGE_LABELS).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
spinner.adapter = adapter
spinner.setSelection(LANGUAGE_IDS.indexOf(languageID).coerceAtLeast(0))
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, v: View?, position: Int, id: Long) {
val newLang = LANGUAGE_IDS[position]
if (newLang == languageID) return
spinner.post { onLanguageChanged(newLang) }
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
}
}
private fun onLanguageChanged(newLang: String) {
for (section in sections) {
if (section.card.visibility == View.VISIBLE) {
try { section.handler?.saveAnswer() } catch (_: Exception) {}
}
}
languageID = newLang
buildAllInOneUi()
}
private fun createEmbeddedHandler(question: QuestionItem): QuestionHandler? { private fun createEmbeddedHandler(question: QuestionItem): QuestionHandler? {
val noop = {} val noop = {}
val noopId: (String) -> Unit = {} val noopId: (String) -> Unit = {}

View File

@ -40,6 +40,17 @@ abstract class QuestionnaireBase<T> {
abstract fun startQuestionnaire() abstract fun startQuestionnaire()
abstract fun showCurrentQuestion() abstract fun showCurrentQuestion()
companion object {
val LANGUAGE_IDS = listOf(
"GERMAN", "ENGLISH", "FRENCH", "ROMANIAN", "ARABIC",
"POLISH", "TURKISH", "UKRAINIAN", "RUSSIAN", "SPANISH"
)
val LANGUAGE_LABELS = listOf(
"DE", "EN", "FR", "RO", "AR",
"PL", "TR", "UA", "RU", "ES"
)
}
fun attach(activity: Activity, language: String) { fun attach(activity: Activity, language: String) {
this.context = activity this.context = activity
this.languageID = language this.languageID = language

View File

@ -1,8 +1,16 @@
package com.dano.test1.questionnaire package com.dano.test1.questionnaire
import android.view.Gravity
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.Button import android.widget.Button
import android.widget.FrameLayout
import android.widget.Spinner
import androidx.core.content.ContextCompat
import com.dano.test1.LocalizationHelper import com.dano.test1.LocalizationHelper
import com.dano.test1.R import com.dano.test1.R
import com.dano.test1.utils.ViewUtils
open class QuestionnaireGeneric(private val questionnaireFileName: String) : QuestionnaireBase<Unit>() { open class QuestionnaireGeneric(private val questionnaireFileName: String) : QuestionnaireBase<Unit>() {
@ -39,6 +47,40 @@ open class QuestionnaireGeneric(private val questionnaireFileName: String) : Que
} else { } else {
showEmptyScreen() showEmptyScreen()
} }
injectLanguageSpinner(layout)
}
}
private fun injectLanguageSpinner(layout: View) {
val container = layout as? FrameLayout ?: return
val spinner = Spinner(context)
val adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, LANGUAGE_LABELS).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
spinner.adapter = adapter
spinner.background = ContextCompat.getDrawable(context, R.drawable.bg_field_filled)
spinner.setSelection(LANGUAGE_IDS.indexOf(languageID).coerceAtLeast(0))
val margin = ViewUtils.dp(context, 10)
container.addView(
spinner,
FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT,
Gravity.TOP or Gravity.END
).apply { setMargins(0, margin, margin, 0) }
)
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, v: View?, position: Int, id: Long) {
val newLang = LANGUAGE_IDS[position]
if (newLang == languageID) return
languageID = newLang
spinner.post { showCurrentQuestion() }
}
override fun onNothingSelected(parent: AdapterView<*>?) {}
} }
} }
} }

View File

@ -1,12 +1,16 @@
package com.dano.test1.ui package com.dano.test1.ui
import android.os.Bundle import android.os.Bundle
import android.view.View
import android.widget.Button import android.widget.Button
import android.widget.RadioButton import android.widget.RadioButton
import android.widget.RadioGroup import android.widget.RadioGroup
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SwitchCompat import androidx.appcompat.widget.SwitchCompat
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.dano.test1.R import com.dano.test1.R
/** /**
@ -19,9 +23,18 @@ class DevSettingsActivity : AppCompatActivity() {
private lateinit var radioGroup: RadioGroup private lateinit var radioGroup: RadioGroup
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dev_settings) setContentView(R.layout.activity_dev_settings)
findViewById<View>(android.R.id.content)?.let { content ->
ViewCompat.setOnApplyWindowInsetsListener(content) { v, insets ->
val bars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(bars.left, bars.top, bars.right, bars.bottom)
WindowInsetsCompat.CONSUMED
}
}
val toolbar = findViewById<Toolbar>(R.id.devSettingsToolbar) val toolbar = findViewById<Toolbar>(R.id.devSettingsToolbar)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)

View File

@ -22,7 +22,8 @@ object ViewUtils {
*/ */
fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) { fun setTextSizePercentOfScreenHeight(view: TextView, percentOfHeight: Float) {
val dm = view.context.resources.displayMetrics val dm = view.context.resources.displayMetrics
val sp = (dm.heightPixels * percentOfHeight) / dm.scaledDensity val shortSide = minOf(dm.heightPixels, dm.widthPixels)
val sp = (shortSide * percentOfHeight) / dm.scaledDensity
TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE) TextViewCompat.setAutoSizeTextTypeWithDefaults(view, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE)
view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp) view.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp)
} }
@ -33,8 +34,9 @@ object ViewUtils {
*/ */
fun <T> setupResponsiveSpinner(context: Context, spinner: Spinner, items: List<T>, selectedItem: T?) { fun <T> setupResponsiveSpinner(context: Context, spinner: Spinner, items: List<T>, selectedItem: T?) {
val dm = context.resources.displayMetrics val dm = context.resources.displayMetrics
val shortSide = minOf(dm.heightPixels, dm.widthPixels)
fun spFromScreenHeight(percent: Float): Float = (dm.heightPixels * percent) / dm.scaledDensity fun spFromScreenHeight(percent: Float): Float = (shortSide * percent) / dm.scaledDensity
fun pxFromSp(sp: Float): Int = (sp * dm.scaledDensity).toInt() fun pxFromSp(sp: Float): Int = (sp * dm.scaledDensity).toInt()
val textSp = spFromScreenHeight(0.0275f) val textSp = spFromScreenHeight(0.0275f)

View File

@ -5,21 +5,45 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.button.MaterialButton <LinearLayout
android:id="@+id/btnBack" android:layout_width="match_parent"
android:layout_width="@dimen/nav_btn_size" android:layout_height="wrap_content"
android:layout_height="@dimen/nav_btn_size" android:orientation="horizontal"
android:layout_marginStart="12dp" android:gravity="center_vertical"
android:layout_marginTop="12dp" android:paddingStart="12dp"
android:text="" android:paddingEnd="12dp"
android:textAllCaps="false" android:paddingTop="12dp">
app:icon="@drawable/ic_chevron_left"
app:iconTint="@color/btn_nav_left_icon_tint" <com.google.android.material.button.MaterialButton
app:iconSize="@dimen/nav_icon_size" android:id="@+id/btnBack"
app:iconPadding="0dp" android:layout_width="@dimen/nav_btn_size"
app:cornerRadius="999dp" android:layout_height="@dimen/nav_btn_size"
app:backgroundTint="@color/btn_nav_left_tint" android:text=""
app:rippleColor="@color/btn_nav_left_ripple" /> android:textAllCaps="false"
app:icon="@drawable/ic_chevron_left"
app:iconTint="@color/btn_nav_left_icon_tint"
app:iconSize="@dimen/nav_icon_size"
app:iconPadding="0dp"
app:cornerRadius="999dp"
app:backgroundTint="@color/btn_nav_left_tint"
app:rippleColor="@color/btn_nav_left_ripple" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<Spinner
android:id="@+id/langSpinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_field_filled"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingTop="6dp"
android:paddingBottom="6dp" />
</LinearLayout>
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/scrollContainer" android:id="@+id/scrollContainer"