Initialer Upload neues Unity-Projekt

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

31
.gitattributes.txt Normal file
View File

@ -0,0 +1,31 @@
# Bildformate
*.png filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.webp filter=lfs diff=lfs merge=lfs -text
# Audioformate
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
# Videoformate
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.mov filter=lfs diff=lfs merge=lfs -text
*.webm filter=lfs diff=lfs merge=lfs -text
# Fonts
*.ttf filter=lfs diff=lfs merge=lfs -text
*.otf filter=lfs diff=lfs merge=lfs -text
# Große Binaries / Assets / Libraries
*.so filter=lfs diff=lfs merge=lfs -text
*.aar filter=lfs diff=lfs merge=lfs -text
*.jar filter=lfs diff=lfs merge=lfs -text
*.dll filter=lfs diff=lfs merge=lfs -text
# Archive / export
*.zip filter=lfs diff=lfs merge=lfs -text
*.tar filter=lfs diff=lfs merge=lfs -text
*.7z filter=lfs diff=lfs merge=lfs -text

15
.gitignore vendored Normal file
View File

@ -0,0 +1,15 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

65
.gitignore.txt Normal file
View File

@ -0,0 +1,65 @@
# Gradle
.gradle/
build/
*/build/
!gradle/wrapper/gradle-wrapper.jar
# IDE
.idea/
*.iml
*.ipr
*.iws
# VS Code
.vscode/
# OS
.DS_Store
Thumbs.db
# Signing / secrets
*.jks
*.keystore
local.properties
*.pem
*.key
# Android specific
captures/
.externalNativeBuild/
.cxx/
# Misc
*.log
*.tmp
*.bak
# Ignore generated files
generated/
gen/
# APK / AAB
*.apk
*.ap_
*.aab
# Testing / coverage
test-results/
coverage/
*.ec
# Lint and analysis
lint/reports/
lint/intermediates/
# Crashlytics / Firebase
crashlytics-build.properties
fabric.properties
# Gradle cache
.gradle/
build/
# Ignore Google services config (if private)
google-services.json

3
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

1
.idea/.name generated Normal file
View File

@ -0,0 +1 @@
Test1

6
.idea/AndroidProjectSystem.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>

6
.idea/compiler.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>

18
.idea/deploymentTargetSelector.xml generated Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-07-24T13:14:09.057647300Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=HA218GZY" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>
</project>

19
.idea/gradle.xml generated Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

6
.idea/kotlinc.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="2.0.21" />
</component>
</project>

10
.idea/migrations.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>

9
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,9 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

17
.idea/runConfigurations.xml generated Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

56
app/build.gradle.kts Normal file
View File

@ -0,0 +1,56 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
id("kotlin-kapt")
}
android {
namespace = "com.dano.test1"
compileSdk = 35
defaultConfig {
applicationId = "com.dano.test1"
minSdk = 29
targetSdk = 35
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
}
val room_version = "2.6.1"
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
implementation("com.google.code.gson:gson:2.10.1")
implementation("androidx.room:room-runtime:$room_version")
kapt("androidx.room:room-compiler:$room_version")
implementation("androidx.room:room-ktx:$room_version")
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
implementation ("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
}

21
app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -0,0 +1,24 @@
package com.dano.test1
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.dano.test1", appContext.packageName)
}
}

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".MyApp"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Test1"
tools:targetApi="31">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,39 @@
Holen Sie die Einwilligung des Klienten / der Klientin ein: {X1}
Code Klient: {X2}
Code Coach: {X3}
Unterkunft: {X4}
Alter: {X5}
Geschlecht: {X6}
Herkunftsland: {X7}
Herkunftsland verlassen: {X8}
In Deutschland seit: {X9}
Wie leben Sie hier?: {X10}
Anzahl Familienmitglieder: {X11}
Welche Sprachen sprechen Sie?: {X12}
Wie stufen Sie Ihre Deutschkenntnisse ein?: {X13}
Wie viele Jahre haben Sie die Schule besucht?: {X14}
Wie viele Jahre haben Sie die Schule besucht? (im Heimatland): {X15}
Wie viele Jahre haben Sie die Schule besucht? (auf Zwischenstation): {X16}
Wie viele Jahre haben Sie die Schule besucht? (in Deutschland): {X17}
Haben Sie eine Berufsausbildung?: {X18}
Seit wann sind Sie in der vorläufigen Unterbringung?: {X19}
Seit wann läuft Ihr Asylverfahren?: {X20}

View File

@ -0,0 +1,48 @@
Code Klient: {X1}
Code Coach: {X2}
1. Frage: {X3}
2. Frage: {X4}
3. Frage: {X5}
4. Frage: {X6}
5. Frage: {X7}
6. Frage: {X8}
7. Frage: {X9}
8. Frage: {X10}
9. Frage: {X11}
10. Frage: {X12}
11. Frage: {X13}
12. Frage: {X14}
13. Frage: {X15}
14. Frage: {X16}
Wählen Sie die Nummer (0-10), die am besten beschreibt, wie viel Leid der
Befragte in der vergangenen Woche einschließlich heute fühlen muss.: {X17}
Sind Sie jemals so schwer von anderen verletzt worden, dass Sie ärztliche
Unterstützung benötigt haben? (Arzt, Krankenhaus): {X18}
Ist das ... passiert: {X19}
In welchem Alter: mit ... Jahren?: {X20}
Seitdem Sie in Deutschland sind, sind Sie jemals in gewalttätige Konflikte
(Prügelei, körperlich, und psychische Konflikte) geraten?: {X21}
Ist das ... passiert: {X22}
In welchem Alter: mit ... Jahren?: {X23}

View File

@ -0,0 +1,136 @@
{
"meta": {
"_comment": "Diese Zeiel erklärt im folgenden wie die einzelnen Fragetypen definiert werden können und was für features diese mit sich bringen.",
"id": "questionnaire_0_readme"
},
"questions": [
{
"id": "q1",
"_comment": "Frage mit Radio Buttons. In diesem Fragetypen kann man maximal eine Antwort wählen. Über nextQuestionId kann man die nächste Frage definieren., Über pointsMap kann man die Punkte für die Antwort der Frage festlegen.",
"layout": "radio_question",
"question": "consent_instruction",
"options": [
{ "key": "consent_signed"},
{ "key": "consent_not_signed",
"nextQuestionId": "last_page"}
],
"pointsMap": {
"consent_signed": 0,
"consent_not_signed": 0
}
},
{
"id": "q2",
"_comment": "Frage nach dem Code des Coaches.",
"layout": "client_not_signed",
"textKey1": "no_consent_entered",
"textKey2": "no_consent_note",
"question": "coach_code_request",
"hint": "coach_code"
},
{
"id": "q3",
"_comment": "Frage mit Spinner, dessen Antwortmöglichkeiten über options selbst definiert werden können.",
"layout": "string_spinner",
"question": "other_accommodation",
"options": ["Unterkunft 1", "Unterkunft 2", "Unterkunft 3"]
},
{
"id": "q4",
"_comment": "Frage mit Spinner zur Auswahl des Datums",
"layout": "date_spinner",
"question": "departure_country"
},
{
"id": "q5",
"_comment": "Frage mit Spinner zur Auswahl des Datums. Über constraints und notBefore und notAfter, ist es möglich das Datum nicht in der Zukunft oder Vergangenheit zu wählen.",
"layout": "date_spinner",
"question": "since_in_germany",
"constraints": {
"notBefore": "departure_country",
"notAfter": "departure_country"
}
},
{
"id": "q6",
"_comment": "Frage mit Spinner, dessen Antwortmöglichkeiten über range selbst definiert werden können.",
"layout": "value_spinner",
"question": "number_family_members",
"range": {
"min": 1,
"max": 15
}
},
{
"id": "q7",
"_comment": "Frage nach dem Code des Klienten und den Code des Coaches.",
"layout": "client_coach_code_question",
"question": "client_code_entry_question",
"hint1": "client_code",
"hint2": "coach_code"
},
{
"id": "q8",
"_comment": "Frage mit Checkboxen, dessen Antwortmöglichkeiten über options selbst definiert werden können. Über pointsMap können die Punkte für die jeweiligen Antworten festgelegt werden. Über minSelection kann die minimal Anzahl an auszufüllenden Antworten festgelegt werden.",
"layout": "multi_check_box_question",
"question": "languages_spoken",
"options": [
{ "key": "language_albanian" },
{ "key": "language_pashto" },
{ "key": "language_russian" },
{ "key": "language_other"}
],
"pointsMap": {
"language_albanian": 1,
"language_pashto": 1,
"language_russian": 1,
"language_other": 1
},
"minSelection": 1
},
{
"id": "q9",
"_comment":"Glass scale frage, bei der es für jede Frage, welche über symptoms festgelegt werden kann eine Antwort festgelegt werden muss. Die Antwort ganz links gibt immer 0 Punkte und die Antwort ganz rechts gibt immer 4 Punkte.",
"layout": "glass_scale_question",
"question": "glass_explanation",
"symptoms": [
"q1_symptom",
"q2_symptom",
"q3_symptom",
"q4_symptom",
"q5_symptom",
"q6_symptom",
"q7_symptom",
"q8_symptom",
"q9_symptom"
]
},
{
"id": "last_page",
"_comment":"Letzte Seite, welche den Abschluss des Fragebogens markiert.",
"layout": "last_page",
"textKey": "finish_data_entry",
"question": "data_final_warning"
}
]
}

View File

@ -0,0 +1,297 @@
{
"meta": {
"id": "questionnaire_1_demographic_information"
},
"questions": [
{
"id": "q1",
"layout": "radio_question",
"question": "consent_instruction",
"options": [
{ "key": "consent_signed",
"nextQuestionId": "q6"},
{ "key": "consent_not_signed",
"nextQuestionId": "q2"}
],
"pointsMap": {
"consent_signed": 0,
"consent_not_signed": 0
}
},
{
"id": "q2",
"layout": "client_not_signed",
"textKey1": "no_consent_entered",
"textKey2": "no_consent_note",
"question": "coach_code_request",
"hint": "coach_code"
},
{
"id": "q28",
"layout": "radio_question",
"question": "accommodation",
"options": [
{ "key": "steinstrasse",
"nextQuestionId": "last_page"},
{ "key": "doerfle",
"nextQuestionId": "last_page"},
{ "key": "zoll_emmishofer",
"nextQuestionId": "last_page"},
{ "key": "other",
"nextQuestionId": "q4"}
]
},
{
"id": "q4",
"layout": "string_spinner",
"question": "other_accommodation",
"options": ["Unterkunft 1", "Unterkunft 2", "Unterkunft 3"]
},
{
"id": "last_page",
"layout": "last_page",
"textKey": "finish_data_entry",
"question": "data_final_warning"
},
{
"id": "q6",
"layout": "client_coach_code_question",
"question": "client_code_entry_question",
"hint1": "client_code",
"hint2": "coach_code"
},
{
"id": "q29",
"layout": "radio_question",
"question": "accommodation",
"options": [
{ "key": "steinstrasse",
"nextQuestionId": "q9"},
{ "key": "doerfle",
"nextQuestionId": "q9"},
{ "key": "zoll_emmishofer",
"nextQuestionId": "q9"},
{ "key": "other",
"nextQuestionId": "q8"}
],
"pointsMap": {
"consent_signed": 0,
"consent_not_signed": 0
}
},
{
"id": "q8",
"layout": "string_spinner",
"question": "other_accommodation",
"options": ["Unterkunft 1", "Unterkunft 2", "Unterkunft 3"]
},
{
"id": "q9",
"layout": "value_spinner",
"question": "age",
"range": {
"min": 18,
"max": 122
}
},
{
"id": "q10",
"layout": "radio_question",
"question": "gender",
"options": [
{ "key": "gender_male"},
{ "key": "gender_female"},
{ "key": "gender_diverse"}
],
"pointsMap": {
"consent_signed": 0,
"consent_not_signed": 0
}
},
{
"id": "q11",
"layout": "string_spinner",
"question": "country_of_origin"
},
{
"id": "q13",
"layout": "date_spinner",
"question": "departure_country"
},
{
"id": "q14",
"layout": "date_spinner",
"question": "since_in_germany",
"constraints": {
"notBefore": "departure_country"
}
},
{
"id": "q16",
"layout": "radio_question",
"question": "living_situation",
"options": [
{ "key": "alone",
"nextQuestionId": "q18"},
{ "key": "with_family",
"nextQuestionId": "q17"}
],
"pointsMap": {
"consent_signed": 0,
"consent_not_signed": 0
}
},
{
"id": "q17",
"layout": "value_spinner",
"question": "number_family_members",
"range": {
"min": 1,
"max": 15
}
},
{
"id": "q18",
"layout": "multi_check_box_question",
"question": "languages_spoken",
"options": [
{ "key": "language_albanian" },
{ "key": "language_pashto" },
{ "key": "language_russian" },
{ "key": "language_serbo" },
{ "key": "language_somali" },
{ "key": "language_tamil" },
{ "key": "language_tigrinya" },
{ "key": "language_turkish" },
{ "key": "language_ukrainian" },
{ "key": "language_urdu" },
{ "key": "language_arabic" },
{ "key": "language_dari_farsi" },
{ "key": "language_chinese" },
{ "key": "language_english" },
{ "key": "language_macedonian" },
{ "key": "language_kurmanji" },
{ "key": "language_hindi" },
{ "key": "language_french"},
{ "key": "language_other"}
],
"pointsMap": {
"language_albanian": 0,
"language_pashto": 0,
"language_russian": 0,
"language_serbo": 0,
"language_somali": 0,
"language_tamil": 0,
"language_tigrinya": 0,
"language_turkish": 0,
"language_ukrainian": 0,
"language_urdu": 0,
"language_arabic": 0,
"language_dari_farsi": 0,
"language_chinese": 0,
"language_english": 0,
"language_macedonian": 0,
"language_kurmanji": 0,
"language_hindi": 0,
"language_french": 0,
"language_other": 0
},
"minSelection": 1
},
{
"id": "q19",
"layout": "radio_question",
"question": "german_skills",
"options": [
{ "key": "skill_none"},
{ "key": "skill_basic"},
{ "key": "skill_a1"},
{ "key": "skill_a2"},
{ "key": "skill_b1"},
{ "key": "skill_b2"},
{ "key": "skill_c1"},
{ "key": "skill_c2"}
],
"pointsMap": {
"consent_signed": 0,
"consent_not_signed": 0
}
},
{
"id": "q30",
"layout": "value_spinner",
"question": "school_years_total",
"range": {
"min": 0,
"max": 15
},
"options": [
{ "value": 0, "nextQuestionId": "q23" }
]
},
{
"id": "q20",
"layout": "value_spinner",
"question": "school_years_origin",
"range": {
"min": 0,
"max": 15
}
},
{
"id": "q21",
"layout": "value_spinner",
"question": "school_years_transit",
"range": {
"min": 0,
"max": 15
}
},
{
"id": "q22",
"layout": "value_spinner",
"question": "school_years_germany",
"range": {
"min": 0,
"max": 15
}
},
{
"id": "q23",
"layout": "radio_question",
"question": "vocational_training",
"options": [
{ "key": "answer_no"},
{ "key": "answer_started"},
{ "key": "answer_completed"}
],
"pointsMap": {
"consent_signed": 0,
"consent_not_signed": 0
}
},
{
"id": "q24",
"layout": "date_spinner",
"question": "provisional_accommodation_since",
"constraints": {
"notBefore": "since_in_germany"
}
},
{
"id": "q25",
"layout": "date_spinner",
"question": "asylum_procedure_since",
"constraints": {
"notBefore": "since_in_germany"
}
},
{
"id": "last_page",
"layout": "last_page",
"textKey": "finish_data_entry",
"question": "data_final_warning"
}
]
}

View File

@ -0,0 +1,137 @@
{
"meta": {
"id": "questionnaire_2_rhs"
},
"questions": [
{
"id": "q0",
"layout": "client_coach_code_question",
"question": "client_code_entry_question",
"hint1": "client_code",
"hint2": "coach_code"
},
{
"id": "q3",
"layout": "glass_scale_question",
"question": "glass_explanation",
"symptoms": [
"q1_symptom",
"q2_symptom",
"q3_symptom",
"q4_symptom",
"q5_symptom",
"q6_symptom",
"q7_symptom",
"q8_symptom",
"q9_symptom"
]
},
{
"id": "q4",
"layout": "glass_scale_question",
"question": "how_strong_past_month",
"symptoms": [
"q10_reexperience_trauma",
"q11_physical_reaction",
"q12_emotional_numbness",
"q13_easily_startled"
]
},
{
"id": "q5",
"layout": "radio_question",
"textKey": "resilience_reflection_prompt",
"question": "q14_intro",
"options": [
{ "key": "resilience_fully_capable" },
{ "key": "resilience_mostly_capable" },
{ "key": "resilience_some_capable_some_not" },
{ "key": "resilience_mostly_not_capable" },
{ "key": "resilience_not_capable" }
],
"pointsMap": {
"resilience_fully_capable": 0,
"resilience_mostly_capable": 1,
"resilience_some_capable_some_not": 2,
"resilience_mostly_not_capable": 3,
"resilience_not_capable": 4
}
},
{
"id": "q6",
"layout": "value_spinner",
"question": "pain_rating_instruction",
"range": {
"min": 0,
"max": 10
}
},
{
"id": "q7",
"layout": "radio_question",
"question": "violence_question_1",
"options": [
{ "key": "no",
"nextQuestionId": "q10"},
{ "key": "yes" }
]
},
{
"id": "q8",
"layout": "radio_question",
"question": "times_happend",
"options": [
{ "key": "once" },
{ "key": "multiple_times" }
]
},
{
"id": "q9",
"layout": "value_spinner",
"question": "age_at_incident",
"range": {
"min": 1,
"max": 122
}
},
{
"id": "q10",
"layout": "radio_question",
"question": "conflict_since_arrival",
"options": [
{ "key": "no",
"nextQuestionId": "last_page"},
{ "key": "yes" }
]
},
{
"id": "q11",
"layout": "radio_question",
"question": "times_happend",
"options": [
{ "key": "once" },
{ "key": "multiple_times" }
]
},
{
"id": "q12",
"layout": "value_spinner",
"question": "age_at_incident",
"range": {
"min": 1,
"max": 122
}
},
{
"id": "q25",
"layout": "date_spinner",
"question": "asylum_procedure_since"
},
{
"id": "last_page",
"layout": "last_page",
"textKey": "finish_data_entry",
"question": "data_final_warning"
}
]
}

View File

@ -0,0 +1,255 @@
{
"meta": {
"id": "questionnaire_3_integration_index"
},
"questions": [
{
"id": "q0",
"layout": "client_coach_code_question",
"question": "client_code_entry_question",
"hint1": "client_code",
"hint2": "coach_code"
},
{
"id": "q1",
"layout": "radio_question",
"textKey": "intro_life_in_germany",
"question": "feeling_connected_to_germany_question",
"options": [
{ "key": "very_connected" },
{ "key": "quite_connected" },
{ "key": "moderately_connected" },
{ "key": "somewhat_loose" },
{ "key": "not_connected_at_all" }
],
"pointsMap": {
"very_connected": 3,
"quite_connected": 4,
"moderately_connected": 5,
"somewhat_loose": 1,
"not_connected_at_all": 2
}
},
{
"id": "q2",
"layout": "radio_question",
"question": "understanding_political_issues",
"options": [
{ "key": "very_good" },
{ "key": "good" },
{ "key": "fairly_good" },
{ "key": "somewhat" },
{ "key": "not_at_all" }
],
"pointsMap": {
"very_good": 1,
"good": 2,
"fairly_good": 3,
"somewhat": 4,
"not_at_all": 5
}
},
{
"id": "q3",
"layout": "radio_question",
"question": "unexpected_expense_question",
"options": [
{ "key": "expense_50" },
{ "key": "expense_100" },
{ "key": "expense_300" },
{ "key": "expense_500" },
{ "key": "expense_800" }
],
"pointsMap": {
"expense_50": 1,
"expense_100": 2,
"expense_300": 3,
"expense_500": 4,
"expense_800": 5
}
},
{
"id": "q4",
"layout": "radio_question",
"question": "dining_with_germans_question",
"options": [
{ "key": "never" },
{ "key": "once_a_year" },
{ "key": "once_a_month" },
{ "key": "once_a_week" },
{ "key": "almost_every_day" }
],
"pointsMap": {
"never": 1,
"once_a_year": 2,
"once_a_month": 3,
"once_a_week": 4,
"almost_every_day": 5
}
},
{
"id": "q5",
"layout": "radio_question",
"question": "reading_german_articles_question",
"options": [
{ "key": "very_good" },
{ "key": "good" },
{ "key": "moderately_good" },
{ "key": "not_good" },
{ "key": "not_at_all" }
],
"pointsMap": {
"very_good": 1,
"good": 2,
"moderately_good": 3,
"not_good": 4,
"not_at_all": 5
}
},
{
"id": "q6",
"layout": "radio_question",
"question": "visiting_doctor_question",
"options": [
{ "key": "very_difficult" },
{ "key": "rather_difficult" },
{ "key": "neither_nor" },
{ "key": "rather_easy" },
{ "key": "very_easy" }
],
"pointsMap": {
"very_difficult": 1,
"rather_difficult": 2,
"neither_nor": 3,
"rather_easy": 4,
"very_easy": 5
}
},
{
"id": "q7",
"layout": "radio_question",
"question": "feeling_as_outsider_question",
"options": [
{ "key": "never" },
{ "key": "rarely" },
{ "key": "sometimes" },
{ "key": "often" },
{ "key": "always" }
],
"pointsMap": {
"never": 1,
"rarely": 2,
"sometimes": 3,
"often": 4,
"always": 5
}
},
{
"id": "q8",
"layout": "radio_question",
"question": "recent_occupation_question",
"options": [
{ "key": "employed" },
{ "key": "education" },
{ "key": "internship" },
{ "key": "unemployed_searching" },
{ "key": "unemployed_not_searching" },
{ "key": "sick_or_disabled" },
{ "key": "unpaid_housework" },
{ "key": "other_activity" }
],
"pointsMap": {
"employed": 1,
"education": 2,
"internship": 3,
"unemployed_searching": 4,
"unemployed_not_searching": 8,
"sick_or_disabled": 5,
"unpaid_housework": 6,
"other_activity": 7
}
},
{
"id": "q9",
"layout": "radio_question",
"question": "discussing_politics_question",
"options": [
{ "key": "never" },
{ "key": "once_a_year" },
{ "key": "once_a_month" },
{ "key": "once_a_week" },
{ "key": "almost_every_day" }
],
"pointsMap": {
"never": 1,
"once_a_year": 2,
"once_a_month": 3,
"once_a_week": 4,
"almost_every_day": 5
}
},
{
"id": "q10",
"layout": "radio_question",
"question": "contact_with_germans_question",
"options": [
{ "key": "zero" },
{ "key": "one_to_three" },
{ "key": "three_to_six" },
{ "key": "seven_to_fourteen" },
{ "key": "fifteen_or_more" }
],
"pointsMap": {
"zero": 1,
"one_to_three": 2,
"three_to_six": 3,
"seven_to_fourteen": 4,
"fifteen_or_more": 5
}
},
{
"id": "q11",
"layout": "radio_question",
"question": "speaking_german_opinion_question",
"options": [
{ "key": "very_good" },
{ "key": "good" },
{ "key": "moderately_good" },
{ "key": "not_good" },
{ "key": "not_at_all" }
],
"pointsMap": {
"very_good": 1,
"good": 2,
"moderately_good": 3,
"not_good": 4,
"not_at_all": 5
}
},
{
"id": "q12",
"layout": "radio_question",
"question": "job_search_question",
"options": [
{ "key": "very_difficult" },
{ "key": "rather_difficult" },
{ "key": "neither_nor" },
{ "key": "rather_easy" },
{ "key": "very_easy" }
],
"pointsMap": {
"very_difficult": 1,
"rather_difficult": 2,
"neither_nor": 3,
"rather_easy": 4,
"very_easy": 5
}
},
{
"id": "last_page",
"layout": "last_page",
"textKey": "finish_data_entry",
"question": "data_final_warning"
}
]
}

View File

@ -0,0 +1,182 @@
{
"meta": {
"id": "questionnaire_4_consultation_results"
},
"questions": [
{
"id": "q0",
"layout": "client_coach_code_question",
"question": "client_code_entry_question",
"hint1": "client_code",
"hint2": "coach_code"
},
{
"id": "q1",
"layout": "date_spinner",
"question": "date_consultation_health_interview_result"
},
{
"id": "q2",
"layout": "radio_question",
"question": "consultation_decision",
"options": [
{
"key": "green",
"nextQuestionId": "q3"},
{"key": "yellow",
"nextQuestionId": "q4"},
{ "key": "red",
"nextQuestionId": "q9"}
],
"pointsMap": {
"green": 0,
"yellow": 0,
"red": 0
}
},
{
"id": "q3",
"layout": "radio_question",
"question": "consent_conversation_in_6_months",
"options": [
{ "key": "yes",
"nextQuestionId": "last_page"},
{ "key": "no",
"nextQuestionId": "last_page"}
],
"pointsMap": {
"yes": 0,
"no": 0
}
},
{
"id": "q4",
"layout": "radio_question",
"question": "participation_in_coaching",
"options": [
{"key": "yes",
"nextQuestionId": "q5"},
{"key": "no",
"nextQuestionId": "q6"},
{"key": "time_to_think_about_it",
"nextQuestionId": "q7"}
],
"pointsMap": {
"yes": 0,
"no": 0,
"time_to_think_about_it": 0
}
},
{
"id": "q5",
"layout": "radio_question",
"question": "consent_coaching_given",
"options": [
{"key": "consent_yes",
"nextQuestionId": "q3"},
{"key": "consent_no",
"nextQuestionId": "last_page"}
],
"pointsMap": {
"consent_yes": 0,
"consent_no": 0
}
},
{
"id": "q6",
"layout": "radio_question",
"question": "consent_conversation_in_6_months",
"options": [
{ "key": "yes",
"nextQuestionId": "last_page"},
{ "key": "no",
"nextQuestionId": "last_page"}
],
"pointsMap": {
"yes": 0,
"no": 0
}
},
{
"id": "q7",
"layout": "date_spinner",
"question": "decision_after_reflection_period"
},
{
"id": "q8",
"layout": "radio_question",
"question": "consent_conversation_in_6_months",
"options": [
{ "key": "yes",
"nextQuestionId": "last_page"},
{ "key": "no",
"nextQuestionId": "last_page"}
],
"pointsMap": {
"yes": 0,
"no": 0
}
},
{
"id": "q9",
"layout": "radio_question",
"question": "professional_referral",
"options": [
{ "key": "yes"},
{ "key": "no"}
],
"pointsMap": {
"yes": 0,
"no": 0
}
},
{
"id": "q10",
"layout": "radio_question",
"question": "confidentiality_agreement" ,
"options": [
{ "key": "available_yes"},
{ "key": "available_no",
"nextQuestionId": "last_page"}
],
"pointsMap": {
"yes": 0,
"no": 0
}
},
{
"id": "q11",
"layout": "radio_question",
"question": "health_insurance_card",
"options": [
{ "key": "yes"},
{ "key": "no"}
],
"pointsMap": {
"yes": 0,
"no": 0
}
},
{
"id": "q12",
"layout": "radio_question",
"question": "consent_conversation_in_6_months",
"options": [
{ "key": "yes",
"nextQuestionId": "last_page"},
{ "key": "no",
"nextQuestionId": "last_page"}
],
"pointsMap": {
"yes": 0,
"no": 0
}
},
{
"id": "last_page",
"layout": "last_page",
"textKey": "finish_data_entry",
"question": "data_final_warning"
}
]
}

View File

@ -0,0 +1,84 @@
{
"meta": {
"id": "questionnaire_5_final_interview"
},
"questions": [
{
"id": "q0",
"layout": "client_coach_code_question",
"question": "client_code_entry_question",
"hint1": "client_code",
"hint2": "coach_code"
},
{
"id": "q1",
"layout": "radio_question",
"question": "consent_followup_6_months",
"options": [
{ "key": "yes" },
{ "key": "no" }
]
},
{
"id": "q2",
"layout": "date_spinner",
"question": "date_final_interview"
},
{
"id": "q3",
"layout": "value_spinner",
"question": "amount_nat_appointments",
"range": {
"min": 0,
"max": 20
}
},
{
"id": "q4",
"layout": "value_spinner",
"question": "amount_session_flowers",
"range": {
"min": 0,
"max": 20
}
},
{
"id": "q5",
"layout": "value_spinner",
"question": "amount_session_stones",
"range": {
"min": 0,
"max": 20
}
},
{
"id": "q6",
"layout": "radio_question",
"question": "termination_nat_coaching",
"options": [
{"key": "termination_nat_regular",
"nextQuestionId": "last_page"},
{"key": "termination_nat_referral",
"nextQuestionId": "last_page"},
{"key": "termination_nat_dropout",
"nextQuestionId": "q7"}
]
},
{
"id": "q7",
"layout": "radio_question",
"question": "client_canceled_NAT",
"options": [
{"key": "after_meeting"},
{"key": "without_giving_reasons"},
{"key": "with_reasons_given"}
]
},
{
"id": "last_page",
"layout": "last_page",
"textKey": "finish_data_entry",
"question": "data_final_warning"
}
]
}

View File

@ -0,0 +1,349 @@
{
"meta": {
"id": "questionnaire_6_follow_up_survey"
},
"questions": [
{
"id": "q0",
"layout": "client_coach_code_question",
"question": "client_code_entry_question",
"hint1": "client_code",
"hint2": "coach_code"
},
{
"id": "q1",
"layout": "radio_question",
"question": "follow_up",
"options": [
{
"key": "follow_up_completed",
"nextQuestionId": "q2"
},
{
"key": "follow_up_failed_contact",
"nextQuestionId": "last_page"
},
{
"key": "follow_up_consent_withdrawn",
"nextQuestionId": "last_page"
}
],
"pointsMap": {
"follow_up_completed": 0,
"follow_up_failed_contact": 0,
"follow_up_consent_withdrawn": 0
}
},
{
"id": "q2",
"layout": "radio_question",
"question": "special_burden_question",
"options": [
{ "key": "yes" },
{ "key": "no" }
],
"pointsMap": {
"yes": 0,
"no": 0
}
},
{
"id": "q3",
"layout": "glass_scale_question",
"question": "glass_explanation",
"symptoms": [
"q1_symptom",
"q2_symptom",
"q3_symptom",
"q4_symptom",
"q5_symptom",
"q6_symptom",
"q7_symptom",
"q8_symptom",
"q9_symptom"
]
},
{
"id": "q4",
"layout": "glass_scale_question",
"question": "how_strong_past_month",
"symptoms": [
"q10_reexperience_trauma",
"q11_physical_reaction",
"q12_emotional_numbness",
"q13_easily_startled"
]
},
{
"id": "q5",
"layout": "radio_question",
"textKey": "resilience_reflection_prompt",
"question": "q14_intro",
"options": [
{ "key": "resilience_fully_capable" },
{ "key": "resilience_mostly_capable" },
{ "key": "resilience_some_capable_some_not" },
{ "key": "resilience_mostly_not_capable" },
{ "key": "resilience_not_capable" }
],
"pointsMap": {
"resilience_fully_capable": 0,
"resilience_mostly_capable": 0,
"resilience_some_capable_some_not": 0,
"resilience_mostly_not_capable": 0,
"resilience_not_capable": 0
}
},
{
"id": "q6",
"layout": "value_spinner",
"question": "pain_rating_instruction",
"range": {
"min": 0,
"max": 10
}
},
{
"id": "q7",
"layout": "radio_question",
"textKey": "intro_life_in_germany",
"question": "feeling_connected_to_germany_question",
"options": [
{ "key": "very_connected" },
{ "key": "quite_connected" },
{ "key": "moderately_connected" },
{ "key": "somewhat_loose" },
{ "key": "not_connected_at_all" }
],
"pointsMap": {
"very_connected": 0,
"quite_connected": 0,
"moderately_connected": 0,
"somewhat_loose": 0,
"not_connected_at_all": 0
}
},
{
"id": "q8",
"layout": "radio_question",
"question": "understanding_political_issues",
"options": [
{ "key": "very_good" },
{ "key": "good" },
{ "key": "fairly_good" },
{ "key": "somewhat" },
{ "key": "not_at_all" }
],
"pointsMap": {
"very_good": 0,
"good": 0,
"fairly_good": 0,
"somewhat": 0,
"not_at_all": 0
}
},
{
"id": "q9",
"layout": "radio_question",
"question": "unexpected_expense_question",
"options": [
{ "key": "expense_50" },
{ "key": "expense_100" },
{ "key": "expense_300" },
{ "key": "expense_500" },
{ "key": "expense_800" }
],
"pointsMap": {
"expense_50": 0,
"expense_100": 0,
"expense_300": 0,
"expense_500": 0,
"expense_800": 0
}
},
{
"id": "q10",
"layout": "radio_question",
"question": "dining_with_germans_question",
"options": [
{ "key": "never" },
{ "key": "once_a_year" },
{ "key": "once_a_month" },
{ "key": "once_a_week" },
{ "key": "almost_every_day" }
],
"pointsMap": {
"never": 0,
"once_a_year": 0,
"once_a_month": 0,
"once_a_week": 0,
"almost_every_day": 0
}
},
{
"id": "q11",
"layout": "radio_question",
"question": "reading_german_articles_question",
"options": [
{ "key": "very_good" },
{ "key": "good" },
{ "key": "moderately_good" },
{ "key": "not_good" },
{ "key": "not_at_all" }
],
"pointsMap": {
"very_good": 0,
"good": 0,
"moderately_good": 0,
"not_good": 0,
"not_at_all": 0
}
},
{
"id": "q12",
"layout": "radio_question",
"question": "visiting_doctor_question",
"options": [
{ "key": "very_difficult" },
{ "key": "rather_difficult" },
{ "key": "neither_nor" },
{ "key": "rather_easy" },
{ "key": "very_easy" }
],
"pointsMap": {
"very_difficult": 0,
"rather_difficult": 0,
"neither_nor": 0,
"rather_easy": 0,
"very_easy": 0
}
},
{
"id": "q13",
"layout": "radio_question",
"question": "feeling_as_outsider_question",
"options": [
{ "key": "never" },
{ "key": "rarely" },
{ "key": "sometimes" },
{ "key": "often" },
{ "key": "always" }
],
"pointsMap": {
"never": 0,
"rarely": 0,
"sometimes": 0,
"often": 0,
"always": 0
}
},
{
"id": "q14",
"layout": "radio_question",
"question": "recent_occupation_question",
"options": [
{ "key": "employed" },
{ "key": "education" },
{ "key": "internship" },
{ "key": "unemployed_searching" },
{ "key": "unemployed_not_searching" },
{ "key": "sick_or_disabled" },
{ "key": "unpaid_housework" },
{ "key": "other_activity" }
],
"pointsMap": {
"employed": 0,
"education": 0,
"internship": 0,
"unemployed_searching": 0,
"unemployed_not_searching": 0,
"sick_or_disabled": 0,
"unpaid_housework": 0,
"other_activity": 0
}
},
{
"id": "q15",
"layout": "radio_question",
"question": "discussing_politics_question",
"options": [
{ "key": "never" },
{ "key": "once_a_year" },
{ "key": "once_a_month" },
{ "key": "once_a_week" },
{ "key": "almost_every_day" }
],
"pointsMap": {
"never": 0,
"once_a_year": 0,
"once_a_month": 0,
"once_a_week": 0,
"almost_every_day": 0
}
},
{
"id": "q16",
"layout": "radio_question",
"question": "contact_with_germans_question",
"options": [
{ "key": "zero" },
{ "key": "one_to_three" },
{ "key": "three_to_six" },
{ "key": "seven_to_fourteen" },
{ "key": "fifteen_or_more" }
],
"pointsMap": {
"zero": 0,
"one_to_three": 0,
"three_to_six": 0,
"seven_to_fourteen": 0,
"fifteen_or_more": 0
}
},
{
"id": "q17",
"layout": "radio_question",
"question": "speaking_german_opinion_question",
"options": [
{ "key": "very_good" },
{ "key": "good" },
{ "key": "fairly_good" },
{ "key": "not_good" },
{ "key": "not_at_all" }
],
"pointsMap": {
"very_good": 0,
"good": 0,
"fairly_good": 0,
"not_good": 0,
"not_at_all": 0
}
},
{
"id": "q18",
"layout": "radio_question",
"question": "job_search_question",
"options": [
{ "key": "very_difficult" },
{ "key": "rather_difficult" },
{ "key": "neither_nor" },
{ "key": "rather_easy" },
{ "key": "very_easy" }
],
"pointsMap": {
"very_difficult": 0,
"rather_difficult": 0,
"neither_nor": 0,
"rather_easy": 0,
"very_easy": 0
}
},
{
"id": "last_page",
"layout": "last_page",
"textKey": "finish_data_entry",
"question": "data_final_warning"
}
]
}

View File

@ -0,0 +1,26 @@
[
{
"file": "questionnaire_1_demographic_information.json"
},
{
"file": "questionnaire_2_rhs.json"
},
{
"file": "questionnaire_3_integration_index.json"
},
{
"file": "questionnaire_4_consultation_results.json"
},
{
"file": "questionnaire_5_final_interview.json",
"condition": {
"questionnaire": "questionnaire_4_consultation_results",
"questionId": "consultation_decision",
"operator": "==",
"value": "yellow"
}
},
{
"file": "questionnaire_6_follow_up_survey.json"
}
]

View File

@ -0,0 +1,22 @@
package com.dano.test1.data
import androidx.room.Database
import androidx.room.RoomDatabase
@Database(
entities = [
Client::class,
Questionnaire::class,
Question::class,
Answer::class,
CompletedQuestionnaire::class
],
version = 10
)
abstract class AppDatabase : RoomDatabase() {
abstract fun clientDao(): ClientDao
abstract fun questionnaireDao(): QuestionnaireDao
abstract fun questionDao(): QuestionDao
abstract fun answerDao(): AnswerDao
abstract fun completedQuestionnaireDao(): CompletedQuestionnaireDao
}

View File

@ -0,0 +1,50 @@
package com.dano.test1
object Countries {
fun getAllCountries(languageID: String): List<String> {
val prompt = LanguageManager.getText(languageID, "choose_answer")
val countries = listOf(
"Afghanistan", "Albanien", "Algerien", "Andorra", "Angola",
"Antigua und Barbuda", "Argentinien", "Armenien", "Australien",
"Österreich", "Aserbaidschan", "Bahamas", "Bahrain", "Bangladesch",
"Barbados", "Belgien", "Belize", "Benin", "Bhutan", "Bolivien",
"Bosnien und Herzegowina", "Botswana", "Brasilien", "Brunei",
"Bulgarien", "Burkina Faso", "Burundi", "Cabo Verde", "Cambodia",
"Kamerun", "Kanada", "Zentralafrikanische Republik", "Tschad",
"Chile", "China", "Kolumbien", "Komoren", "Kongo (Dem. Rep.)",
"Kongo (Rep.)", "Costa Rica", "Kroatien", "Kuba", "Zypern",
"Tschechien", "Dänemark", "Djibouti", "Dominica",
"Dominikanische Republik", "Ecuador", "Ägypten", "El Salvador",
"Äquatorialguinea", "Eritrea", "Estland", "Eswatini", "Äthiopien",
"Fiji", "Finnland", "Frankreich", "Gabon", "Gambia", "Georgien",
"Deutschland", "Ghana", "Griechenland", "Grenada", "Guatemala",
"Guinea", "Guinea-Bissau", "Guyana", "Haiti", "Honduras", "Ungarn",
"Island", "Indien", "Indonesien", "Irak", "Iran", "Irland",
"Israel", "Italien", "Jamaika", "Japan", "Jordanien", "Kasachstan",
"Kenia", "Kiribati", "Korea (Nord)", "Korea (Süd)", "Kuwait",
"Kyrgyzstan", "Laos", "Lesotho", "Latvia", "Lebanon", "Liberia",
"Libyen", "Liechtenstein", "Litauen", "Luxemburg", "Madagaskar",
"Malawi", "Malaysia", "Maldiven", "Mali", "Malta", "Marokko",
"Marshallinseln", "Mauritius", "Mauritanien", "Mexiko", "Mikronesien",
"Moldawien", "Monaco", "Mongolei", "Montenegro", "Mozambique",
"Namibia", "Nauru", "Nepal", "Nicaragua", "Niger", "Nigeria",
"Nordmazedonien", "Norwegen", "Oman", "Pakistan", "Palau", "Panama",
"Papua-Neuguinea", "Paraguay", "Peru", "Philippinen", "Polen",
"Portugal", "Ruanda", "Rumänien", "Russland", "Salomonen", "Sambia",
"Samoa", "San Marino", "Sao Tome und Principe", "Saudi-Arabien",
"Senegal", "Serbien", "Seychellen", "Sierra Leone", "Singapur",
"Slowakei", "Slowenien", "Solomonen", "Südafrika", "Südkorea",
"Südsudan", "Spanien", "Sri Lanka", "St. Kitts und Nevis",
"St. Lucia", "St. Vincent und die Grenadinen", "Sudan", "Suriname",
"Syrien", "São Tomé und Príncipe", "Schweden", "Schweiz", "Simbabwe",
"Tadschikistan", "Taiwan", "Tansania", "Togo", "Trinidad und Tobago",
"Tonga", "Tunisien", "Türkei", "Turkmenistan", "Tuvalu", "Uganda",
"Ukraine", "Uruguay", "Usbekistan", "Vanuatu", "Venezuela",
"Vereinigte Arabische Emirate", "Vereinigte Staaten",
"Vereinigtes Königreich", "Vietnam", "Wallis und Futuna",
"Westjordanland", "Westsahara", "Yemen", "Zentralafrika", "Zypern",
"Zimbabwe"
)
return listOf(prompt) + countries
}
}

View File

@ -0,0 +1,88 @@
package com.dano.test1.data
import androidx.room.*
@Dao
interface ClientDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertClient(client: Client)
@Query("SELECT * FROM clients WHERE clientCode = :code LIMIT 1")
suspend fun getClientByCode(code: String): Client?
//@Query("SELECT * FROM clients")
//suspend fun getAllClients(): List<Client>
@Delete
suspend fun deleteClient(client: Client)
}
@Dao
interface QuestionnaireDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertQuestionnaire(questionnaire: Questionnaire)
@Query("SELECT * FROM questionnaires WHERE id = :id LIMIT 1")
suspend fun getById(id: String): Questionnaire?
//@Query("SELECT * FROM questionnaires")
//suspend fun getAllQuestionnaires(): List<Questionnaire>
}
@Dao
interface QuestionDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertQuestions(questions: List<Question>)
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertQuestion(question: Question)
@Query("SELECT * FROM questions WHERE questionId = :id LIMIT 1")
suspend fun getById(id: String): Question?
@Query("SELECT * FROM questions WHERE questionnaireId = :questionnaireId")
suspend fun getQuestionsForQuestionnaire(questionnaireId: String): List<Question>
//@Query("SELECT * FROM questions")
//suspend fun getAllQuestions(): List<Question> // <-- HIER NEU
}
@Dao
interface AnswerDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAnswers(answers: List<Answer>)
@Query("""
SELECT a.* FROM answers a
INNER JOIN questions q ON a.questionId = q.questionId
WHERE a.clientCode = :clientCode AND q.questionnaireId = :questionnaireId
""")
suspend fun getAnswersForClientAndQuestionnaire(
clientCode: String,
questionnaireId: String
): List<Answer>
@Query("SELECT * FROM answers WHERE clientCode = :clientCode")
suspend fun getAnswersForClient(clientCode: String): List<Answer>
}
@Dao
interface CompletedQuestionnaireDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(entry: CompletedQuestionnaire)
@Query("SELECT * FROM completed_questionnaires WHERE clientCode = :clientCode AND questionnaireId = :questionnaireId LIMIT 1")
suspend fun getStatus(clientCode: String, questionnaireId: String): CompletedQuestionnaire?
@Query("SELECT * FROM completed_questionnaires WHERE clientCode = :clientCode")
suspend fun getAllForClient(clientCode: String): List<CompletedQuestionnaire>
@Query("SELECT questionnaireId FROM completed_questionnaires WHERE clientCode = :clientCode")
suspend fun getCompletedQuestionnairesForClient(clientCode: String): List<String>
}

View File

@ -0,0 +1,84 @@
package com.dano.test1.data
import androidx.room.*
@Entity(tableName = "clients")
data class Client(
@PrimaryKey val clientCode: String,
)
@Entity(tableName = "questionnaires")
data class Questionnaire(
@PrimaryKey val id: String,
)
@Entity(
tableName = "questions",
foreignKeys = [
ForeignKey(
entity = Questionnaire::class,
parentColumns = ["id"],
childColumns = ["questionnaireId"],
onDelete = ForeignKey.CASCADE
)
],
indices = [Index("questionnaireId")]
)
data class Question(
@PrimaryKey val questionId: String,
val questionnaireId: String,
val question: String = ""
)
@Entity(
tableName = "answers",
primaryKeys = ["clientCode", "questionId"],
foreignKeys = [
ForeignKey(
entity = Client::class,
parentColumns = ["clientCode"],
childColumns = ["clientCode"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = Question::class,
parentColumns = ["questionId"],
childColumns = ["questionId"],
onDelete = ForeignKey.CASCADE
)
],
indices = [Index("clientCode"), Index("questionId")]
)
data class Answer(
val clientCode: String,
val questionId: String,
val answerValue: String = ""
)
@Entity(
tableName = "completed_questionnaires",
primaryKeys = ["clientCode", "questionnaireId"],
foreignKeys = [
ForeignKey(
entity = Client::class,
parentColumns = ["clientCode"],
childColumns = ["clientCode"],
onDelete = ForeignKey.CASCADE
),
ForeignKey(
entity = Questionnaire::class,
parentColumns = ["id"],
childColumns = ["questionnaireId"],
onDelete = ForeignKey.CASCADE
)
],
indices = [Index("clientCode"), Index("questionnaireId")]
)
data class CompletedQuestionnaire(
val clientCode: String,
val questionnaireId: String,
val timestamp: Long = System.currentTimeMillis(),
val isDone: Boolean,
val sumPoints: Int? = null
)

View File

@ -0,0 +1,116 @@
package com.dano.test1
import android.view.View
import android.widget.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class HandlerClientCoachCode(
private val answers: MutableMap<String, Any>,
private val languageID: String,
private val goToNextQuestion: () -> Unit,
private val goToPreviousQuestion: () -> Unit,
private val showToast: (String) -> Unit,
) : QuestionHandler {
private lateinit var question: QuestionItem.ClientCoachCodeQuestion
private lateinit var layout: View
override fun bind(layout: View, question: QuestionItem) {
if (question !is QuestionItem.ClientCoachCodeQuestion) return
this.layout = layout
this.question = question
// Bind UI components
val clientCodeField = layout.findViewById<EditText>(R.id.client_code)
val coachCodeField = layout.findViewById<EditText>(R.id.coach_code)
val questionTextView = layout.findViewById<TextView>(R.id.question)
// Fill question text using language manager
questionTextView.text = question.question?.let {
LanguageManager.getText(languageID, it)
} ?: ""
// Load last used client code if available
val lastClientCode = GlobalValues.LAST_CLIENT_CODE
if (!lastClientCode.isNullOrBlank()) {
clientCodeField.setText(lastClientCode)
clientCodeField.isEnabled = false
} else {
clientCodeField.setText(answers["client_code"] as? String ?: "")
clientCodeField.isEnabled = true
}
// Load saved coach code
coachCodeField.setText(answers["coach_code"] as? String ?: "")
// Set click listener for Next button
layout.findViewById<Button>(R.id.Qnext).setOnClickListener {
onNextClicked(clientCodeField, coachCodeField)
}
// Set click listener for Previous button
layout.findViewById<Button>(R.id.Qprev).setOnClickListener {
onPreviousClicked(clientCodeField, coachCodeField)
}
}
// Handle Next button click
private fun onNextClicked(clientCodeField: EditText, coachCodeField: EditText) {
if (!validate()) {
val message = LanguageManager.getText(languageID, "fill_both_fields")
showToast(message)
return
}
val clientCode = clientCodeField.text.toString()
val coachCode = coachCodeField.text.toString()
// Check if client code already exists asynchronously
CoroutineScope(Dispatchers.IO).launch {
val existingClient = MyApp.database.clientDao().getClientByCode(clientCode)
withContext(Dispatchers.Main) {
if (existingClient != null && clientCodeField.isEnabled) {
// Client code already exists and field was editable
val message = LanguageManager.getText(languageID, "client_code_exists")
showToast(message)
} else {
// Either no existing client or re-using previous code
saveAnswers(clientCode, coachCode)
goToNextQuestion()
}
}
}
}
// Handle Previous button click
private fun onPreviousClicked(clientCodeField: EditText, coachCodeField: EditText) {
val clientCode = clientCodeField.text.toString()
val coachCode = coachCodeField.text.toString()
saveAnswers(clientCode, coachCode)
goToPreviousQuestion()
}
// Validate that both fields are filled
override fun validate(): Boolean {
val clientCode = layout.findViewById<EditText>(R.id.client_code).text
val coachCode = layout.findViewById<EditText>(R.id.coach_code).text
return clientCode.isNotBlank() && coachCode.isNotBlank()
}
// Save answers to shared state and global value
private fun saveAnswers(clientCode: String, coachCode: String) {
GlobalValues.LAST_CLIENT_CODE = clientCode
answers["client_code"] = clientCode
answers["coach_code"] = coachCode
}
// Required override but not used here
override fun saveAnswer() {
// Not used
}
}

View File

@ -0,0 +1,80 @@
package com.dano.test1
import android.view.View
import android.widget.*
class HandlerClientNotSigned(
private val answers: MutableMap<String, Any>,
private val languageID: String,
private val goToNextQuestion: () -> Unit,
private val goToPreviousQuestion: () -> Unit,
private val showToast: (String) -> Unit
) : QuestionHandler {
private lateinit var layout: View
private lateinit var question: QuestionItem.ClientNotSigned
// UI components
private lateinit var textView1: TextView
private lateinit var textView2: TextView
private lateinit var questionTextView: TextView
private lateinit var coachCodeField: EditText
override fun bind(layout: View, question: QuestionItem) {
if (question !is QuestionItem.ClientNotSigned) return
this.layout = layout
this.question = question
// Initialize UI components only once
initViews()
// Set localized text values from LanguageManager
textView1.text = question.textKey1?.let { LanguageManager.getText(languageID, it) } ?: ""
textView2.text = question.textKey2?.let { LanguageManager.getText(languageID, it) } ?: ""
questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
// Populate EditText with previous value if exists
coachCodeField.setText(answers[question.id] as? String ?: "")
// Set click listener for Next button
layout.findViewById<Button>(R.id.Qnext).setOnClickListener {
onNextClicked()
}
// Set click listener for Previous button
layout.findViewById<Button>(R.id.Qprev).setOnClickListener {
goToPreviousQuestion()
}
}
// Initialize all views once to avoid repeated findViewById calls
private fun initViews() {
textView1 = layout.findViewById(R.id.textView1)
textView2 = layout.findViewById(R.id.textView2)
questionTextView = layout.findViewById(R.id.question)
coachCodeField = layout.findViewById(R.id.coach_code)
}
// Handle Next button click
private fun onNextClicked() {
if (validate()) {
saveAnswer()
goToNextQuestion()
} else {
val message = LanguageManager.getText(languageID, "enter_coach_code")
showToast(message)
}
}
// Validate that coach code field is not empty
override fun validate(): Boolean {
val coachCode = coachCodeField.text
return coachCode.isNotBlank()
}
// Save entered coach code to answers map
override fun saveAnswer() {
answers[question.id] = coachCodeField.text.toString()
}
}

View File

@ -0,0 +1,154 @@
package com.dano.test1
import android.content.Context
import android.view.View
import android.widget.*
import java.text.SimpleDateFormat
import java.util.*
class HandlerDateSpinner(
private val context: Context,
private val answers: MutableMap<String, Any>,
private val languageID: String,
private val goToNextQuestion: () -> Unit,
private val goToPreviousQuestion: () -> Unit,
private val showToast: (String) -> Unit
) : QuestionHandler {
private lateinit var question: QuestionItem.DateSpinnerQuestion
private lateinit var layout: View
private lateinit var spinnerDay: Spinner
private lateinit var spinnerMonth: Spinner
private lateinit var spinnerYear: Spinner
override fun bind(layout: View, question: QuestionItem) {
if (question !is QuestionItem.DateSpinnerQuestion) return
this.layout = layout
this.question = question
initViews()
val questionTextView = layout.findViewById<TextView>(R.id.question)
val textView = layout.findViewById<TextView>(R.id.textView)
questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
textView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
val (savedYear, savedMonthIndex, savedDay) = question.question?.let {
parseSavedDate(answers[it] as? String)
} ?: Triple(null, null, null)
val days = (1..31).toList()
val months = Months.getAllMonths(languageID)
val years = (1900..MAX_VALUE_YEAR + 1).toList().reversed()
val today = Calendar.getInstance()
val defaultDay = savedDay ?: today.get(Calendar.DAY_OF_MONTH)
val defaultMonth = savedMonthIndex?.takeIf { it in months.indices }?.let { months[it] }
?: months[today.get(Calendar.MONTH)]
val defaultYear = savedYear ?: today.get(Calendar.YEAR)
setupSpinner(spinnerDay, days, defaultDay)
setupSpinner(spinnerMonth, months, defaultMonth)
setupSpinner(spinnerYear, years, defaultYear)
layout.findViewById<Button>(R.id.Qnext).setOnClickListener {
if (validate()) {
saveAnswer()
goToNextQuestion()
}
}
layout.findViewById<Button>(R.id.Qprev).setOnClickListener {
goToPreviousQuestion()
}
}
private fun initViews() {
spinnerDay = layout.findViewById(R.id.spinner_value_day)
spinnerMonth = layout.findViewById(R.id.spinner_value_month)
spinnerYear = layout.findViewById(R.id.spinner_value_year)
}
override fun validate(): Boolean {
val day = spinnerDay.selectedItem.toString().padStart(2, '0')
val month = spinnerMonth.selectedItem as Month
val year = spinnerYear.selectedItem.toString()
val allMonths = Months.getAllMonths(languageID)
val monthNumber = (allMonths.indexOf(month) + 1).toString().padStart(2, '0')
val selectedDateString = "$year-$monthNumber-$day"
val selectedDate = parseDate(selectedDateString)
question.constraints?.notBefore?.let { key ->
val referenceDateString = answers[key] as? String
val referenceDate = referenceDateString?.let { parseDate(it) }
if (referenceDate != null && selectedDate.before(referenceDate)) {
val message1 = LanguageManager.getText(languageID, "date_after")
val message2 = LanguageManager.getText(languageID, "lay")
showToast(message1 + " " + referenceDateString + " " + message2)
return false
}
}
question.constraints?.notAfter?.let { key ->
val referenceDate = when (key) {
"today" -> Date()
else -> (answers[key] as? String)?.let { parseDate(it) }
}
if (referenceDate != null && selectedDate.after(referenceDate)) {
val message1 = LanguageManager.getText(languageID, "date_before")
val message2 = LanguageManager.getText(languageID, "lay")
showToast(message1 + " " + referenceDate + " " + message2)
return false
}
}
return true
}
override fun saveAnswer() {
val day = spinnerDay.selectedItem.toString().padStart(2, '0')
val month = spinnerMonth.selectedItem as Month
val year = spinnerYear.selectedItem.toString()
val allMonths = Months.getAllMonths(languageID)
val monthNumber = (allMonths.indexOf(month) + 1).toString().padStart(2, '0')
question.question?.let { key ->
answers[key] = "$year-$monthNumber-$day"
}
}
private fun parseSavedDate(dateString: String?): Triple<Int?, Int?, Int?> {
if (dateString.isNullOrBlank()) return Triple(null, null, null)
val parts = dateString.split("-")
if (parts.size != 3) return Triple(null, null, null)
val year = parts[0].toIntOrNull()
val monthIndex = parts[1].toIntOrNull()?.minus(1)
val day = parts[2].toIntOrNull()
return Triple(year, monthIndex, day)
}
private fun parseDate(dateString: String): Date {
val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault())
return sdf.parse(dateString)
}
private fun <T> setupSpinner(spinner: Spinner, items: List<T>, defaultSelection: T?) {
val adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, items)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = adapter
defaultSelection?.let {
val index = items.indexOf(it)
if (index >= 0) {
spinner.setSelection(index)
}
}
}
}

View File

@ -0,0 +1,167 @@
package com.dano.test1
import android.content.Context
import android.view.Gravity
import android.view.View
import android.widget.*
class HandlerGlassScaleQuestion(
private val context: Context,
private val answers: MutableMap<String, Any>,
private val points: MutableList<Int>,
private val languageID: String,
private val goToNextQuestion: () -> Unit,
private val goToPreviousQuestion: () -> Unit,
private val showToast: (String) -> Unit
) : QuestionHandler {
private lateinit var layout: View
private lateinit var question: QuestionItem.GlassScaleQuestion
private val scaleLabels = listOf(
"never_glass", "little_glass", "moderate_glass", "much_glass", "extreme_glass"
)
private val pointsMap = mapOf(
"never_glass" to 0,
"little_glass" to 1,
"moderate_glass" to 2,
"much_glass" to 3,
"extreme_glass" to 4
)
override fun bind(layout: View, question: QuestionItem) {
if (question !is QuestionItem.GlassScaleQuestion) return
this.layout = layout
this.question = question
layout.findViewById<TextView>(R.id.textView).text =
question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
layout.findViewById<TextView>(R.id.question).text =
question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
val tableLayout = layout.findViewById<TableLayout>(R.id.glass_table)
tableLayout.removeAllViews()
val headerRow = TableRow(context).apply {
layoutParams = TableLayout.LayoutParams(
TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT
)
gravity = Gravity.CENTER
}
val emptyCell = TextView(context).apply {
layoutParams = TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT, 4f)
}
headerRow.addView(emptyCell)
scaleLabels.forEach { labelKey ->
val labelText = LanguageManager.getText(languageID, labelKey)
val labelView = TextView(context).apply {
text = labelText
gravity = Gravity.START
layoutParams = TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT, 1f)
}
headerRow.addView(labelView)
}
tableLayout.addView(headerRow)
addSymptomRows(tableLayout)
layout.findViewById<Button>(R.id.Qnext).setOnClickListener {
if (validate()) {
saveAnswer()
goToNextQuestion()
} else {
val message = LanguageManager.getText(languageID, "select_one_answer_per_row")
showToast(message)
}
}
layout.findViewById<Button>(R.id.Qprev).setOnClickListener {
goToPreviousQuestion()
}
}
private fun addSymptomRows(table: TableLayout) {
question.symptoms.forEach { symptomKey ->
val savedLabel = answers[symptomKey] as? String
val row = TableRow(context).apply {
layoutParams = TableRow.LayoutParams(
TableRow.LayoutParams.MATCH_PARENT,
TableRow.LayoutParams.WRAP_CONTENT
)
}
val symptomText = TextView(context).apply {
text = LanguageManager.getText(languageID, symptomKey)
layoutParams = TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT, 4f)
setPadding(4, 16, 4, 16)
}
row.addView(symptomText)
val radioGroup = RadioGroup(context).apply {
orientation = RadioGroup.HORIZONTAL
layoutParams = TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT, 5f)
}
scaleLabels.forEach { labelKey ->
val radioButton = RadioButton(context).apply {
tag = labelKey
id = View.generateViewId()
isChecked = savedLabel == labelKey
layoutParams =
RadioGroup.LayoutParams(0, RadioGroup.LayoutParams.WRAP_CONTENT, 1f)
gravity = Gravity.CENTER
}
radioGroup.addView(radioButton)
}
row.addView(radioGroup)
table.addView(row)
}
}
override fun validate(): Boolean {
val table = layout.findViewById<TableLayout>(R.id.glass_table)
for (i in 1 until table.childCount) {
val row = table.getChildAt(i) as TableRow
val radioGroup = row.getChildAt(1) as RadioGroup
if (radioGroup.checkedRadioButtonId == -1) {
return false
}
}
return true
}
override fun saveAnswer() {
// Vorherige Punkte dieser Frage entfernen
question.symptoms.forEach {
val previousLabel = answers[it] as? String
val previousPoint = pointsMap[previousLabel]
if (previousPoint != null) {
points.remove(previousPoint)
}
}
val table = layout.findViewById<TableLayout>(R.id.glass_table)
for (i in 1 until table.childCount) {
val row = table.getChildAt(i) as TableRow
val symptomKey = question.symptoms[i - 1]
val radioGroup = row.getChildAt(1) as RadioGroup
val checkedId = radioGroup.checkedRadioButtonId
if (checkedId != -1) {
val radioButton = radioGroup.findViewById<RadioButton>(checkedId)
val selectedLabel = radioButton.tag as String
answers[symptomKey] = selectedLabel
val point = pointsMap[selectedLabel] ?: 0
points.add(point)
}
}
}
}

View File

@ -0,0 +1,93 @@
package com.dano.test1
import android.view.View
import android.widget.*
import android.text.Html
import kotlinx.coroutines.*
class HandlerLastPage(
private val answers: Map<String, Any>,
private val languageID: String,
private val goToNextQuestion: () -> Unit,
private val goToPreviousQuestion: () -> Unit,
private val saveAnswersToDatabase: suspend (Map<String, Any>) -> Unit
) : QuestionHandler {
private lateinit var currentQuestion: QuestionItem.LastPage
private lateinit var layout: View
private val minLoadingTimeMs = 2000L // Minimum loading time in milliseconds (2 seconds)
override fun bind(layout: View, question: QuestionItem) {
this.layout = layout
currentQuestion = question as QuestionItem.LastPage
// Set localized text for the last page
layout.findViewById<TextView>(R.id.textView).text =
LanguageManager.getText(languageID, currentQuestion.textKey)
// Set question text with HTML formatting
layout.findViewById<TextView>(R.id.question).text =
Html.fromHtml(
LanguageManager.getText(languageID, currentQuestion.question),
Html.FROM_HTML_MODE_LEGACY
)
// Setup previous button
layout.findViewById<Button>(R.id.Qprev).setOnClickListener {
goToPreviousQuestion()
}
// Setup finish button
layout.findViewById<Button>(R.id.Qfinish).setOnClickListener {
showLoading(true) // Show loading indicator
// Save answers on a background thread
CoroutineScope(Dispatchers.IO).launch {
val startTime = System.currentTimeMillis()
// Save answers to database (suspend function)
saveAnswersToDatabase(answers)
// Calculate total points and update global value
GlobalValues.INTEGRATION_INDEX = sumPoints()
// Save last client code globally if available
val clientCode = answers["client_code"] as? String
if (clientCode != null) GlobalValues.LAST_CLIENT_CODE = clientCode
// Ensure loading animation runs at least 2 seconds
val elapsedTime = System.currentTimeMillis() - startTime
if (elapsedTime < minLoadingTimeMs) {
delay(minLoadingTimeMs - elapsedTime)
}
// Switch back to main thread to update UI
withContext(Dispatchers.Main) {
showLoading(false) // Hide loading indicator
val activity = layout.context as? MainActivity
activity?.finishQuestionnaire() ?: goToNextQuestion()
}
}
}
}
override fun validate(): Boolean = true // No validation needed on last page
override fun saveAnswer() {} // No answers to save here
// Calculate the sum of all keys ending with "_points"
private fun sumPoints(): Int =
answers.filterKeys { it.endsWith("_points") }
.values.mapNotNull { it as? Int }
.sum()
// Show or hide a ProgressBar (loading spinner)
private fun showLoading(show: Boolean) {
val progressBar = layout.findViewById<ProgressBar>(R.id.progressBar)
val finishButton = layout.findViewById<Button>(R.id.Qfinish)
val prevButton = layout.findViewById<Button>(R.id.Qprev)
progressBar?.visibility = if (show) View.VISIBLE else View.GONE
finishButton?.isEnabled = !show
prevButton?.isEnabled = !show
}
}

View File

@ -0,0 +1,104 @@
package com.dano.test1
import android.content.Context
import android.view.View
import android.widget.*
class HandlerMultiCheckboxQuestion(
private val context: Context,
private val answers: MutableMap<String, Any>,
private val points: MutableList<Int>,
private val languageID: String,
private val goToNextQuestion: () -> Unit,
private val goToPreviousQuestion: () -> Unit,
private val showToast: (String) -> Unit
) : QuestionHandler {
private lateinit var layout: View
private lateinit var question: QuestionItem.MultiCheckboxQuestion
override fun bind(layout: View, question: QuestionItem) {
this.layout = layout
this.question = question as QuestionItem.MultiCheckboxQuestion
val container = layout.findViewById<LinearLayout>(R.id.CheckboxContainer)
val questionTitle = layout.findViewById<TextView>(R.id.question)
val questionTextView = layout.findViewById<TextView>(R.id.textView)
// Hier jetzt identisch zur RadioQuestion:
questionTextView.text = this.question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
questionTitle.text = this.question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
container.removeAllViews()
val selectedKeys = this.question.question?.let {
(answers[it] as? List<*>)?.map { it.toString() }?.toSet()
} ?: emptySet()
this.question.options.forEach { option ->
val checkBox = CheckBox(context).apply {
text = LanguageManager.getText(languageID, option.key)
tag = option.key
isChecked = selectedKeys.contains(option.key)
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply {
val scale = context.resources.displayMetrics.density
val marginBottom = (16 * scale + 0.5f).toInt()
setMargins(0, 0, 0, marginBottom)
}
}
container.addView(checkBox)
}
layout.findViewById<Button>(R.id.Qnext).setOnClickListener {
if (validate()) {
saveAnswer()
goToNextQuestion()
} else {
val msgKey = if (question.minSelection == 1) {
"select_at_least_one_answer"
} else {
"select_at_least_minimum"
}
val errorMessage = LanguageManager.getTextFormatted(languageID, msgKey, "choose_more_elements")
showToast(errorMessage)
}
}
layout.findViewById<Button>(R.id.Qprev).setOnClickListener {
goToPreviousQuestion()
}
}
override fun validate(): Boolean {
val container = layout.findViewById<LinearLayout>(R.id.CheckboxContainer)
var selectedCount = 0
for (i in 0 until container.childCount) {
val checkBox = container.getChildAt(i) as? CheckBox ?: continue
if (checkBox.isChecked) selectedCount++
}
return selectedCount >= question.minSelection
}
override fun saveAnswer() {
val container = layout.findViewById<LinearLayout>(R.id.CheckboxContainer)
val selectedKeys = mutableListOf<String>()
var totalPoints = 0
for (i in 0 until container.childCount) {
val checkBox = container.getChildAt(i) as? CheckBox ?: continue
if (checkBox.isChecked) {
val key = checkBox.tag.toString()
selectedKeys.add(key)
totalPoints += question.pointsMap?.get(key) ?: 0
}
}
question.question?.let { questionKey ->
answers[questionKey] = selectedKeys
points.add(totalPoints)
}
}
}

View File

@ -0,0 +1,513 @@
package com.dano.test1
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Color
import android.graphics.pdf.PdfDocument
import android.view.View
import android.widget.*
import kotlinx.coroutines.*
import org.json.JSONArray
import android.util.Log
import java.util.Calendar
// Global constants and values
var INTEGRATION_INDEX_POINTS: Int? = null
class HandlerOpeningScreen(private val activity: MainActivity) {
private var languageID: String = "GERMAN"
private lateinit var editText: EditText
private lateinit var spinner: Spinner
private lateinit var textView: TextView
private lateinit var buttonContainer: LinearLayout
private lateinit var buttonLoad: Button
private val dynamicButtons = mutableListOf<Button>()
private val questionnaireFiles = mutableMapOf<Button, String>()
private val buttonPoints: MutableMap<String, Int> = mutableMapOf()
private var questionnaireOrder: List<String> = emptyList()
private var questionnaireEntries: List<QuestionItem.QuestionnaireEntry> = emptyList()
fun init() {
activity.setContentView(R.layout.opening_screen)
bindViews()
loadQuestionnaireOrder()
createQuestionnaireButtons()
restorePreviousClientCode()
setupLanguageSpinner()
setupLoadButton()
if (!editText.text.isNullOrBlank()) {
buttonLoad.performClick()
}
}
private fun bindViews() {
editText = activity.findViewById(R.id.editText)
spinner = activity.findViewById(R.id.string_spinner1)
textView = activity.findViewById(R.id.textView)
buttonContainer = activity.findViewById(R.id.buttonContainer)
buttonLoad = activity.findViewById(R.id.loadButton)
val tag = editText.tag as? String ?: ""
editText.hint = LanguageManager.getText(languageID, tag)
textView.text = LanguageManager.getText(languageID, "example_text")
}
private fun loadQuestionnaireOrder() {
try {
val inputStream = activity.assets.open("questionnaire_order.json")
val json = inputStream.bufferedReader().use { it.readText() }
val jsonArray = JSONArray(json)
questionnaireEntries = (0 until jsonArray.length()).map { i ->
val obj = jsonArray.getJSONObject(i)
val file = obj.getString("file")
val conditionObj = obj.optJSONObject("condition")
val condition = if (conditionObj != null) {
QuestionItem.Condition(
questionnaire = conditionObj.getString("questionnaire"),
questionId = conditionObj.getString("questionId"),
operator = conditionObj.getString("operator"),
value = conditionObj.getString("value")
)
} else null
QuestionItem.QuestionnaireEntry(file, condition)
}
} catch (e: Exception) {
e.printStackTrace()
questionnaireEntries = emptyList()
}
}
private fun createQuestionnaireButtons() {
buttonContainer.removeAllViews()
dynamicButtons.clear()
questionnaireFiles.clear()
for ((index, entry) in questionnaireEntries.withIndex()) {
val button = Button(activity).apply {
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
).apply { setMargins(0, 8, 0, 8) }
text = "Questionnaire ${index + 1}"
id = View.generateViewId()
}
buttonContainer.addView(button)
dynamicButtons.add(button)
questionnaireFiles[button] = entry.file
}
updateButtonTexts()
setButtonsEnabled(listOf(dynamicButtons.firstOrNull()).filterNotNull())
dynamicButtons.forEach { button ->
button.setOnClickListener {
startQuestionnaireForButton(button)
setButtonsEnabled(dynamicButtons.filter { it != button })
}
}
}
private fun restorePreviousClientCode() {
GlobalValues.LAST_CLIENT_CODE?.let { editText.setText(it) }
}
private fun setupLanguageSpinner() {
val languages = listOf("GERMAN", "ENGLISH", "FRENCH", "ROMANIAN", "ARABIC", "POLISH", "TURKISH", "UKRAINIAN", "RUSSIAN", "SPANISH")
val adapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, languages).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
spinner.adapter = adapter
spinner.setSelection(languages.indexOf(languageID))
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
languageID = languages[position]
updateButtonTexts()
val hintTag = editText.tag as? String ?: ""
editText.hint = LanguageManager.getText(languageID, hintTag)
}
override fun onNothingSelected(parent: AdapterView<*>) {}
}
}
private fun setupLoadButton() {
buttonLoad.text = LanguageManager.getText(languageID, "load")
buttonLoad.setOnClickListener { handleLoadButton() }
}
private fun handleLoadButton() {
buttonPoints.clear()
updateButtonTexts()
setButtonsEnabled(emptyList())
val inputText = editText.text.toString().trim()
if (inputText.isBlank()) {
val message = LanguageManager.getText(languageID, "please_client_code")
Toast.makeText(activity, message, Toast.LENGTH_SHORT).show()
return
}
val isDatabaseView = inputText.endsWith("_database")
val clientCode = if (isDatabaseView) inputText.removeSuffix("_database") else inputText
GlobalValues.LAST_CLIENT_CODE = clientCode
CoroutineScope(Dispatchers.IO).launch {
val client = MyApp.database.clientDao().getClientByCode(clientCode)
if (client == null) {
withContext(Dispatchers.Main) {
val message = LanguageManager.getText(languageID, "no_profile")
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
setButtonsEnabled(listOf(dynamicButtons.firstOrNull()).filterNotNull())
}
return@launch
}
// Profil ist gültig → entweder normal laden oder PDF erzeugen
withContext(Dispatchers.Main) {
if (isDatabaseView) {
// Profil laden + PDF erzeugen
handleNormalLoad(clientCode) // ← Option: Zeige auch Punktefarben etc.
showCompletedQuestionnaires(clientCode)
} else {
handleNormalLoad(clientCode)
}
}
}
}
private suspend fun handleNormalLoad(clientCode: String) {
val completedIds = withContext(Dispatchers.IO) {
MyApp.database.completedQuestionnaireDao().getCompletedQuestionnairesForClient(clientCode)
}
if (completedIds.isEmpty()) {
setButtonsEnabled(listOf(dynamicButtons.firstOrNull()).filterNotNull())
val message = LanguageManager.getText(languageID, "no_profile")
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
return
}
val completedIndexes = completedIds.mapNotNull { id ->
questionnaireEntries.indexOfFirst { it.file.contains(id, ignoreCase = true) }.takeIf { it >= 0 }
}.sorted()
val completedEntries = withContext(Dispatchers.IO) {
MyApp.database.completedQuestionnaireDao().getAllForClient(clientCode)
}
buttonPoints.clear()
for (entry in completedEntries) {
if (entry.isDone && (entry.sumPoints ?: 0) > 0) {
buttonPoints[entry.questionnaireId] = entry.sumPoints ?: 0
if (entry.questionnaireId.contains("questionnaire_3_integration_index", ignoreCase = true)) {
INTEGRATION_INDEX_POINTS = entry.sumPoints
}
}
}
updateButtonTexts()
val rhsPoints = buttonPoints.entries.firstOrNull { it.key.contains("questionnaire_2_rhs", ignoreCase = true) }?.value
var nextIndex = (completedIndexes.lastOrNull() ?: -1) + 1
while (nextIndex < questionnaireEntries.size) {
val entry = questionnaireEntries[nextIndex]
val condition = entry.condition
if (condition != null) {
val answers = MyApp.database.answerDao()
.getAnswersForClientAndQuestionnaire(clientCode, condition.questionnaire)
val relevantAnswer = answers.find {
it.questionId.endsWith(condition.questionId)
}
val answerValue = relevantAnswer?.answerValue ?: ""
val conditionMet = when (condition.operator) {
"!=" -> answerValue != condition.value
"==" -> answerValue == condition.value
else -> true // fallback: zeige Fragebogen
}
if (conditionMet) break // Bedingung erfüllt → anzeigen
else nextIndex++ // überspringen
} else {
break // keine Bedingung → anzeigen
}
}
if (nextIndex >= questionnaireEntries.size) {
setButtonsEnabled(emptyList())
val message = LanguageManager.getText(languageID, "questionnaires_finished")
Toast.makeText(activity, message, Toast.LENGTH_LONG).show()
} else {
val nextFileName = questionnaireEntries[nextIndex].file
val nextButton = questionnaireFiles.entries.firstOrNull { it.value == nextFileName }?.key
setButtonsEnabled(listOfNotNull(nextButton))
}
}
private fun resetToStartState() {
GlobalValues.LAST_CLIENT_CODE = null
editText.setText("")
setButtonsEnabled(listOf(dynamicButtons.firstOrNull()).filterNotNull())
}
private fun updateButtonTexts() {
questionnaireFiles.forEach { (button, fileName) ->
val key = fileName.substringAfter("questionnaire_").substringAfter("_").removeSuffix(".json")
var buttonText = LanguageManager.getText(languageID, key)
val matchedEntry = buttonPoints.entries.firstOrNull { fileName.contains(it.key, ignoreCase = true) }
val points = matchedEntry?.value ?: 0
if (points > 0) {
buttonText += " (${points} P)"
}
button.text = buttonText
// Farbgebung je nach Punktzahl
when {
points in 1..12 -> button.setBackgroundColor(Color.parseColor("#4CAF50")) // Grün
points in 13..36 -> button.setBackgroundColor(Color.parseColor("#FFEB3B")) // Gelb
points in 37..100 -> button.setBackgroundColor(Color.parseColor("#F44336")) // Rot
else -> button.setBackgroundColor(Color.parseColor("#E0E0E0")) // Standardgrau bei 0
}
}
buttonLoad.text = LanguageManager.getText(languageID, "load")
}
private fun setButtonsEnabled(enabledButtons: List<Button>) {
questionnaireFiles.keys.forEach { button ->
val fileName = questionnaireFiles[button] ?: ""
val shouldDisable = fileName.contains("questionnaire_5_final_interview.json", ignoreCase = true) &&
buttonPoints.entries.firstOrNull { it.key.contains("questionnaire_2_rhs", ignoreCase = true) }?.value in 13..36
button.isEnabled = enabledButtons.contains(button) && !shouldDisable
button.alpha = if (button.isEnabled) 1.0f else 0.5f
}
}
private fun startQuestionnaireForButton(button: Button) {
val fileName = questionnaireFiles[button] ?: return
val questionnaire = QuestionnaireGeneric(fileName)
startQuestionnaire(questionnaire)
}
private fun startQuestionnaire(questionnaire: QuestionnaireBase<*>) {
activity.startQuestionnaire(questionnaire, languageID)
}
fun onBackPressed(): Boolean = false
private fun showCompletedQuestionnaires(clientCode: String) {
CoroutineScope(Dispatchers.IO).launch {
val actualClientCode = clientCode.removeSuffix("_database")
val completedEntries = MyApp.database.completedQuestionnaireDao().getAllForClient(actualClientCode)
Log.d("PDF_DEBUG", "Completed entries for client $actualClientCode:")
for (entry in completedEntries) {
Log.d("PDF_DEBUG", "Questionnaire ID: ${entry.questionnaireId}, Done: ${entry.isDone}, Points: ${entry.sumPoints}")
}
// ===== PDF ERSTELLUNG =====
val pdfDocument = PdfDocument()
val pageWidth = 595
val pageHeight = 842
val paint = Paint().apply { textSize = 12f }
// ===== CSV AUFBAU =====
val csvBuilder = StringBuilder()
csvBuilder.appendLine("ClientCode,QuestionnaireID,IsDone,Points,Question,Answer")
for ((index, entry) in completedEntries.withIndex()) {
val pageInfo = PdfDocument.PageInfo.Builder(pageWidth, pageHeight, index + 1).create()
var page = pdfDocument.startPage(pageInfo)
var canvas = page.canvas
var yPosition = 40f
// Header PDF
canvas.drawText("Client Code: $actualClientCode", 20f, yPosition, paint)
yPosition += 20f
canvas.drawText("Questionnaire: ${entry.questionnaireId}", 20f, yPosition, paint)
yPosition += 20f
canvas.drawText("Status: ${entry.isDone}", 20f, yPosition, paint)
yPosition += 20f
canvas.drawText("Points: ${entry.sumPoints ?: "N/A"}", 20f, yPosition, paint)
yPosition += 30f
val answers = MyApp.database.answerDao().getAnswersForClientAndQuestionnaire(actualClientCode, entry.questionnaireId)
for (answer in answers) {
val questionKey = answer.questionId.substringAfter("-")
val questionText = LanguageManager.getText("ENGLISH", questionKey)
val rawAnswerText = LanguageManager.getText("ENGLISH", answer.answerValue)
println("Entry " + entry)
println("Question " + questionKey)
println("Answer " + answer.answerValue)
//if (questionKey = "counsultation_decision" and answer.answerValue == "red")
val answerText = rawAnswerText.trim().removePrefix("[").removeSuffix("]")
// PDF
yPosition = drawMultilineText(canvas, "Question: $questionText", 20f, yPosition, paint, pageWidth - 40, isBold = true)
yPosition += 8f
yPosition = drawMultilineText(canvas, "Answer: $answerText", 20f, yPosition, paint, pageWidth - 40)
yPosition += 20f
paint.strokeWidth = 0.5f
canvas.drawLine(20f, yPosition - 30f, pageWidth - 20f, yPosition - 30f, paint)
paint.strokeWidth = 0f
// CSV
val sanitizedQuestion = questionText.replace(",", " ").replace("\n", " ")
val sanitizedAnswer = answerText.replace(",", " ").replace("\n", " ")
csvBuilder.appendLine("${actualClientCode},${entry.questionnaireId},${entry.isDone},${entry.sumPoints ?: ""},\"$sanitizedQuestion\",\"$sanitizedAnswer\"")
if (yPosition > pageHeight - 60) {
pdfDocument.finishPage(page)
val newPageInfo = PdfDocument.PageInfo.Builder(pageWidth, pageHeight, pdfDocument.pages.size + 1).create()
page = pdfDocument.startPage(newPageInfo)
canvas = page.canvas
yPosition = 40f
}
}
pdfDocument.finishPage(page)
}
// ==== CSV LOG AUSGABE ====
Log.d("CSV_OUTPUT", "Generated CSV:\n${csvBuilder.toString()}")
val pdfFileName = "DatabaseOutput_${actualClientCode}.pdf"
val csvFileName = "DatabaseOutput_${actualClientCode}.csv"
val resolver = activity.contentResolver
// Bestehende Dateien löschen
val deleteIfExists: (String) -> Unit = { name ->
val projection = arrayOf(android.provider.MediaStore.MediaColumns._ID)
val selection = "${android.provider.MediaStore.MediaColumns.DISPLAY_NAME} = ?"
val selectionArgs = arrayOf(name)
val query = resolver.query(android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, null)
query?.use { cursor ->
if (cursor.moveToFirst()) {
val idColumn = cursor.getColumnIndexOrThrow(android.provider.MediaStore.MediaColumns._ID)
val id = cursor.getLong(idColumn)
val deleteUri = android.content.ContentUris.withAppendedId(android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI, id)
resolver.delete(deleteUri, null, null)
}
}
}
deleteIfExists(pdfFileName)
deleteIfExists(csvFileName)
// PDF und CSV speichern
try {
val pdfUri = resolver.insert(
android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI,
android.content.ContentValues().apply {
put(android.provider.MediaStore.MediaColumns.DISPLAY_NAME, pdfFileName)
put(android.provider.MediaStore.MediaColumns.MIME_TYPE, "application/pdf")
put(android.provider.MediaStore.MediaColumns.RELATIVE_PATH, "Download/")
}
)
val csvUri = resolver.insert(
android.provider.MediaStore.Downloads.EXTERNAL_CONTENT_URI,
android.content.ContentValues().apply {
put(android.provider.MediaStore.MediaColumns.DISPLAY_NAME, csvFileName)
put(android.provider.MediaStore.MediaColumns.MIME_TYPE, "text/csv")
put(android.provider.MediaStore.MediaColumns.RELATIVE_PATH, "Download/")
}
)
pdfUri?.let {
resolver.openOutputStream(it)?.use { out -> pdfDocument.writeTo(out) }
pdfDocument.close()
}
csvUri?.let {
resolver.openOutputStream(it)?.use { out ->
out.write(csvBuilder.toString().toByteArray(Charsets.UTF_8))
}
}
withContext(Dispatchers.Main) {
Toast.makeText(activity, "PDF und CSV gespeichert in Downloads", Toast.LENGTH_LONG).show()
pdfUri?.let {
val intent = android.content.Intent(android.content.Intent.ACTION_VIEW).apply {
setDataAndType(it, "application/pdf")
addFlags(android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION or android.content.Intent.FLAG_ACTIVITY_NO_HISTORY)
}
try {
activity.startActivity(intent)
} catch (e: android.content.ActivityNotFoundException) {
Toast.makeText(activity, "Kein PDF-Viewer installiert", Toast.LENGTH_SHORT).show()
}
}
}
} catch (e: Exception) {
Log.e("SAVE", "Fehler beim Speichern der Dateien", e)
withContext(Dispatchers.Main) {
Toast.makeText(activity, "Fehler beim Speichern: ${e.message}", Toast.LENGTH_LONG).show()
}
}
}
}
private fun drawMultilineText(
canvas: Canvas,
text: String,
x: Float,
yStart: Float,
paint: Paint,
maxWidth: Int,
isBold: Boolean = false
): Float {
paint.isFakeBoldText = isBold
val words = text.split(" ")
var line = ""
var y = yStart
for (word in words) {
val testLine = if (line.isEmpty()) word else "$line $word"
val lineWidth = paint.measureText(testLine)
if (lineWidth > maxWidth) {
canvas.drawText(line, x, y, paint)
y += paint.textSize * 1.4f
line = word
} else {
line = testLine
}
}
if (line.isNotEmpty()) {
canvas.drawText(line, x, y, paint)
y += paint.textSize * 1.4f
}
paint.isFakeBoldText = false
return y
}
}

View File

@ -0,0 +1,136 @@
package com.dano.test1
import android.content.Context
import android.view.View
import android.text.Html
import android.widget.*
class HandlerRadioQuestion(
private val context: Context,
private val answers: MutableMap<String, Any>,
private val points: MutableList<Int>,
private val languageID: String,
private val goToNextQuestion: () -> Unit,
private val goToPreviousQuestion: () -> Unit,
private val goToQuestionById: (String) -> Unit,
private val showToast: (String) -> Unit
) : QuestionHandler {
private lateinit var layout: View
private lateinit var question: QuestionItem.RadioQuestion
// Bind the question data to the view
override fun bind(layout: View, question: QuestionItem) {
this.layout = layout
this.question = question as QuestionItem.RadioQuestion
val radioGroup = layout.findViewById<RadioGroup>(R.id.RadioGroup)
val questionTextView = layout.findViewById<TextView>(R.id.textView)
val questionTitle = layout.findViewById<TextView>(R.id.question)
// Set question text and optional text key
questionTextView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
questionTitle.text = question.question?.let {
Html.fromHtml(LanguageManager.getText(languageID, it), Html.FROM_HTML_MODE_LEGACY)
} ?: ""
// Clear previous radio buttons if any
radioGroup.removeAllViews()
// Dynamically create radio buttons based on options
question.options.forEach { option ->
val radioButton = RadioButton(context).apply {
text = LanguageManager.getText(languageID, option.key)
tag = option.key
layoutParams = RadioGroup.LayoutParams(
RadioGroup.LayoutParams.MATCH_PARENT,
RadioGroup.LayoutParams.WRAP_CONTENT
).apply {
val scale = context.resources.displayMetrics.density
val margin = (16 * scale + 0.5f).toInt()
setMargins(0, 0, 0, margin)
}
}
radioGroup.addView(radioButton)
}
// Restore previous answer if one was saved
restorePreviousAnswer(radioGroup)
// Handle Next button click
layout.findViewById<Button>(R.id.Qnext).setOnClickListener {
if (validate()) {
saveAnswer()
val selectedId = radioGroup.checkedRadioButtonId
val selectedRadioButton = layout.findViewById<RadioButton>(selectedId)
val selectedKey = selectedRadioButton.tag.toString()
// Check if there is a specific next question ID
val nextId = question.options.find { it.key == selectedKey }?.nextQuestionId
if (!nextId.isNullOrEmpty()) {
goToQuestionById(nextId)
} else {
goToNextQuestion()
}
} else {
showToast(LanguageManager.getText(languageID, "select_one_answer"))
}
}
// Handle Previous button click
layout.findViewById<Button>(R.id.Qprev).setOnClickListener {
goToPreviousQuestion()
}
}
// Restore previously saved answer (if exists)
private fun restorePreviousAnswer(radioGroup: RadioGroup) {
question.question?.let { questionKey ->
val savedAnswer = answers[questionKey] as? String
savedAnswer?.let { saved ->
for (i in 0 until radioGroup.childCount) {
val radioButton = radioGroup.getChildAt(i) as RadioButton
if (radioButton.tag == saved) {
radioButton.isChecked = true
break
}
}
}
}
}
// Validate if any radio button has been selected
override fun validate(): Boolean {
return layout.findViewById<RadioGroup>(R.id.RadioGroup).checkedRadioButtonId != -1
}
// Save selected answer and update points list
override fun saveAnswer() {
val radioGroup = layout.findViewById<RadioGroup>(R.id.RadioGroup)
val selectedId = radioGroup.checkedRadioButtonId
val selectedRadioButton = layout.findViewById<RadioButton>(selectedId)
val answerKey = selectedRadioButton.tag.toString()
question.question?.let { questionKey ->
// Alte Antwort und Punkt ermitteln
val oldAnswerKey = answers[questionKey] as? String
val oldPoint = oldAnswerKey?.let { question.pointsMap?.get(it) } ?: 0
// Alten Punkt entfernen, falls vorhanden
points.remove(oldPoint)
// Neue Antwort speichern
answers[questionKey] = answerKey
// Neuen Punkt ermitteln und hinzufügen
val newPoint = question.pointsMap?.get(answerKey) ?: 0
points.add(newPoint)
}
}
}

View File

@ -0,0 +1,93 @@
package com.dano.test1
import android.content.Context
import android.view.View
import android.widget.*
class HandlerStringSpinner(
private val context: Context,
private val answers: MutableMap<String, Any>,
private val languageID: String,
private val goToNextQuestion: () -> Unit,
private val goToPreviousQuestion: () -> Unit,
private val showToast: (String) -> Unit
) : QuestionHandler {
private lateinit var layout: View
private lateinit var question: QuestionItem.StringSpinnerQuestion
override fun bind(layout: View, question: QuestionItem) {
if (question !is QuestionItem.StringSpinnerQuestion) return
this.layout = layout
this.question = question
val questionTextView = layout.findViewById<TextView>(R.id.question)
val textView = layout.findViewById<TextView>(R.id.textView)
val spinner = layout.findViewById<Spinner>(R.id.string_spinner)
questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
textView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
val options = buildOptionsList()
val savedSelection = question.question?.let { answers[it] as? String }
setupSpinner(spinner, options, savedSelection)
layout.findViewById<Button>(R.id.Qnext).setOnClickListener {
if (validate()) {
saveAnswer()
goToNextQuestion()
}
}
layout.findViewById<Button>(R.id.Qprev).setOnClickListener {
goToPreviousQuestion()
}
}
override fun validate(): Boolean {
val spinner = layout.findViewById<Spinner>(R.id.string_spinner)
val selected = spinner.selectedItem as? String
val prompt = LanguageManager.getText(languageID, "choose_answer")
return if (selected.isNullOrEmpty() || selected == prompt) {
showToast(LanguageManager.getText(languageID, "select_one_answer"))
false
} else {
true
}
}
override fun saveAnswer() {
val spinner = layout.findViewById<Spinner>(R.id.string_spinner)
val selected = spinner.selectedItem as? String ?: return
question.question?.let { key ->
answers[key] = selected
}
}
private fun buildOptionsList(): List<String> {
return if (question.id == "q11") {
Countries.getAllCountries(languageID)
} else {
val prompt = LanguageManager.getText(languageID, "choose_answer")
listOf(prompt) + question.options
}
}
private fun <T> setupSpinner(spinner: Spinner, items: List<T>, selectedItem: T?) {
val adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, items)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = adapter
selectedItem?.let {
val index = items.indexOf(it)
if (index >= 0) {
spinner.setSelection(index)
}
}
}
}

View File

@ -0,0 +1,97 @@
package com.dano.test1
import android.content.Context
import android.view.View
import android.widget.*
class HandlerValueSpinner(
private val context: Context,
private val answers: MutableMap<String, Any>,
private val languageID: String,
private val goToNextQuestion: () -> Unit,
private val goToPreviousQuestion: () -> Unit,
private val goToQuestionById: (String) -> Unit,
private val showToast: (String) -> Unit
) : QuestionHandler {
private lateinit var layout: View
private lateinit var question: QuestionItem.ValueSpinnerQuestion
override fun bind(layout: View, question: QuestionItem) {
if (question !is QuestionItem.ValueSpinnerQuestion) return
this.layout = layout
this.question = question
val questionTextView = layout.findViewById<TextView>(R.id.question)
val textView = layout.findViewById<TextView>(R.id.textView)
val spinner = layout.findViewById<Spinner>(R.id.value_spinner)
questionTextView.text = question.question?.let { LanguageManager.getText(languageID, it) } ?: ""
textView.text = question.textKey?.let { LanguageManager.getText(languageID, it) } ?: ""
val prompt = LanguageManager.getText(languageID, "choose_answer")
val spinnerItems: List<String> = listOf(prompt) + if (question.range != null) {
(question.range.min..question.range.max).map { it.toString() }
} else {
question.options.map { it.value.toString() }
}
val savedValue = question.question?.let { answers[it] as? String }
setupSpinner(spinner, spinnerItems, savedValue)
layout.findViewById<Button>(R.id.Qnext).setOnClickListener {
if (validate()) {
saveAnswer()
val selectedValue = (spinner.selectedItem as? String)?.toIntOrNull()
if (!question.options.isNullOrEmpty() && selectedValue != null) {
val selectedOption = question.options.find { it.value == selectedValue }
val nextId = selectedOption?.nextQuestionId
if (!nextId.isNullOrEmpty()) {
goToQuestionById(nextId)
return@setOnClickListener
}
}
goToNextQuestion()
}
}
layout.findViewById<Button>(R.id.Qprev).setOnClickListener {
goToPreviousQuestion()
}
}
override fun validate(): Boolean {
val spinner = layout.findViewById<Spinner>(R.id.value_spinner)
val selected = spinner.selectedItem as? String
val prompt = LanguageManager.getText(languageID, "choose_answer")
return if (selected == null || selected == prompt) {
showToast(LanguageManager.getText(languageID, "select_one_answer"))
false
} else {
true
}
}
override fun saveAnswer() {
val spinner = layout.findViewById<Spinner>(R.id.value_spinner)
val selected = spinner.selectedItem as? String ?: return
question.question?.let { key ->
answers[key] = selected
}
}
private fun <T> setupSpinner(spinner: Spinner, items: List<T>, selectedItem: T?) {
val adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, items)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = adapter
selectedItem?.let {
val index = items.indexOf(it)
if (index >= 0) spinner.setSelection(index)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,64 @@
package com.dano.test1
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import android.widget.*
object LocalizationHelper {
/**
* Extension function to set localized hint on EditText.
*/
fun EditText.setLocalizedHint(languageID: String, key: String) {
this.hint = LanguageManager.getText(languageID, key)
}
/**
* Extension function to set localized text on Button.
*/
fun Button.setLocalizedText(languageID: String, key: String) {
this.text = LanguageManager.getText(languageID, key)
}
/**
* Recursively localizes the view hierarchy by updating text or hint
* of views according to their tag as localization key.
* @param root The root view to start localization.
* @param languageID The language identifier for localization.
*/
fun localizeViewTree(root: View, languageID: String) {
val key = root.tag as? String
when (root) {
is EditText -> {
// Set hint for EditText if tag key is present
key?.let { root.setLocalizedHint(languageID, it) }
}
is Button -> {
// Set text for Button if tag key is present
key?.let { root.setLocalizedText(languageID, it) }
}
is TextView -> {
// Set text for TextView if tag key is present
// Note: Button and others inherit from TextView, but handled separately above
if (root !is Button) key?.let { root.text = LanguageManager.getText(languageID, it) }
}
is RadioButton -> {
// Set text for RadioButton if tag key is present
key?.let { root.text = LanguageManager.getText(languageID, it) }
}
is CheckBox -> {
// Set text for CheckBox if tag key is present
key?.let { root.text = LanguageManager.getText(languageID, it) }
}
is ViewGroup -> {
// Recursively localize all child views
for (i in 0 until root.childCount) {
localizeViewTree(root.getChildAt(i), languageID)
}
}
}
}
}

View File

@ -0,0 +1,52 @@
package com.dano.test1
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
private lateinit var openingScreenHandler: HandlerOpeningScreen
var isInQuestionnaire: Boolean = false
var isFirstQuestionnairePage: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Initialize the opening screen handler and show the opening screen
openingScreenHandler = HandlerOpeningScreen(this)
openingScreenHandler.init()
}
/**
* Starts the given questionnaire and attaches it to this activity.
* @param questionnaire The questionnaire instance to start.
* @param languageID The language identifier for localization.
*/
fun startQuestionnaire(questionnaire: QuestionnaireBase<*>, languageID: String) {
isInQuestionnaire = true
isFirstQuestionnairePage = true
questionnaire.attach(this, languageID)
questionnaire.startQuestionnaire()
}
/**
* Handle the back button press.
* If the openingScreenHandler can handle it, do not call super.
* Otherwise, call the default back press behavior.
*/
override fun onBackPressed() {
if (!openingScreenHandler.onBackPressed()) {
super.onBackPressed()
}
}
/**
* Finish the questionnaire and return to the opening screen.
*/
fun finishQuestionnaire() {
// For example, switch back to the opening screen:
isInQuestionnaire = false
isFirstQuestionnairePage = false
openingScreenHandler.init()
}
}

View File

@ -0,0 +1,24 @@
package com.dano.test1
data class Month(val name: String) {
override fun toString(): String = name
}
object Months {
fun getAllMonths(languageID: String): List<Any> {
return listOf(
Month(LanguageManager.getText(languageID, "january")),
Month(LanguageManager.getText(languageID, "february")),
Month(LanguageManager.getText(languageID, "march")),
Month(LanguageManager.getText(languageID, "april")),
Month(LanguageManager.getText(languageID, "may")),
Month(LanguageManager.getText(languageID, "june")),
Month(LanguageManager.getText(languageID, "july")),
Month(LanguageManager.getText(languageID, "august")),
Month(LanguageManager.getText(languageID, "september")),
Month(LanguageManager.getText(languageID, "october")),
Month(LanguageManager.getText(languageID, "november")),
Month(LanguageManager.getText(languageID, "december"))
)
}
}

View File

@ -0,0 +1,24 @@
package com.dano.test1
import android.app.Application
import androidx.room.Room
import com.dano.test1.data.AppDatabase
class MyApp : Application() {
companion object {
lateinit var database: AppDatabase
private set
}
override fun onCreate() {
super.onCreate()
database = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java,
"questionnaire_database"
)
.fallbackToDestructiveMigration()
.build()
}
}

View File

@ -0,0 +1,8 @@
package com.dano.test1
import android.view.View
interface QuestionHandler {
fun bind(layout: View, question: QuestionItem)
fun validate(): Boolean
fun saveAnswer()
}

View File

@ -0,0 +1,249 @@
package com.dano.test1
import android.app.Activity
import android.util.Log
import android.view.View
import android.widget.*
import com.dano.test1.data.*
import com.google.gson.Gson
import com.google.gson.JsonParser
import kotlinx.coroutines.*
import java.io.File
import java.util.Calendar
// Global constants and values
const val MAX_VALUE_AGE = 122
val MAX_VALUE_YEAR = Calendar.getInstance().get(Calendar.YEAR)
object GlobalValues {
var INTEGRATION_INDEX: Int = 0
var LAST_CLIENT_CODE: String? = null
var LOADED_CLIENT_CODE: String? = null
}
// Data classes for questionnaire metadata
data class QuestionnaireMeta(val id: String)
data class QuestionnaireData(val meta: QuestionnaireMeta, val questions: List<QuestionItem>)
// Abstract base class for questionnaire
abstract class QuestionnaireBase<T> {
abstract fun startQuestionnaire()
abstract fun showCurrentQuestion()
fun attach(activity: Activity, language: String) {
this.context = activity
this.languageID = language
}
// Protected properties
protected lateinit var questionnaireMeta: QuestionnaireMeta
protected var currentIndex = 0
protected lateinit var questions: List<QuestionItem>
protected val answers = mutableMapOf<String, Any>()
protected val points = mutableListOf<Int>()
protected var languageID: String = "GERMAN"
protected lateinit var context: Activity
private val navigationHistory = mutableListOf<Int>()
// Utility functions
protected fun showToast(message: String) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
protected fun validateEditTexts(vararg fields: EditText): Boolean {
return fields.all { it.text.toString().trim().isNotEmpty() }
}
// Replace placeholders in file content and save updated file
protected fun updateFileContent(fileName: String, placeholders: Map<String, Any?>) {
val file = File(context.filesDir, fileName)
if (file.exists()) {
var updatedContent = file.readText()
placeholders.forEach { (key, value) ->
updatedContent = updatedContent.replace("{$key}", value.toString())
}
file.writeText(updatedContent)
}
}
// Setup spinner UI element
protected fun setupSpinner(spinner: Spinner, spinnerValues: List<Any>, selectedValue: Any?) {
val adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, spinnerValues).apply {
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}
spinner.adapter = adapter
selectedValue?.let { value ->
val index = spinnerValues.indexOfFirst {
it.toString().trim().equals(value.toString().trim(), ignoreCase = true)
}
if (index >= 0) spinner.setSelection(index)
}
}
// Navigate to specific layout
protected fun navigateTo(layoutResId: Int, setup: (View) -> Unit) {
context.setContentView(layoutResId)
val rootView = context.findViewById<View>(android.R.id.content)
setup(rootView)
}
protected fun setupPrevButton(buttonId: Int, action: () -> Unit) {
context.findViewById<Button>(buttonId)?.setOnClickListener { action() }
}
protected fun showEmptyScreen() {
navigateTo(getLayoutResId("empty")) {
setupPrevButton(R.id.Qprev) { goToPreviousQuestion() }
}
}
// Resolve layout resource ID from layout name
protected fun getLayoutResId(layoutName: String?): Int {
return layoutName?.let {
context.resources.getIdentifier(it, "layout", context.packageName)
} ?: 0
}
// Calculate sum of all points
protected fun calculateIntegrationIndex(): Int = points.sum()
// Navigation logic
protected fun goToPreviousQuestion() {
if (navigationHistory.isNotEmpty()) {
currentIndex = navigationHistory.removeAt(navigationHistory.size - 1)
showCurrentQuestion()
} else {
(context as? MainActivity)?.finishQuestionnaire()
}
}
protected fun goToNextQuestion() {
if (currentIndex < questions.size - 1) {
navigationHistory.add(currentIndex)
currentIndex++
showCurrentQuestion()
}
}
fun goToQuestionById(questionId: String) {
val index = questions.indexOfFirst { it.id == questionId }
if (index != -1) {
navigationHistory.add(currentIndex)
currentIndex = index
showCurrentQuestion()
}
}
// Load questionnaire data from JSON
protected fun loadQuestionnaireFromJson(filename: String): Pair<QuestionnaireMeta, List<QuestionItem>> {
val jsonString = context.assets.open(filename).bufferedReader().use { it.readText() }
val gson = Gson()
val jsonObject = JsonParser.parseString(jsonString).asJsonObject
val meta = gson.fromJson(jsonObject.getAsJsonObject("meta"), QuestionnaireMeta::class.java)
val jsonArray = jsonObject.getAsJsonArray("questions")
val questionTypeMap = mapOf(
"client_coach_code_question" to QuestionItem.ClientCoachCodeQuestion::class.java,
"radio_question" to QuestionItem.RadioQuestion::class.java,
"date_spinner" to QuestionItem.DateSpinnerQuestion::class.java,
"value_spinner" to QuestionItem.ValueSpinnerQuestion::class.java,
"glass_scale_question" to QuestionItem.GlassScaleQuestion::class.java,
"last_page" to QuestionItem.LastPage::class.java,
"client_not_signed" to QuestionItem.ClientNotSigned::class.java,
"string_spinner" to QuestionItem.StringSpinnerQuestion::class.java,
"multi_check_box_question" to QuestionItem.MultiCheckboxQuestion::class.java
)
val questionsList = jsonArray.mapNotNull { element ->
val obj = element.asJsonObject
val layoutType = obj.get("layout").asString
val clazz = questionTypeMap[layoutType] ?: return@mapNotNull null
gson.fromJson(obj, clazz)
}
return Pair(meta, questionsList)
}
// Factory to create handler for each question type
protected open fun createHandlerForQuestion(question: QuestionItem): QuestionHandler? {
return when (question) {
is QuestionItem.RadioQuestion -> HandlerRadioQuestion(context, answers, points, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::goToQuestionById, ::showToast)
is QuestionItem.ClientCoachCodeQuestion -> HandlerClientCoachCode(answers, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast)
is QuestionItem.DateSpinnerQuestion -> HandlerDateSpinner(context, answers, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast)
is QuestionItem.ValueSpinnerQuestion -> HandlerValueSpinner(context, answers, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::goToQuestionById, ::showToast)
is QuestionItem.GlassScaleQuestion -> HandlerGlassScaleQuestion(context, answers, points, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast)
is QuestionItem.ClientNotSigned -> HandlerClientNotSigned(answers, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast)
is QuestionItem.StringSpinnerQuestion -> HandlerStringSpinner(context, answers, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast)
is QuestionItem.MultiCheckboxQuestion -> HandlerMultiCheckboxQuestion(context, answers, points, languageID, ::goToNextQuestion, ::goToPreviousQuestion, ::showToast)
is QuestionItem.LastPage -> HandlerLastPage(
answers, languageID, ::goToNextQuestion, ::goToPreviousQuestion
) { CoroutineScope(Dispatchers.IO).launch { saveAnswersToDatabase(answers, questionnaireMeta.id) } }
else -> null
}
}
// Persist answers into database
suspend fun saveAnswersToDatabase(answers: Map<String, Any>, questionnaireId: String) {
Log.d("AnswersMap", answers.toString())
val db = MyApp.database
val clientCode = answers["client_code"] as? String ?: return
saveClientAndQuestionnaire(db, clientCode, questionnaireId)
saveQuestions(db, answers, questionnaireId)
saveAnswers(db, answers, questionnaireId, clientCode)
markQuestionnaireCompleted(db, questionnaireId, clientCode)
}
private suspend fun saveClientAndQuestionnaire(db: AppDatabase, clientCode: String, questionnaireId: String) {
db.clientDao().insertClient(Client(clientCode))
db.questionnaireDao().insertQuestionnaire(Questionnaire(id = questionnaireId))
}
private suspend fun saveQuestions(db: AppDatabase, answers: Map<String, Any>, questionnaireId: String) {
val questionEntities = answers.keys
.filterNot { it.startsWith("client") }
.map { key ->
val questionId = "$questionnaireId-$key"
Question(
questionId = questionId,
questionnaireId = questionnaireId,
question = LanguageManager.getText(languageID, key)
)
}
db.questionDao().insertQuestions(questionEntities)
}
private suspend fun saveAnswers(db: AppDatabase, answers: Map<String, Any>, questionnaireId: String, clientCode: String) {
val answerEntities = answers.entries
.filterNot { it.key.startsWith("client") }
.map { (key, value) ->
val questionId = "$questionnaireId-$key"
Answer(
clientCode = clientCode,
questionId = questionId,
answerValue = value.toString()
)
}
db.answerDao().insertAnswers(answerEntities)
}
private suspend fun markQuestionnaireCompleted(db: AppDatabase, questionnaireId: String, clientCode: String) {
val completedEntry = CompletedQuestionnaire(
clientCode = clientCode,
questionnaireId = questionnaireId,
isDone = true,
sumPoints = points.sum()
)
db.completedQuestionnaireDao().insert(completedEntry)
}
fun endQuestionnaire() {
(context as? MainActivity)?.finishQuestionnaire()
}
}

View File

@ -0,0 +1,59 @@
package com.dano.test1
import android.widget.Button
open class QuestionnaireGeneric(private val questionnaireFileName: String) : QuestionnaireBase<Unit>() {
/**
* Starts the questionnaire by loading questions and metadata from JSON,
* then shows the first question.
*/
override fun startQuestionnaire() {
val (meta, questionsList) = loadQuestionnaireFromJson(questionnaireFileName)
questionnaireMeta = meta
questions = questionsList
currentIndex = 0
showCurrentQuestion()
}
/**
* Displays the current question based on the current index.
* Loads the appropriate layout, applies localization, sets navigation buttons,
* and delegates question binding to the appropriate handler.
*/
override fun showCurrentQuestion() {
val question = questions[currentIndex]
// Get the layout resource ID based on the question layout name or fallback to default
val layoutResId = getLayoutResId(question.layout ?: "default_layout")
if (layoutResId == 0) {
// No valid layout found, show empty screen instead
showEmptyScreen()
return
}
// Navigate to the question layout and initialize it
navigateTo(layoutResId) { layout ->
// Localize all views in the layout tree according to current language
LocalizationHelper.localizeViewTree(layout, languageID)
// Setup previous button navigation, if present
layout.findViewById<Button>(R.id.Qprev)?.setOnClickListener {
goToPreviousQuestion()
}
// Create the appropriate handler for the question type
val handler = createHandlerForQuestion(question)
if (handler != null) {
// Bind the question data to the UI
handler.bind(layout, question)
} else {
// No handler found for this question type; show empty screen
showEmptyScreen()
}
}
}
}

View File

@ -0,0 +1,121 @@
package com.dano.test1
data class Option(
val key: String, // Must always be set
val nextQuestionId: String? = null
)
data class ValueOption(
val value: Int,
val nextQuestionId: String? = null
)
data class Range(
val min: Int,
val max: Int
)
data class Constraints(
val notBefore: String? = null,
val notAfter: String? = null
)
sealed class QuestionItem {
abstract val layout: String?
abstract val id: String
data class ClientCoachCodeQuestion(
override val layout: String?,
override val id: String,
val question: String,
val hint1: String?,
val hint2: String?
) : QuestionItem()
data class RadioQuestion(
override val layout: String?,
override val id: String,
val textKey: String?,
val question: String?,
val options: List<Option>,
val pointsMap: Map<String, Int>? = null
) : QuestionItem()
data class GlassScaleQuestion(
override val layout: String?,
override val id: String,
val textKey: String?,
val question: String,
val symptomTextKeys: List<String>,
val pointsMap: Map<String, Int>,
val symptoms: List<String>
) : QuestionItem()
data class ClientNotSigned(
override val layout: String?,
override val id: String,
val textKey1: String?,
val textKey2: String?,
val question: String,
val hint: String?
) : QuestionItem()
data class MultiCheckboxQuestion(
override val id: String,
override val layout: String,
val textKey: String?,
val question: String,
val options: List<Option>,
val nextQuestionId: String?,
val pointsMap: Map<String, Int>?,
val minSelection: Int = 1
) : QuestionItem()
data class DateSpinnerQuestion(
override val layout: String?,
override val id: String,
val textKey: String?,
val question: String,
val nextQuestionId: String? = null,
val storeAsGlobalKey: String? = null,
val constraints: Constraints? = null
) : QuestionItem()
data class ValueSpinnerQuestion(
override val layout: String?,
override val id: String,
val textKey: String?,
val question: String,
val options: List<ValueOption> = emptyList(),
val range: Range? = null
) : QuestionItem()
data class StringSpinnerQuestion(
override val layout: String?,
override val id: String,
val textKey: String?,
val question: String,
val options: List<String>,
val nextQuestionId: String? = null
) : QuestionItem()
data class LastPage(
override val layout: String?,
override val id: String,
val textKey: String,
val question: String
) : QuestionItem()
data class QuestionnaireEntry(
val file: String,
val condition: Condition? = null
)
data class Condition(
val questionnaire: String,
val questionId: String,
val operator: String,
val value: String
)
}

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
tools:layout_editor_absoluteX="11dp"
tools:layout_editor_absoluteY="107dp">
<Button
android:id="@+id/Qprev"
android:tag="previous"
android:layout_width="130dp"
android:layout_height="42dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.056"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<Button
android:id="@+id/Qnext"
android:tag="next"
android:layout_width="130dp"
android:layout_height="42dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.943"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<EditText
android:id="@+id/client_code"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:background="@android:drawable/edit_text"
android:ems="10"
android:inputType="text"
android:tag="client_code"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.495"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.368" />
<EditText
android:id="@+id/coach_code"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:background="@android:drawable/edit_text"
android:ems="10"
android:inputType="text"
android:tag="coach_code"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.495"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.499" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.104" />
<TextView
android:id="@+id/question"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textStyle="bold"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.188" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
tools:layout_editor_absoluteX="11dp"
tools:layout_editor_absoluteY="107dp">
<Button
android:id="@+id/Qprev"
android:tag="previous"
android:layout_width="130dp"
android:layout_height="42dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.056"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<Button
android:id="@+id/Qnext"
android:tag="next"
android:layout_width="130dp"
android:layout_height="42dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.943"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<EditText
android:id="@+id/coach_code"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:background="@android:drawable/edit_text"
android:ems="10"
android:inputType="text"
android:tag="coach_code"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.495"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.499" />
<TextView
android:id="@+id/textView2"
android:layout_width="329dp"
android:layout_height="55dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.186" />
<TextView
android:id="@+id/question"
android:layout_width="329dp"
android:layout_height="55dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.267" />
<TextView
android:id="@+id/textView1"
android:layout_width="329dp"
android:layout_height="55dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.105" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.104" />
<TextView
android:id="@+id/question"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textStyle="bold"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.188" />
<Spinner
android:id="@+id/spinner_value_month"
android:layout_width="142dp"
android:layout_height="35dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.453"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.341" />
<TextView
android:id="@+id/date_spinner_year"
android:layout_width="110dp"
android:layout_height="22dp"
android:tag="year"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.877"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.282" />
<TextView
android:id="@+id/date_spinner_day"
android:layout_width="90dp"
android:layout_height="22dp"
android:tag="day"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.099"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.282" />
<Spinner
android:id="@+id/spinner_value_day"
android:layout_width="90dp"
android:layout_height="35dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.099"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.341" />
<TextView
android:id="@+id/date_spinner_month"
android:layout_width="142dp"
android:layout_height="22dp"
android:tag="month"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.453"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.282" />
<Spinner
android:id="@+id/spinner_value_year"
android:layout_width="110dp"
android:layout_height="35dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.877"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.341" />
<Button
android:id="@+id/Qnext"
android:layout_width="130dp"
android:layout_height="42dp"
android:tag="next"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.943"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<Button
android:id="@+id/Qprev"
android:layout_width="130dp"
android:layout_height="42dp"
android:tag="previous"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.056"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,71 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="466dp"
android:fillViewport="true"
app:layout_constraintTop_toBottomOf="@+id/question"
app:layout_constraintBottom_toTopOf="@+id/Qprev"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<TableLayout
android:id="@+id/glass_table"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:stretchColumns="1,2,3,4,5"
android:textAlignment="center"
android:textStyle="bold" />
</ScrollView>
<Button
android:id="@+id/Qprev"
android:layout_width="130dp"
android:layout_height="wrap_content"
android:tag="previous"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:layout_margin="16dp" />
<Button
android:id="@+id/Qnext"
android:layout_width="130dp"
android:layout_height="wrap_content"
android:tag="next"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_margin="16dp" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.104" />
<TextView
android:id="@+id/question"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textStyle="bold"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.188" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/question"
android:layout_width="389dp"
android:layout_height="115dp"
android:tag="finish_data_entry"
android:textAlignment="viewStart"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.376" />
<Button
android:id="@+id/Qprev"
android:layout_width="130dp"
android:layout_height="42dp"
android:tag="previous"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.056"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<Button
android:id="@+id/Qfinish"
android:layout_width="231dp"
android:layout_height="42dp"
android:tag="finish"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.944"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<TextView
android:id="@+id/textView"
android:layout_width="389dp"
android:layout_height="115dp"
android:tag="finish_data_entry"
android:textAlignment="viewStart"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.125" />
<!-- ProgressBar for loading animation -->
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
tools:layout_editor_absoluteX="11dp"
tools:layout_editor_absoluteY="107dp">
<Button
android:id="@+id/Qprev"
android:tag="previous"
android:layout_width="130dp"
android:layout_height="42dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.056"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<Button
android:id="@+id/Qnext"
android:tag="next"
android:layout_width="130dp"
android:layout_height="42dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.943"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.104" />
<TextView
android:id="@+id/question"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textStyle="bold"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.188" />
<ScrollView
android:id="@+id/scrollView"
android:layout_width="360dp"
android:layout_height="417dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.489"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.658">
<LinearLayout
android:id="@+id/CheckboxContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp" />
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/editText"
android:layout_width="216dp"
android:layout_height="53dp"
android:background="@android:drawable/edit_text"
android:ems="10"
android:inputType="text"
android:tag="client_code"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.897"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.091" />
<Spinner
android:id="@+id/string_spinner1"
android:layout_width="143dp"
android:layout_height="43dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.07"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.091" />
<!-- Container für dynamische Buttons -->
<LinearLayout
android:id="@+id/buttonContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="28dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView" />
<Button
android:id="@+id/loadButton"
android:layout_width="166dp"
android:layout_height="52dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.816"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.167" />
<TextView
android:id="@+id/textView"
android:layout_width="348dp"
android:layout_height="162dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.492"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.307" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
tools:layout_editor_absoluteX="11dp"
tools:layout_editor_absoluteY="107dp">
<Button
android:id="@+id/Qprev"
android:tag="previous"
android:layout_width="130dp"
android:layout_height="42dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.056"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<Button
android:id="@+id/Qnext"
android:tag="next"
android:layout_width="130dp"
android:layout_height="42dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.943"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<RadioGroup
android:id="@+id/RadioGroup"
android:layout_width="323dp"
android:layout_height="419dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.675" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.104" />
<TextView
android:id="@+id/question"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textStyle="bold"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.188" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Spinner
android:id="@+id/string_spinner"
android:layout_width="250dp"
android:layout_height="35dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.38" />
<Button
android:id="@+id/Qnext"
android:layout_width="130dp"
android:layout_height="42dp"
android:tag="next"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.943"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<Button
android:id="@+id/Qprev"
android:layout_width="130dp"
android:layout_height="42dp"
android:tag="previous"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.056"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.104" />
<TextView
android:id="@+id/question"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textStyle="bold"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.188" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Spinner
android:id="@+id/value_spinner"
android:layout_width="250dp"
android:layout_height="35dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.38" />
<Button
android:id="@+id/Qnext"
android:layout_width="130dp"
android:layout_height="42dp"
android:tag="next"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.943"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<Button
android:id="@+id/Qprev"
android:layout_width="130dp"
android:layout_height="42dp"
android:tag="previous"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.056"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.104" />
<TextView
android:id="@+id/question"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textStyle="bold"
android:layout_marginStart="25dp"
android:layout_marginEnd="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.188" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -0,0 +1,7 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.Test1" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your dark theme here. -->
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
</style>
</resources>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">Test1</string>
</resources>

View File

@ -0,0 +1,9 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.Test1" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
</style>
<style name="Theme.Test1" parent="Base.Theme.Test1" />
</resources>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample backup rules file; uncomment and customize as necessary.
See https://developer.android.com/guide/topics/data/autobackup
for details.
Note: This file is ignored for devices older than API 31
See https://developer.android.com/about/versions/12/backup-restore
-->
<full-backup-content>
<!--
<include domain="sharedpref" path="."/>
<exclude domain="sharedpref" path="device.xml"/>
-->
</full-backup-content>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?><!--
Sample data extraction rules file; uncomment and customize as necessary.
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
for details.
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->
</cloud-backup>
<!--
<device-transfer>
<include .../>
<exclude .../>
</device-transfer>
-->
</data-extraction-rules>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="." />
</paths>

View File

@ -0,0 +1,17 @@
package com.dano.test1
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

6
build.gradle.kts Normal file
View File

@ -0,0 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
}

23
gradle.properties Normal file
View File

@ -0,0 +1,23 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. For more details, visit
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true

26
gradle/libs.versions.toml Normal file
View File

@ -0,0 +1,26 @@
[versions]
agp = "8.9.1"
kotlin = "2.0.21"
coreKtx = "1.16.0"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
appcompat = "1.7.0"
material = "1.12.0"
activity = "1.8.0"
constraintlayout = "2.2.1"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Mon Apr 28 14:49:40 CEST 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

185
gradlew vendored Normal file
View File

@ -0,0 +1,185 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"

89
gradlew.bat vendored Normal file
View File

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

23
settings.gradle.kts Normal file
View File

@ -0,0 +1,23 @@
pluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "Test1"
include(":app")