diff --git a/app/src/main/java/de/medapp/wunddoku/data/repository/DocumentationRepository.kt b/app/src/main/java/de/medapp/wunddoku/data/repository/DocumentationRepository.kt index 1ec75c0..ab7933d 100644 --- a/app/src/main/java/de/medapp/wunddoku/data/repository/DocumentationRepository.kt +++ b/app/src/main/java/de/medapp/wunddoku/data/repository/DocumentationRepository.kt @@ -29,10 +29,13 @@ import de.medapp.wunddoku.pdf.DocumentationPdfExporter import de.medapp.wunddoku.pdf.DocumentationToPdfExportModelMapper import de.medapp.wunddoku.pdf.PdfExportError import de.medapp.wunddoku.pdf.PdfExportResult +import de.medapp.wunddoku.pdf.PdfToImagesConverter +import de.medapp.wunddoku.pdf.PdfToImagesResult import de.medapp.wunddoku.signature.SignatureBitmapValidation import de.medapp.wunddoku.signature.SignatureStorage import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map +import java.io.File import java.time.Instant import java.util.UUID @@ -60,6 +63,7 @@ class DocumentationRepository( private val signatureStorage: SignatureStorage, private val woundCarerRepository: WoundCarerRepository, private val pdfExporter: DocumentationPdfExporter, + private val pdfToImagesConverter: PdfToImagesConverter, private val db: AppDatabase, ) { @@ -284,6 +288,16 @@ class DocumentationRepository( return pdfExporter.export(model) } + /** + * Rendert eine bereits erzeugte PDF-Datei (s. [exportPdf]) seitenweise in JPEG-Dateien + * (Änderungswunsch: "PDF zusätzlich als JPEG speichern/senden können"). Nimmt bewusst die + * fertige [pdfFile] entgegen, statt die Dokumentation erneut zu laden/zu mappen - der + * Aufrufer hat sie direkt aus einem vorangegangenen [exportPdf]-Aufruf zur Verfügung, ein + * erneutes Laden wäre unnötige Doppelarbeit. + */ + suspend fun convertPdfToImages(documentationId: String, pdfFile: File): PdfToImagesResult = + pdfToImagesConverter.convert(documentationId, pdfFile) + /** * Liefert eine speicherschonende Vorschauversion eines Fotos für die Anzeige im Fotogitter * der Dokumentationsbearbeitung (kein Laden der vollen gespeicherten Auflösung). Wird von diff --git a/app/src/main/java/de/medapp/wunddoku/di/AppContainer.kt b/app/src/main/java/de/medapp/wunddoku/di/AppContainer.kt index ab5bbaf..4909755 100644 --- a/app/src/main/java/de/medapp/wunddoku/di/AppContainer.kt +++ b/app/src/main/java/de/medapp/wunddoku/di/AppContainer.kt @@ -8,7 +8,9 @@ import de.medapp.wunddoku.data.repository.WoundCarerRepository import de.medapp.wunddoku.media.InternalPhotoStorage import de.medapp.wunddoku.media.PhotoStorage import de.medapp.wunddoku.pdf.AndroidPdfDocumentExporter +import de.medapp.wunddoku.pdf.AndroidPdfToImagesConverter import de.medapp.wunddoku.pdf.DocumentationPdfExporter +import de.medapp.wunddoku.pdf.PdfToImagesConverter import de.medapp.wunddoku.signature.InternalSignatureStorage import de.medapp.wunddoku.signature.SignatureStorage @@ -38,6 +40,7 @@ class AppContainer(context: Context) { private val pdfExporter: DocumentationPdfExporter by lazy { AndroidPdfDocumentExporter(context, photoStorage, signatureStorage) } + private val pdfToImagesConverter: PdfToImagesConverter by lazy { AndroidPdfToImagesConverter(context) } val patientRepository: PatientRepository by lazy { PatientRepository(patientDao = database.patientDao()) @@ -62,6 +65,7 @@ class AppContainer(context: Context) { signatureStorage = signatureStorage, woundCarerRepository = woundCarerRepository, pdfExporter = pdfExporter, + pdfToImagesConverter = pdfToImagesConverter, db = database, ) } diff --git a/app/src/main/java/de/medapp/wunddoku/pdf/PdfImageSharer.kt b/app/src/main/java/de/medapp/wunddoku/pdf/PdfImageSharer.kt new file mode 100644 index 0000000..6f9dc84 --- /dev/null +++ b/app/src/main/java/de/medapp/wunddoku/pdf/PdfImageSharer.kt @@ -0,0 +1,99 @@ +package de.medapp.wunddoku.pdf + +import android.content.ActivityNotFoundException +import android.content.ClipData +import android.content.Context +import android.content.Intent +import androidx.core.content.FileProvider +import java.io.File + +/** + * Übergibt alle als JPEG gerenderten PDF-Seiten (s. [PdfToImagesConverter]) GEMEINSAM an den + * Android-"Senden an"-Dialog (`ACTION_SEND_MULTIPLE`) - Änderungswunsch: "alle generierten + * Bilder über 'Senden an' direkt verschicken können", nicht Seite für Seite einzeln. + * + * Bewusst getrennt von [PdfToImagesConverter] (reine Datei-Logik): das Starten einer Activity + * ist eine UI-/Plattform-Aktion, keine Repository-Aufgabe - exakt dieselbe Trennung wie bei + * [DocumentationPdfExporter] (Erzeugung) vs. [PdfOpener] (Anzeige). + * + * VulneraDoc implementiert ausdrücklich KEINEN eigenen Versand-Mechanismus; welche konkrete App + * (Mail, Messenger, Cloud-Speicher, ...) die Bilder entgegennimmt, entscheidet ausschließlich + * Android/der Nutzer über den System-Dialog. + */ +interface PdfImageSharer { + fun share(imageFiles: List): PdfImageShareResult +} + +sealed interface PdfImageShareResult { + data object Success : PdfImageShareResult + + sealed interface Failure : PdfImageShareResult { + /** Es wurden keine Bilddateien übergeben - nichts zum Teilen vorhanden. */ + data object NoImages : Failure + + /** FileProvider konnte für mindestens eine Datei keine content://-Uri erzeugen. */ + data class UriResolutionFailed(val message: String) : Failure + + /** Keine installierte App kann Bilder (image/jpeg) entgegennehmen. */ + data object NoAppAvailable : Failure + + data class Unexpected(val message: String) : Failure + } +} + +fun PdfImageShareResult.Failure.toUserMessage(): String = when (this) { + PdfImageShareResult.Failure.NoImages -> + "Es sind keine Bilder zum Versenden vorhanden." + is PdfImageShareResult.Failure.UriResolutionFailed -> + "Die Bilder konnten nicht zum Versenden freigegeben werden." + PdfImageShareResult.Failure.NoAppAvailable -> + "Auf diesem Gerät ist keine App zum Versenden von Bildern installiert." + is PdfImageShareResult.Failure.Unexpected -> + "Die Bilder konnten nicht versendet werden." +} + +class AndroidPdfImageSharer(private val context: Context) : PdfImageSharer { + + override fun share(imageFiles: List): PdfImageShareResult { + if (imageFiles.isEmpty()) return PdfImageShareResult.Failure.NoImages + + val authority = "${context.packageName}.fileprovider" + val uris = try { + imageFiles.map { file -> FileProvider.getUriForFile(context, authority, file) } + } catch (e: IllegalArgumentException) { + // Wird von FileProvider geworfen, wenn eine Datei außerhalb der in file_paths.xml + // freigegebenen Verzeichnisse liegt oder die Authority nicht passt. + return PdfImageShareResult.Failure.UriResolutionFailed(e.message ?: "Uri konnte nicht erzeugt werden.") + } + + val intent = Intent(Intent.ACTION_SEND_MULTIPLE).apply { + type = "image/jpeg" + putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(uris)) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + // Zusätzlich zum Flag oben wird die Leseberechtigung explizit über ClipData an JEDE + // einzelne Uri gehängt - bei ACTION_SEND_MULTIPLE mit mehreren content://-Uris ist + // das der zuverlässige Weg, damit wirklich ALLE empfangenden Apps Zugriff auf ALLE + // Bilder bekommen (nicht nur auf die im Intent-Datenfeld primär referenzierte). + clipData = ClipData.newUri(context.contentResolver, "Wunddokumentation", uris.first()).apply { + for (uri in uris.drop(1)) { + addItem(ClipData.Item(uri)) + } + } + } + + val chooser = Intent.createChooser(intent, "Bilder senden an") + + return try { + context.startActivity(chooser) + PdfImageShareResult.Success + } catch (e: ActivityNotFoundException) { + // Bewusst KEIN vorheriges intent.resolveActivity(...) - s. Begründung in + // AndroidPdfOpener.open() (Package-Visibility-Einschränkungen ab Android 11). + PdfImageShareResult.Failure.NoAppAvailable + } catch (e: SecurityException) { + PdfImageShareResult.Failure.Unexpected(e.message ?: "Zugriff auf die Bilddateien wurde verweigert.") + } catch (e: Exception) { + PdfImageShareResult.Failure.Unexpected(e.message ?: "Unbekannter Fehler beim Versenden der Bilder.") + } + } +} diff --git a/app/src/main/java/de/medapp/wunddoku/pdf/PdfToImagesConverter.kt b/app/src/main/java/de/medapp/wunddoku/pdf/PdfToImagesConverter.kt new file mode 100644 index 0000000..422a249 --- /dev/null +++ b/app/src/main/java/de/medapp/wunddoku/pdf/PdfToImagesConverter.kt @@ -0,0 +1,117 @@ +package de.medapp.wunddoku.pdf + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Color +import android.graphics.pdf.PdfRenderer +import android.os.ParcelFileDescriptor +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.File +import java.io.FileOutputStream +import kotlin.math.roundToInt + +/** + * Rendert eine bereits fertig erzeugte PDF-Datei (s. [DocumentationPdfExporter]) seitenweise in + * einzelne JPEG-Dateien - Änderungswunsch: "PDF zusätzlich als JPEG speichern/senden können". + * + * Bewusst als eigener Schritt NACH dem PDF-Export statt als Alternative dazu: Es gibt weiterhin + * genau eine Quelle der Wahrheit für das Seitenlayout (der bestehende [PdfPageCursor]-basierte + * Export) - dieser Konverter rastert lediglich das bereits fertige Ergebnis, statt eine zweite, + * parallele Layout-Logik zu pflegen. + * + * Verwendet [android.graphics.pdf.PdfRenderer] (Android-Bordmittel seit API 21, unproblematisch + * bei minSdk 26) - keine zusätzliche Bibliothek nötig. Eine mehrseitige Wunddokumentation (Kopf/ + * Blöcke, Signaturen, ggf. 30+ Fotoseiten) ergibt dabei bewusst EIN JPEG PRO SEITE, kein + * einzelnes Sammelbild - beim späteren Teilen (s. [PdfImageSharer]) werden alle Seiten + * gemeinsam als Bilder-Mehrfachauswahl übergeben. + */ +interface PdfToImagesConverter { + suspend fun convert(documentationId: String, pdfFile: File): PdfToImagesResult +} + +sealed interface PdfToImagesResult { + data class Success(val imageFiles: List) : PdfToImagesResult + + sealed interface Failure : PdfToImagesResult { + /** Die übergebene PDF-Datei existiert nicht (mehr). */ + data object FileNotFound : Failure + + /** PdfRenderer meldet 0 Seiten - sollte bei einem selbst erzeugten PDF nie vorkommen. */ + data object EmptyDocument : Failure + + data class Unexpected(val message: String) : Failure + } +} + +fun PdfToImagesResult.Failure.toUserMessage(): String = when (this) { + PdfToImagesResult.Failure.FileNotFound -> + "Die PDF-Datei wurde nicht gefunden. Bitte zuerst die Dokumentation erneut exportieren." + PdfToImagesResult.Failure.EmptyDocument -> + "Die PDF-Datei enthält keine Seiten." + is PdfToImagesResult.Failure.Unexpected -> + "Die PDF-Datei konnte nicht in Bilder umgewandelt werden." +} + +class AndroidPdfToImagesConverter(private val context: Context) : PdfToImagesConverter { + + override suspend fun convert(documentationId: String, pdfFile: File): PdfToImagesResult = + withContext(Dispatchers.IO) { + if (!pdfFile.exists()) return@withContext PdfToImagesResult.Failure.FileNotFound + + val outputDir = File(File(context.filesDir, "pdf_images"), documentationId) + // Bilder eines vorherigen Exports zuerst entfernen, damit bei einer inzwischen + // kürzeren PDF (z. B. Fotos gelöscht) keine veralteten, zu vielen Seiten-Dateien + // liegen bleiben. + outputDir.deleteRecursively() + outputDir.mkdirs() + + try { + ParcelFileDescriptor.open(pdfFile, ParcelFileDescriptor.MODE_READ_ONLY).use { fd -> + PdfRenderer(fd).use { renderer -> + if (renderer.pageCount == 0) { + return@withContext PdfToImagesResult.Failure.EmptyDocument + } + + val imageFiles = mutableListOf() + for (pageIndex in 0 until renderer.pageCount) { + renderer.openPage(pageIndex).use { page -> + val scale = RENDER_DPI / POINTS_PER_INCH + val width = (page.width * scale).roundToInt().coerceAtLeast(1) + val height = (page.height * scale).roundToInt().coerceAtLeast(1) + + val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + // WICHTIG: PdfRenderer zeichnet nur die tatsächlich im PDF + // vorhandenen Inhalte - unbedeckte Flächen bleiben transparent + // (Alpha 0). JPEG kennt keine Transparenz; ohne dieses + // Vorab-Weißfüllen würde jede unbedeckte Fläche beim + // Komprimieren als SCHWARZ statt als weißer Seitenhintergrund + // enden (bekannte PdfRenderer-Falle, s. offizielle Android- + // Beispiele). + bitmap.eraseColor(Color.WHITE) + page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY) + + val imageFile = File(outputDir, "seite_%02d.jpg".format(pageIndex + 1)) + FileOutputStream(imageFile).use { out -> + bitmap.compress(Bitmap.CompressFormat.JPEG, JPEG_QUALITY, out) + } + bitmap.recycle() + imageFiles.add(imageFile) + } + } + PdfToImagesResult.Success(imageFiles) + } + } + } catch (e: Exception) { + PdfToImagesResult.Failure.Unexpected(e.message ?: "Unbekannter Fehler bei der Bildkonvertierung.") + } + } + + companion object { + /** PDF-Koordinaten sind in Punkten (1/72 Zoll) angegeben - diese Ziel-Auflösung ergibt + * ein für Ausdruck/Weitergabe ausreichend scharfes, aber nicht unnötig großes Bild. */ + private const val RENDER_DPI = 200f + private const val POINTS_PER_INCH = 72f + private const val JPEG_QUALITY = 90 + } +} diff --git a/app/src/main/java/de/medapp/wunddoku/ui/documentation/edit/DocumentationEditScreen.kt b/app/src/main/java/de/medapp/wunddoku/ui/documentation/edit/DocumentationEditScreen.kt index 07b4c78..007af8e 100644 --- a/app/src/main/java/de/medapp/wunddoku/ui/documentation/edit/DocumentationEditScreen.kt +++ b/app/src/main/java/de/medapp/wunddoku/ui/documentation/edit/DocumentationEditScreen.kt @@ -30,6 +30,7 @@ import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.ExpandLess import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material.icons.filled.PictureAsPdf +import androidx.compose.material.icons.filled.Share import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.Checkbox @@ -75,7 +76,9 @@ import de.medapp.wunddoku.domain.model.CheckItem import de.medapp.wunddoku.domain.model.DocumentationBlock import de.medapp.wunddoku.domain.model.Photo import de.medapp.wunddoku.domain.model.Signature +import de.medapp.wunddoku.pdf.AndroidPdfImageSharer import de.medapp.wunddoku.pdf.AndroidPdfOpener +import de.medapp.wunddoku.pdf.PdfImageShareResult import de.medapp.wunddoku.pdf.PdfOpenResult import de.medapp.wunddoku.pdf.toUserMessage import de.medapp.wunddoku.ui.documentation.edit.signature.AddSignatureDialog @@ -137,6 +140,7 @@ fun DocumentationEditScreen( // Starten einer externen Activity ist eine UI-/Plattform-Aktion, keine Repository-Aufgabe. val context = LocalContext.current val pdfOpener = remember(context) { AndroidPdfOpener(context) } + val pdfImageSharer = remember(context) { AndroidPdfImageSharer(context) } val snackbarHostState = remember { SnackbarHostState() } LaunchedEffect(uiState.pdfExportMessage) { val message = uiState.pdfExportMessage ?: return@LaunchedEffect @@ -157,6 +161,24 @@ fun DocumentationEditScreen( viewModel.dismissPdfExportMessage() } + // Änderungswunsch: erzeugte Bilder direkt über den Android-"Senden an"-Dialog verschicken + // können. Sobald das ViewModel frische JPEG-Seiten bereitstellt (s. + // DocumentationEditViewModel.exportAndShareAsImages), öffnet dieser Effekt den System- + // Share-Dialog - Intent/Activity-Start ist bewusst UI-Aufgabe, nicht Sache des ViewModels. + LaunchedEffect(uiState.imagesToShare) { + val images = uiState.imagesToShare ?: return@LaunchedEffect + when (val shareResult = pdfImageSharer.share(images)) { + PdfImageShareResult.Success -> Unit + is PdfImageShareResult.Failure -> snackbarHostState.showSnackbar(shareResult.toUserMessage()) + } + viewModel.onImageShareHandled() + } + LaunchedEffect(uiState.imageShareMessage) { + val message = uiState.imageShareMessage ?: return@LaunchedEffect + snackbarHostState.showSnackbar(message) + viewModel.dismissImageShareMessage() + } + Scaffold( snackbarHost = { SnackbarHost(snackbarHostState) }, topBar = { @@ -175,9 +197,8 @@ fun DocumentationEditScreen( } }, actions = { - // PDF-005 (Öffnen)/PDF-006 (Teilen) folgen später - dieser Button erzeugt - // ausschließlich die Datei im internen Speicher (PDF-001/002), als - // minimaler Trigger zum manuellen Testen der Export-Funktionalität. + // PDF-005 (Öffnen) ist umgesetzt (s. LaunchedEffect oben); dieser Button + // erzeugt die Datei im internen Speicher (PDF-001/002). if (uiState.pdfExportInProgress) { CircularProgressIndicator(modifier = Modifier.size(24.dp), strokeWidth = 2.dp) } else { @@ -185,6 +206,16 @@ fun DocumentationEditScreen( Icon(Icons.Filled.PictureAsPdf, contentDescription = "PDF erzeugen") } } + // Änderungswunsch: PDF zusätzlich als JPEG speichern/versenden. Exportiert + // intern frisch als PDF, wandelt seitenweise in JPEG um und öffnet + // anschließend automatisch den "Senden an"-Dialog (s. LaunchedEffect oben). + if (uiState.imageShareInProgress) { + CircularProgressIndicator(modifier = Modifier.size(24.dp), strokeWidth = 2.dp) + } else { + IconButton(onClick = viewModel::exportAndShareAsImages) { + Icon(Icons.Filled.Share, contentDescription = "Als Bilder senden") + } + } // "Fertig" beendet die Bearbeitung bereits jetzt (Inhalte sind ohnehin per // Autosave gesichert, kein expliziter Speichern-Button nötig). Button(onClick = onFinished) { Text("Fertig") } diff --git a/app/src/main/java/de/medapp/wunddoku/ui/documentation/edit/DocumentationEditViewModel.kt b/app/src/main/java/de/medapp/wunddoku/ui/documentation/edit/DocumentationEditViewModel.kt index 5ed53fd..8afda87 100644 --- a/app/src/main/java/de/medapp/wunddoku/ui/documentation/edit/DocumentationEditViewModel.kt +++ b/app/src/main/java/de/medapp/wunddoku/ui/documentation/edit/DocumentationEditViewModel.kt @@ -15,6 +15,8 @@ import de.medapp.wunddoku.domain.model.Documentation import de.medapp.wunddoku.domain.model.WoundCarer import de.medapp.wunddoku.pdf.PdfExportError import de.medapp.wunddoku.pdf.PdfExportResult +import de.medapp.wunddoku.pdf.PdfToImagesResult +import de.medapp.wunddoku.pdf.toUserMessage import de.medapp.wunddoku.ui.navigation.Screen import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.MutableSharedFlow @@ -63,6 +65,20 @@ data class DocumentationEditUiState( * referenziert werden kann, während ein neuer Export läuft. */ val lastExportedPdfFile: File? = null, + /** true, während PDF-Export + JPEG-Konvertierung für den Bilderversand laufen (ein + * gemeinsamer Ladezustand für beide Schritte, s. [DocumentationEditViewModel.exportAndShareAsImages]). */ + val imageShareInProgress: Boolean = false, + /** Fehlermeldung, falls Export oder Konvertierung fehlgeschlagen sind - erfolgreich + * erzeugte Bilder lösen dagegen KEINE Meldung aus, sondern direkt [imagesToShare]. */ + val imageShareMessage: String? = null, + /** + * Frisch erzeugte JPEG-Seiten, bereit zum Teilen (Änderungswunsch: "alle generierten Bilder + * über 'Senden an' direkt verschicken können"). Wird vom Screen per LaunchedEffect + * beobachtet, der daraufhin den System-"Senden an"-Dialog öffnet und anschließend + * [DocumentationEditViewModel.onImageShareHandled] aufruft - das Teilen selbst (Intent/ + * Activity-Start) ist bewusst UI-Aufgabe, nicht Sache des ViewModels. + */ + val imagesToShare: List? = null, ) { val blockCount: Int get() = documentation?.blocks?.size ?: 0 @@ -357,6 +373,63 @@ class DocumentationEditViewModel( _uiState.update { it.copy(pdfExportMessage = null) } } + /** + * Exportiert die Dokumentation frisch als PDF und wandelt das Ergebnis anschließend + * seitenweise in JPEG-Bilder um (Änderungswunsch: "PDF zusätzlich als JPEG speichern/senden + * können"). Bewusst ein eigener, vollständiger Export-Lauf statt Wiederverwendung von + * [uiState.lastExportedPdfFile]: so spiegeln die Bilder IMMER den aktuellen Stand wider, + * unabhängig davon, ob/wann zuvor schon einmal "PDF erzeugen" angetippt wurde. + * + * Das eigentliche Teilen (Android-"Senden an"-Dialog) übernimmt NICHT diese Methode, sondern + * der Screen anhand von [DocumentationEditUiState.imagesToShare] - Intent/Activity-Start ist + * UI-Aufgabe (s. [de.medapp.wunddoku.pdf.PdfImageSharer]). + */ + fun exportAndShareAsImages() { + viewModelScope.launch { + _uiState.update { + it.copy(imageShareInProgress = true, imageShareMessage = null, imagesToShare = null) + } + + val exportResult = repository.exportPdf(documentationId) + val pdfFile = when (exportResult) { + is PdfExportResult.Success -> exportResult.file + is PdfExportResult.Failure -> { + _uiState.update { + it.copy( + imageShareInProgress = false, + imageShareMessage = "PDF-Erzeugung fehlgeschlagen: ${exportResult.error.toUserMessage()}", + ) + } + return@launch + } + } + + when (val imagesResult = repository.convertPdfToImages(documentationId, pdfFile)) { + is PdfToImagesResult.Success -> _uiState.update { + it.copy(imageShareInProgress = false, imagesToShare = imagesResult.imageFiles) + } + + is PdfToImagesResult.Failure -> _uiState.update { + it.copy( + imageShareInProgress = false, + imageShareMessage = "Bildexport fehlgeschlagen: ${imagesResult.toUserMessage()}", + ) + } + } + } + } + + /** Wird vom Screen aufgerufen, nachdem der "Senden an"-Dialog für [DocumentationEditUiState.imagesToShare] + * geöffnet wurde (erfolgreich oder nicht) - verhindert ein erneutes Öffnen bei der nächsten + * Recomposition, solange keine neuen Bilder erzeugt wurden. */ + fun onImageShareHandled() { + _uiState.update { it.copy(imagesToShare = null) } + } + + fun dismissImageShareMessage() { + _uiState.update { it.copy(imageShareMessage = null) } + } + private fun PdfExportError.toUserMessage(): String = when (this) { PdfExportError.DocumentationNotFound -> "Die Dokumentation wurde nicht gefunden." is PdfExportError.DirectoryCreationFailed -> "Zielverzeichnis konnte nicht angelegt werden." diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml index fd6169d..c5bfa87 100644 --- a/app/src/main/res/xml/file_paths.xml +++ b/app/src/main/res/xml/file_paths.xml @@ -7,6 +7,7 @@ --> +