removed error: Toast.makeText(activity, "Fehlende View: $name", Toast.LENGTH_LONG).show()

This commit is contained in:
oxidiert
2025-09-18 08:50:59 +02:00
parent 4cf840b37a
commit 7014386953
2 changed files with 21 additions and 28 deletions

View File

@ -4,10 +4,10 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-09-06T18:16:00.187475900Z"> <DropdownSelection timestamp="2025-09-17T11:15:49.688310200Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=HA218GZY" /> <DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\danie\.android\avd\Medium_Phone.avd" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>

View File

@ -17,7 +17,6 @@ import org.json.JSONArray
import java.io.File 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.ss.usermodel.Row
import org.apache.poi.xssf.usermodel.XSSFWorkbook import org.apache.poi.xssf.usermodel.XSSFWorkbook
@ -89,7 +88,7 @@ class DatabaseButtonHandler(
uiScope.launch { uiScope.launch {
try { try {
progress.visibility = View.VISIBLE progress.visibility = View.VISIBLE
val savedUri = exportHeadersForAllClients() // => speichert NUR in Downloads val savedUri = exportHeadersForAllClients() // -> speichert NUR in Downloads
progress.visibility = View.GONE progress.visibility = View.GONE
if (savedUri != null) { if (savedUri != null) {
@ -101,10 +100,6 @@ class DatabaseButtonHandler(
} else { } else {
Toast.makeText(activity, "Export fehlgeschlagen.", Toast.LENGTH_LONG).show() Toast.makeText(activity, "Export fehlgeschlagen.", Toast.LENGTH_LONG).show()
} }
} catch (e: SecurityException) {
progress.visibility = View.GONE
Log.e(tag, "Berechtigungsfehler beim Schreiben nach Downloads: ${e.message}", e)
Toast.makeText(activity, "Fehlende Speicherberechtigung (ältere Android-Version).", Toast.LENGTH_LONG).show()
} catch (e: Exception) { } catch (e: Exception) {
progress.visibility = View.GONE progress.visibility = View.GONE
Log.e(tag, "Download Header Fehler: ${e.message}", e) Log.e(tag, "Download Header Fehler: ${e.message}", e)
@ -114,13 +109,11 @@ class DatabaseButtonHandler(
} }
/** /**
* Erzeugt eine Excel-Datei (ClientHeaders.xlsx) und speichert sie AUSSCHLIESSLICH * Erzeugt eine Excel-Datei (ClientHeaders.xlsx) mit:
* im öffentlichen Geräte-Ordner „Downloads“. * - Zeile 1: Spalten-IDs (header_order.json / header_order.xlsx)
* * - Zeile 2: ENGLISCHE Fragen/Beschriftungen zu jeder ID (rein EN, keine IDs mehr)
* Hinweis zu alten Geräten (Android 9 und älter): * - Ab Zeile 3: pro Client die Werte ("Done"/"Not Done"/Antwort oder "None")
* - Füge ggf. in der AndroidManifest.xml hinzu: * - Speichert AUSSCHLIESSLICH in den öffentlichen Geräte-Ordner "Downloads"
* <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
* und frage die Laufzeitberechtigung ab, falls dein targetSdk das erfordert.
*/ */
private suspend fun exportHeadersForAllClients(): Uri? { private suspend fun exportHeadersForAllClients(): Uri? {
val orderedIds = loadOrderedIds() val orderedIds = loadOrderedIds()
@ -168,7 +161,7 @@ class DatabaseButtonHandler(
id in questionnaireIdSet -> if (statusMap[id] == true) "Done" else "Not Done" id in questionnaireIdSet -> if (statusMap[id] == true) "Done" else "Not Done"
else -> answerMap[id]?.takeIf { it.isNotBlank() } ?: "None" else -> answerMap[id]?.takeIf { it.isNotBlank() } ?: "None"
} }
val out = localizeForExportEn(id, raw) // Export immer EN (Done/Not Done/None bleiben) val out = localizeForExportEn(id, raw) // Export immer EN
row.createCell(c++).setCellValue(out) row.createCell(c++).setCellValue(out)
} }
} }
@ -179,38 +172,39 @@ class DatabaseButtonHandler(
} }
wb.close() wb.close()
// NUR Downloads speichern // -> nur in Downloads speichern (Q+ via MediaStore, sonst Public Downloads)
val uri = saveToDownloads( return saveToDownloads(
filename = "ClientHeaders.xlsx", filename = "ClientHeaders.xlsx",
mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
bytes = bytes bytes = bytes
) )
return uri
} }
// Speichert Bytes nach Downloads“ (Android 10+ via MediaStore, sonst klassisch). /** Speichert Bytes nach "Downloads". */
private fun saveToDownloads(filename: String, mimeType: String, bytes: ByteArray): Uri? { private fun saveToDownloads(filename: String, mimeType: String, bytes: ByteArray): Uri? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Android 10+ (Scoped Storage)
val resolver = activity.contentResolver val resolver = activity.contentResolver
val values = ContentValues().apply { val values = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, filename) put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, mimeType) put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS) put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
// optional: put(MediaStore.MediaColumns.IS_PENDING, 1)
} }
val collection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) val collection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val uri = resolver.insert(collection, values) val uri = resolver.insert(collection, values)
if (uri != null) { if (uri != null) {
resolver.openOutputStream(uri)?.use { it.write(bytes) } ?: return null resolver.openOutputStream(uri)?.use { it.write(bytes) } ?: return null
// optional: values.clear(); values.put(MediaStore.MediaColumns.IS_PENDING, 0); resolver.update(uri, values, null, null)
uri uri
} else null } else null
} else { } else {
// Android 9 (API 28) und älter: öffentliches Downloads-Verzeichnis // Android 9 und älter public Downloads (WRITE_EXTERNAL_STORAGE kann nötig sein)
val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
if (!downloadsDir.exists()) downloadsDir.mkdirs() if (!downloadsDir.exists()) downloadsDir.mkdirs()
val outFile = File(downloadsDir, filename) val outFile = File(downloadsDir, filename)
outFile.writeBytes(bytes) outFile.writeBytes(bytes)
// Im System sichtbar machen // im System sichtbar machen
MediaScannerConnection.scanFile( MediaScannerConnection.scanFile(
activity, activity,
arrayOf(outFile.absolutePath), arrayOf(outFile.absolutePath),
@ -261,7 +255,7 @@ class DatabaseButtonHandler(
private fun openClientOverviewScreen(clientCode: String) { private fun openClientOverviewScreen(clientCode: String) {
activity.setContentView(R.layout.client_overview_screen) activity.setContentView(R.layout.client_overview_screen)
val title: TextView = requireView(R.id.titleQuestionnaireDetail, "titleQuestionnaireDetail") val title: TextView = requireView(R.id.titleClientOverview, "titleClientOverview")
val tableQ: TableLayout = requireView(R.id.tableQuestionnaires, "tableQuestionnaires") val tableQ: TableLayout = requireView(R.id.tableQuestionnaires, "tableQuestionnaires")
val progress: ProgressBar = requireView(R.id.progressBarClient, "progressBarClient") val progress: ProgressBar = requireView(R.id.progressBarClient, "progressBarClient")
val emptyView: TextView = requireView(R.id.emptyViewClient, "emptyViewClient") val emptyView: TextView = requireView(R.id.emptyViewClient, "emptyViewClient")
@ -534,10 +528,9 @@ class DatabaseButtonHandler(
private fun localizeEnglishNoBrackets(key: String): String? { private fun localizeEnglishNoBrackets(key: String): String? {
val t = try { LanguageManager.getText("ENGLISH", key) } catch (_: Exception) { null } val t = try { LanguageManager.getText("ENGLISH", key) } catch (_: Exception) { null }
if (t == null) return null val m = Regex("^\\[(.*)]$").matchEntire(t?.trim() ?: "")
val m = Regex("^\\[(.*)]$").matchEntire(t.trim())
val stripped = m?.groupValues?.get(1) ?: t val stripped = m?.groupValues?.get(1) ?: t
if (stripped.isBlank() || stripped.equals(key, ignoreCase = true)) return null if (stripped == null || stripped.isBlank() || stripped.equals(key, ignoreCase = true)) return null
return stripped return stripped
} }
@ -593,7 +586,7 @@ class DatabaseButtonHandler(
val row = TableRow(activity).apply { val row = TableRow(activity).apply {
isClickable = true isClickable = true
isFocusable = true isFocusable = true
setBackgroundResource(android.R.drawable.list_selector_background) setBackgroundColor(android.graphics.Color.TRANSPARENT)
setOnClickListener { onClick() } setOnClickListener { onClick() }
} }
cells.forEachIndexed { index, text -> cells.forEachIndexed { index, text ->