diff --git a/app/src/main/java/com/dano/test1/DatabaseButtonHandler.kt b/app/src/main/java/com/dano/test1/DatabaseButtonHandler.kt index 2109144..426a07d 100644 --- a/app/src/main/java/com/dano/test1/DatabaseButtonHandler.kt +++ b/app/src/main/java/com/dano/test1/DatabaseButtonHandler.kt @@ -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: + * + * 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")