saving header in downloads

This commit is contained in:
oxidiert
2025-09-17 12:46:23 +02:00
parent 77742275e6
commit af9c045341

View File

@ -1,6 +1,11 @@
package com.dano.test1
import android.content.ContentValues
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.util.Log
import android.view.View
import android.widget.*
@ -84,18 +89,22 @@ class DatabaseButtonHandler(
uiScope.launch {
try {
progress.visibility = View.VISIBLE
val exportFile = exportHeadersForAllClients()
val savedUri = exportHeadersForAllClients() // => speichert NUR in Downloads
progress.visibility = View.GONE
if (exportFile != null) {
if (savedUri != null) {
Toast.makeText(
activity,
"Export erfolgreich: ${exportFile.absolutePath}",
"Export erfolgreich: Downloads/ClientHeaders.xlsx",
Toast.LENGTH_LONG
).show()
} else {
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) {
progress.visibility = View.GONE
Log.e(tag, "Download Header Fehler: ${e.message}", e)
@ -105,12 +114,15 @@ class DatabaseButtonHandler(
}
/**
* Erzeugt eine Excel-Datei (ClientHeaders.xlsx) mit:
* - Zeile 1: Spalten-IDs (header_order.json / header_order.xlsx)
* - Zeile 2: ENGLISCHE Fragen/Beschriftungen zu jeder ID (rein EN, keine IDs mehr)
* - Ab Zeile 3: pro Client die Werte ("Done"/"Not Done"/Antwort oder "None")
* Erzeugt eine Excel-Datei (ClientHeaders.xlsx) und speichert sie AUSSCHLIESSLICH
* im öffentlichen Geräte-Ordner „Downloads“.
*
* Hinweis zu alten Geräten (Android 9 und älter):
* - Füge ggf. in der AndroidManifest.xml hinzu:
* <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
* und frage die Laufzeitberechtigung ab, falls dein targetSdk das erfordert.
*/
private suspend fun exportHeadersForAllClients(): File? {
private suspend fun exportHeadersForAllClients(): Uri? {
val orderedIds = loadOrderedIds()
if (orderedIds.isEmpty()) return null
@ -161,25 +173,52 @@ class DatabaseButtonHandler(
}
}
// Datei schreiben: extern + intern
// Bytes erzeugen
val bytes = java.io.ByteArrayOutputStream().use { bos ->
wb.write(bos); bos.toByteArray()
}
wb.close()
val extDir = activity.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)?.apply { mkdirs() }
val extFile = extDir?.let { File(it, "ClientHeaders.xlsx") }?.apply { writeBytes(bytes) }
// NUR Downloads speichern
val uri = saveToDownloads(
filename = "ClientHeaders.xlsx",
mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
bytes = bytes
)
val intDir = File(activity.filesDir, "exports").apply { mkdirs() }
val intFile = File(intDir, "ClientHeaders.xlsx").apply { writeBytes(bytes) }
return uri
}
Toast.makeText(
activity,
"Export:\n• intern: ${intFile.absolutePath}\n• extern: ${extFile?.absolutePath ?: "—"}",
Toast.LENGTH_LONG
).show()
return extFile ?: intFile
// Speichert Bytes nach „Downloads“ (Android 10+ via MediaStore, sonst klassisch).
private fun saveToDownloads(filename: String, mimeType: String, bytes: ByteArray): Uri? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val resolver = activity.contentResolver
val values = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
}
val collection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val uri = resolver.insert(collection, values)
if (uri != null) {
resolver.openOutputStream(uri)?.use { it.write(bytes) } ?: return null
uri
} else null
} else {
// Android 9 (API 28) und älter: öffentliches Downloads-Verzeichnis
val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
if (!downloadsDir.exists()) downloadsDir.mkdirs()
val outFile = File(downloadsDir, filename)
outFile.writeBytes(bytes)
// Im System sichtbar machen
MediaScannerConnection.scanFile(
activity,
arrayOf(outFile.absolutePath),
arrayOf(mimeType),
null
)
Uri.fromFile(outFile)
}
}
// Liefert einen englischen, lesbaren Fragetext/Titel zu einer ID (nur EN, keine IDs).
@ -222,7 +261,7 @@ class DatabaseButtonHandler(
private fun openClientOverviewScreen(clientCode: String) {
activity.setContentView(R.layout.client_overview_screen)
val title: TextView = requireView(R.id.titleClientOverview, "titleClientOverview")
val title: TextView = requireView(R.id.titleQuestionnaireDetail, "titleQuestionnaireDetail")
val tableQ: TableLayout = requireView(R.id.tableQuestionnaires, "tableQuestionnaires")
val progress: ProgressBar = requireView(R.id.progressBarClient, "progressBarClient")
val emptyView: TextView = requireView(R.id.emptyViewClient, "emptyViewClient")