worked on header, added language manager and output (only english)
This commit is contained in:
@ -27,6 +27,24 @@ android {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apache POI bringt viele META-INF-Dateien mit – hier aus dem APK ausschließen
|
||||||
|
packaging {
|
||||||
|
resources {
|
||||||
|
excludes += listOf(
|
||||||
|
"META-INF/DEPENDENCIES",
|
||||||
|
"META-INF/LICENSE",
|
||||||
|
"META-INF/LICENSE.txt",
|
||||||
|
"META-INF/NOTICE",
|
||||||
|
"META-INF/NOTICE.txt",
|
||||||
|
"META-INF/NOTICE.md",
|
||||||
|
"META-INF/LICENSE.md",
|
||||||
|
"META-INF/*.kotlin_module",
|
||||||
|
"META-INF/versions/**"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
@ -44,22 +62,31 @@ dependencies {
|
|||||||
implementation(libs.material)
|
implementation(libs.material)
|
||||||
implementation(libs.androidx.activity)
|
implementation(libs.androidx.activity)
|
||||||
implementation(libs.androidx.constraintlayout)
|
implementation(libs.androidx.constraintlayout)
|
||||||
|
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
|
|
||||||
implementation("com.google.code.gson:gson:2.10.1")
|
implementation("com.google.code.gson:gson:2.10.1")
|
||||||
|
|
||||||
implementation("androidx.room:room-runtime:$room_version")
|
implementation("androidx.room:room-runtime:$room_version")
|
||||||
kapt("androidx.room:room-compiler:$room_version")
|
kapt("androidx.room:room-compiler:$room_version")
|
||||||
implementation("androidx.room:room-ktx:$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")
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
|
||||||
|
|
||||||
// SQLCipher
|
// SQLCipher
|
||||||
implementation ("net.zetetic:android-database-sqlcipher:4.5.3@aar")
|
implementation("net.zetetic:android-database-sqlcipher:4.5.3@aar")
|
||||||
implementation ("androidx.sqlite:sqlite:2.1.0")
|
implementation("androidx.sqlite:sqlite:2.1.0")
|
||||||
implementation ("androidx.sqlite:sqlite-framework:2.1.0")
|
implementation("androidx.sqlite:sqlite-framework:2.1.0")
|
||||||
|
|
||||||
// Server Upload
|
// Server Upload
|
||||||
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||||
|
|
||||||
|
// ---- Excel-Export (Apache POI) ----
|
||||||
|
// Leichtgewichtige OOXML-Implementierung + Kernbibliothek
|
||||||
|
implementation("org.apache.poi:poi:5.2.5")
|
||||||
|
implementation("org.apache.poi:poi-ooxml:5.2.5")
|
||||||
|
implementation("org.apache.poi:poi-ooxml-lite:5.2.5") //für kleinere Schemas
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.dano.test1
|
package com.dano.test1
|
||||||
|
|
||||||
|
import android.os.Environment
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.*
|
import android.widget.*
|
||||||
@ -8,13 +9,19 @@ import com.dano.test1.data.Question
|
|||||||
import com.dano.test1.data.Questionnaire
|
import com.dano.test1.data.Questionnaire
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
|
import java.io.File
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
import org.apache.poi.ss.usermodel.Row
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook
|
||||||
|
|
||||||
class DatabaseButtonHandler(
|
class DatabaseButtonHandler(
|
||||||
private val activity: MainActivity,
|
private val activity: MainActivity,
|
||||||
private val databaseButton: Button,
|
private val databaseButton: Button,
|
||||||
private val onClose: () -> Unit
|
private val onClose: () -> Unit,
|
||||||
|
// Liefert die aktuelle Sprache; Default: "GERMAN"
|
||||||
|
private val languageIDProvider: () -> String = { "GERMAN" }
|
||||||
) {
|
) {
|
||||||
private val uiScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
private val uiScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
|
||||||
private val tag = "DatabaseButtonHandler"
|
private val tag = "DatabaseButtonHandler"
|
||||||
@ -37,8 +44,10 @@ class DatabaseButtonHandler(
|
|||||||
val progress: ProgressBar = requireView(R.id.progressBar, "progressBar")
|
val progress: ProgressBar = requireView(R.id.progressBar, "progressBar")
|
||||||
val emptyView: TextView = requireView(R.id.emptyView, "emptyView")
|
val emptyView: TextView = requireView(R.id.emptyView, "emptyView")
|
||||||
val backButton: Button = requireView(R.id.backButton, "backButton")
|
val backButton: Button = requireView(R.id.backButton, "backButton")
|
||||||
|
val btnDownloadHeader: Button = requireView(R.id.btnDownloadHeader, "btnDownloadHeader")
|
||||||
|
|
||||||
backButton.setOnClickListener { onClose() }
|
backButton.setOnClickListener { onClose() }
|
||||||
|
btnDownloadHeader.setOnClickListener { onDownloadHeadersClicked(progress) }
|
||||||
|
|
||||||
progress.visibility = View.VISIBLE
|
progress.visibility = View.VISIBLE
|
||||||
emptyView.visibility = View.GONE
|
emptyView.visibility = View.GONE
|
||||||
@ -68,6 +77,108 @@ class DatabaseButtonHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------
|
||||||
|
// Export: Header aller Clients als Excel
|
||||||
|
// ---------------------------
|
||||||
|
private fun onDownloadHeadersClicked(progress: ProgressBar) {
|
||||||
|
uiScope.launch {
|
||||||
|
try {
|
||||||
|
progress.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
val exportResult = exportHeadersForAllClients()
|
||||||
|
|
||||||
|
progress.visibility = View.GONE
|
||||||
|
|
||||||
|
if (exportResult != null) {
|
||||||
|
Toast.makeText(
|
||||||
|
activity,
|
||||||
|
"Export erfolgreich: ${exportResult.absolutePath}",
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
} else {
|
||||||
|
Toast.makeText(activity, "Export fehlgeschlagen.", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
progress.visibility = View.GONE
|
||||||
|
Log.e(tag, "Download Header Fehler: ${e.message}", e)
|
||||||
|
Toast.makeText(activity, "Fehler: ${e.message}", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erzeugt eine Excel-Datei (ClientHeaders.xlsx) mit:
|
||||||
|
* - Spalten = IDs aus header_order.json (in genau der Reihenfolge)
|
||||||
|
* - Zeilen = je ein Client; Werte:
|
||||||
|
* - "client_code" => code
|
||||||
|
* - Fragebogen-IDs (questionnaire_*) => "Done" / "Not Done"
|
||||||
|
* - alle anderen IDs => Antwort oder "None"
|
||||||
|
*
|
||||||
|
* Rückgabewert: Ausgabedatei oder null bei Fehler.
|
||||||
|
*/
|
||||||
|
private suspend fun exportHeadersForAllClients(): File? {
|
||||||
|
val orderedIds = loadOrderedIds()
|
||||||
|
if (orderedIds.isEmpty()) return null
|
||||||
|
|
||||||
|
val clients = MyApp.database.clientDao().getAllClients()
|
||||||
|
val questionnaires = MyApp.database.questionnaireDao().getAll()
|
||||||
|
val questionnaireIdSet = questionnaires.map { it.id }.toSet()
|
||||||
|
|
||||||
|
val wb = XSSFWorkbook()
|
||||||
|
val sheet = wb.createSheet("Headers")
|
||||||
|
|
||||||
|
sheet.setColumnWidth(0, 8 * 256)
|
||||||
|
for (i in 1..orderedIds.size) sheet.setColumnWidth(i, 36 * 256)
|
||||||
|
|
||||||
|
var col = 0
|
||||||
|
val header = sheet.createRow(0)
|
||||||
|
header.createCell(col++).setCellValue("#")
|
||||||
|
orderedIds.forEach { id -> header.createCell(col++).setCellValue(id) }
|
||||||
|
|
||||||
|
clients.forEachIndexed { rowIdx, client ->
|
||||||
|
val row = sheet.createRow(rowIdx + 1)
|
||||||
|
var c = 0
|
||||||
|
row.createCell(c++).setCellValue((rowIdx + 1).toDouble())
|
||||||
|
|
||||||
|
val completedForClient = MyApp.database.completedQuestionnaireDao().getAllForClient(client.clientCode)
|
||||||
|
val statusMap = completedForClient.associate { it.questionnaireId to it.isDone }
|
||||||
|
val answers = MyApp.database.answerDao().getAnswersForClient(client.clientCode)
|
||||||
|
val answerMap = answers.associate { it.questionId to it.answerValue }
|
||||||
|
|
||||||
|
orderedIds.forEach { id ->
|
||||||
|
// Rohwert wie bisher bestimmen
|
||||||
|
val raw = when {
|
||||||
|
id == "client_code" -> client.clientCode
|
||||||
|
id in questionnaireIdSet -> if (statusMap[id] == true) "Done" else "Not Done"
|
||||||
|
else -> answerMap[id]?.takeIf { it.isNotBlank() } ?: "None"
|
||||||
|
}
|
||||||
|
// NEU: für Excel auf Englisch lokalisieren (Done/Not Done/None bleiben)
|
||||||
|
val out = localizeForExportEn(id, raw)
|
||||||
|
row.createCell(c++).setCellValue(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val bytes = java.io.ByteArrayOutputStream().use { bos ->
|
||||||
|
wb.write(bos)
|
||||||
|
bos.toByteArray()
|
||||||
|
}
|
||||||
|
wb.close()
|
||||||
|
|
||||||
|
val extDir = activity.getExternalFilesDir(android.os.Environment.DIRECTORY_DOCUMENTS)?.apply { mkdirs() }
|
||||||
|
val extFile = extDir?.let { File(it, "ClientHeaders.xlsx") }?.apply { writeBytes(bytes) }
|
||||||
|
|
||||||
|
val intDir = File(activity.filesDir, "exports").apply { mkdirs() }
|
||||||
|
val intFile = File(intDir, "ClientHeaders.xlsx").apply { writeBytes(bytes) }
|
||||||
|
|
||||||
|
Toast.makeText(
|
||||||
|
activity,
|
||||||
|
"Export:\n• intern: ${intFile.absolutePath}\n• extern: ${extFile?.absolutePath ?: "—"}",
|
||||||
|
Toast.LENGTH_LONG
|
||||||
|
).show()
|
||||||
|
|
||||||
|
return extFile ?: intFile
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// SCREEN 2: Fragebogen-Übersicht + "header"-Liste für einen Client
|
// SCREEN 2: Fragebogen-Übersicht + "header"-Liste für einen Client
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
@ -119,16 +230,10 @@ class DatabaseButtonHandler(
|
|||||||
val questionnaireIdSet = allQuestionnaires.map { it.id }.toSet()
|
val questionnaireIdSet = allQuestionnaires.map { it.id }.toSet()
|
||||||
val answerMap = allAnswersForClient.associate { it.questionId to it.answerValue }
|
val answerMap = allAnswersForClient.associate { it.questionId to it.answerValue }
|
||||||
|
|
||||||
// *** Sortierung der FRAGEBÖGEN nach questionnaire_1..., _2..., _3..., _4..., _5..., _6... ***
|
// Tabelle 1: Fragebögen (nur ✓ klickbar) – hier NUR Textfarbe (kein Hintergrund)
|
||||||
val sortedQuestionnaires = allQuestionnaires.sortedWith(
|
allQuestionnaires
|
||||||
compareBy(
|
.sortedWith(compareBy({ extractQuestionnaireNumber(it.id) ?: Int.MAX_VALUE }, { it.id }))
|
||||||
{ extractQuestionnaireNumber(it.id) ?: Int.MAX_VALUE },
|
.forEachIndexed { idx, q ->
|
||||||
{ it.id }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Tabelle 1: Fragebögen (nur ✓ klickbar) – HIER KEINE Hintergrundfarben, nur Textfarbe
|
|
||||||
sortedQuestionnaires.forEachIndexed { idx, q ->
|
|
||||||
val isDone = statusMap[q.id] ?: false
|
val isDone = statusMap[q.id] ?: false
|
||||||
val statusText = if (isDone) "✓" else "✗"
|
val statusText = if (isDone) "✓" else "✗"
|
||||||
val statusTextColor = if (isDone) 0xFF4CAF50.toInt() else 0xFFF44336.toInt()
|
val statusTextColor = if (isDone) 0xFF4CAF50.toInt() else 0xFFF44336.toInt()
|
||||||
@ -138,53 +243,55 @@ class DatabaseButtonHandler(
|
|||||||
table = tableQ,
|
table = tableQ,
|
||||||
cells = listOf((idx + 1).toString(), q.id, statusText),
|
cells = listOf((idx + 1).toString(), q.id, statusText),
|
||||||
onClick = { openQuestionnaireDetailScreen(clientCode, q.id) },
|
onClick = { openQuestionnaireDetailScreen(clientCode, q.id) },
|
||||||
colorOverrides = mapOf(2 to statusTextColor), // nur Text einfärben
|
colorOverrides = mapOf(2 to statusTextColor),
|
||||||
cellBgOverrides = emptyMap()
|
cellBgOverrides = emptyMap()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
addDisabledRow(
|
addDisabledRow(
|
||||||
table = tableQ,
|
table = tableQ,
|
||||||
cells = listOf((idx + 1).toString(), q.id, statusText),
|
cells = listOf((idx + 1).toString(), q.id, statusText),
|
||||||
colorOverrides = mapOf(2 to statusTextColor), // nur Text einfärben
|
colorOverrides = mapOf(2 to statusTextColor),
|
||||||
cellBgOverrides = emptyMap()
|
cellBgOverrides = emptyMap()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tabelle 2: "header"-Liste in der Reihenfolge aus assets/header_order.json
|
// Tabelle 2: "header"-Liste aus header_order.json
|
||||||
// HIER: GRÜN/ROT als HINTERGRUND für Fragebögen + GELB für "None"-Zeilen
|
|
||||||
val orderedIds = loadOrderedIds()
|
val orderedIds = loadOrderedIds()
|
||||||
orderedIds.forEachIndexed { idx, id ->
|
orderedIds.forEachIndexed { idx, id ->
|
||||||
var rowBgColor: Int? = null
|
var rowBgColor: Int? = null
|
||||||
|
val darkGray = 0xFFBDBDBD.toInt()
|
||||||
|
|
||||||
val (value, bgColorForCells) = when {
|
// 1) Rohwert ermitteln (für Logik & Farben)
|
||||||
id == "client_code" -> clientCode to null
|
val raw: String
|
||||||
id in questionnaireIdSet -> {
|
val bgColorForCells: Int?
|
||||||
if (statusMap[id] == true) {
|
|
||||||
"Done" to 0xFF4CAF50.toInt() // GRÜN als Hintergrund
|
if (id == "client_code") {
|
||||||
|
raw = clientCode
|
||||||
|
bgColorForCells = null
|
||||||
|
} else if (id in questionnaireIdSet) {
|
||||||
|
raw = if (statusMap[id] == true) "Done" else "Not Done"
|
||||||
|
bgColorForCells = if (raw == "Done") 0xFF4CAF50.toInt() else 0xFFF44336.toInt()
|
||||||
} else {
|
} else {
|
||||||
"Not Done" to 0xFFF44336.toInt() // ROT als Hintergrund
|
raw = answerMap[id]?.takeIf { it.isNotBlank() } ?: "None"
|
||||||
}
|
bgColorForCells = null
|
||||||
}
|
if (raw == "None") rowBgColor = darkGray
|
||||||
else -> {
|
|
||||||
val v = answerMap[id]
|
|
||||||
val out = if (!v.isNullOrBlank()) v else "None"
|
|
||||||
if (out == "None") {
|
|
||||||
rowBgColor = 0xFFFFF59D.toInt() // GELB ganze Zeile bei None
|
|
||||||
}
|
|
||||||
out to null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Für Fragebögen im Header: ID (Spalte 2) und Wert (Spalte 3) farbig HINTERLEGEN
|
// 2) Anzeige-Wert über LanguageManager lokalisieren (nur Header/Wert-Spalte)
|
||||||
val cellBg = if (bgColorForCells != null) mapOf(1 to bgColorForCells, 2 to bgColorForCells) else emptyMap()
|
val display = localizeHeaderValue(id, raw)
|
||||||
|
|
||||||
|
// Für Fragebögen im Header: "#"(0), ID(1) und Wert(2) farbig HINTERLEGEN
|
||||||
|
val cellBg = if (bgColorForCells != null)
|
||||||
|
mapOf(0 to bgColorForCells, 1 to bgColorForCells, 2 to bgColorForCells)
|
||||||
|
else emptyMap()
|
||||||
|
|
||||||
addRow(
|
addRow(
|
||||||
table = tableOrdered,
|
table = tableOrdered,
|
||||||
cells = listOf((idx + 1).toString(), id, value),
|
cells = listOf((idx + 1).toString(), id, display),
|
||||||
colorOverrides = emptyMap(), // keine Textfarben im Header
|
colorOverrides = emptyMap(), // keine Textfarben im Header
|
||||||
rowBgColor = rowBgColor, // GELB für "None"
|
rowBgColor = rowBgColor, // dunkelgrau für "None"
|
||||||
cellBgOverrides = cellBg // GRÜN/ROT für Done/Not Done
|
cellBgOverrides = cellBg // GRÜN/ROT – inkl. Spalte "#"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,11 +367,54 @@ class DatabaseButtonHandler(
|
|||||||
// Hilfsfunktionen
|
// Hilfsfunktionen
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
private fun extractQuestionnaireNumber(id: String): Int? {
|
private fun extractQuestionnaireNumber(id: String): Int? {
|
||||||
// Erwartet Präfix "questionnaire_<zahl>"
|
|
||||||
val m = Regex("^questionnaire_(\\d+)").find(id.lowercase())
|
val m = Regex("^questionnaire_(\\d+)").find(id.lowercase())
|
||||||
return m?.groupValues?.get(1)?.toIntOrNull()
|
return m?.groupValues?.get(1)?.toIntOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lokalisiert den im Header angezeigten Wert (Wert-Spalte) über LanguageManager.
|
||||||
|
// Probiert mehrere Schlüssel-Varianten, fällt ansonsten auf den Rohwert zurück.
|
||||||
|
// Ersetzt die alte Version 1:1
|
||||||
|
private fun localizeHeaderValue(id: String, raw: String): String {
|
||||||
|
// client_code nie übersetzen
|
||||||
|
if (id == "client_code") return raw
|
||||||
|
|
||||||
|
val lang = try { languageIDProvider() } catch (_: Exception) { "GERMAN" }
|
||||||
|
|
||||||
|
fun stripBrackets(s: String): String {
|
||||||
|
val m = Regex("^\\[(.*)]$").matchEntire(s.trim())
|
||||||
|
return m?.groupValues?.get(1) ?: s
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tryKey(key: String): String? {
|
||||||
|
val t = try { LanguageManager.getText(lang, key) } catch (_: Exception) { key }
|
||||||
|
val stripped = stripBrackets(t)
|
||||||
|
// Wenn Ergebnis leer, gleich dem Key (nach evtl. Klammern) oder nur ein Platzhalter war: keine gültige Übersetzung
|
||||||
|
if (stripped.isBlank() || stripped.equals(key, ignoreCase = true)) return null
|
||||||
|
return stripped
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kandidaten-Schlüssel in sinnvoller Reihenfolge
|
||||||
|
val norm = raw.lowercase().replace(Regex("[^a-z0-9]+"), "_").trim('_')
|
||||||
|
val candidates = buildList {
|
||||||
|
when (raw) {
|
||||||
|
"Done" -> add("done")
|
||||||
|
"Not Done" -> add("not_done")
|
||||||
|
"None" -> add("none")
|
||||||
|
}
|
||||||
|
add(raw)
|
||||||
|
if (norm.isNotBlank() && norm != raw) add(norm)
|
||||||
|
if (norm.isNotBlank()) {
|
||||||
|
add("${id}_$norm")
|
||||||
|
add("${id}-$norm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (key in candidates) {
|
||||||
|
tryKey(key)?.let { return it } // nur echte Übersetzungen übernehmen
|
||||||
|
}
|
||||||
|
return raw // Fallback ohne eckige Klammern
|
||||||
|
}
|
||||||
|
|
||||||
private fun addHeaderRow(table: TableLayout, labels: List<String>) {
|
private fun addHeaderRow(table: TableLayout, labels: List<String>) {
|
||||||
val row = TableRow(activity)
|
val row = TableRow(activity)
|
||||||
labels.forEach { label -> row.addView(makeHeaderCell(label)) }
|
labels.forEach { label -> row.addView(makeHeaderCell(label)) }
|
||||||
@ -377,4 +527,33 @@ class DatabaseButtonHandler(
|
|||||||
}
|
}
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun localizeForExportEn(id: String, raw: String): String {
|
||||||
|
if (id == "client_code") return raw
|
||||||
|
if (raw == "Done" || raw == "Not Done" || raw == "None") return raw
|
||||||
|
|
||||||
|
fun stripBrackets(s: String): String {
|
||||||
|
val m = Regex("^\\[(.*)]$").matchEntire(s.trim())
|
||||||
|
return m?.groupValues?.get(1) ?: s
|
||||||
|
}
|
||||||
|
fun tryKey(key: String): String? {
|
||||||
|
val t = try { LanguageManager.getText("ENGLISH", key) } catch (_: Exception) { key }
|
||||||
|
val stripped = stripBrackets(t)
|
||||||
|
if (stripped.isBlank() || stripped.equals(key, ignoreCase = true)) return null
|
||||||
|
return stripped
|
||||||
|
}
|
||||||
|
|
||||||
|
val norm = raw.lowercase().replace(Regex("[^a-z0-9]+"), "_").trim('_')
|
||||||
|
val candidates = buildList {
|
||||||
|
add(raw)
|
||||||
|
if (norm.isNotBlank() && norm != raw) add(norm)
|
||||||
|
if (norm.isNotBlank()) {
|
||||||
|
add("${id}_$norm")
|
||||||
|
add("${id}-$norm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (key in candidates) tryKey(key)?.let { return it }
|
||||||
|
return raw
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -345,13 +345,16 @@ class HandlerOpeningScreen(private val activity: MainActivity) {
|
|||||||
private fun setupDatabaseButtonHandler() {
|
private fun setupDatabaseButtonHandler() {
|
||||||
DatabaseButtonHandler(
|
DatabaseButtonHandler(
|
||||||
activity = activity,
|
activity = activity,
|
||||||
databaseButton = databaseButton
|
databaseButton = databaseButton,
|
||||||
) {
|
onClose = {
|
||||||
// zurück zum Opening-Screen
|
// zurück zum Opening-Screen
|
||||||
init()
|
init()
|
||||||
}.setup()
|
},
|
||||||
|
languageIDProvider = { languageID } // aktuelle Sprache an DatabaseButtonHandler weitergeben
|
||||||
|
).setup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun updateMainButtonsState(isDatabaseAvailable: Boolean) {
|
private fun updateMainButtonsState(isDatabaseAvailable: Boolean) {
|
||||||
val buttons = listOf(buttonLoad, saveButton, editButton, databaseButton) // <-- NEU dabei
|
val buttons = listOf(buttonLoad, saveButton, editButton, databaseButton) // <-- NEU dabei
|
||||||
buttons.forEach { button ->
|
buttons.forEach { button ->
|
||||||
|
|||||||
@ -53,6 +53,13 @@
|
|||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
android:paddingBottom="8dp" />
|
android:paddingBottom="8dp" />
|
||||||
|
|
||||||
|
<!-- NEU: Download-Button -->
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnDownloadHeader"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Download Header" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/backButton"
|
android:id="@+id/backButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
Reference in New Issue
Block a user