MEDIA-005 #9
@@ -0,0 +1,72 @@
|
|||||||
|
package de.medapp.wunddoku.pdf
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Übergibt die fertige PDF-Datei selbst (statt in JPEG-Seiten umgewandelt, s.
|
||||||
|
* [PdfToImagesConverter]/[PdfImageSharer]) an den Android-"Senden an"-Dialog
|
||||||
|
* (`ACTION_SEND`, `application/pdf`) - Änderungswunsch: Nutzer soll beim Antippen des
|
||||||
|
* Teilen-Buttons wählen können, ob als JPG ODER als PDF geteilt wird.
|
||||||
|
*
|
||||||
|
* Bewusst getrennt von [PdfOpener] (öffnet dieselbe Datei stattdessen per `ACTION_VIEW` in
|
||||||
|
* einem PDF-Reader) - unterschiedliche Nutzerabsicht (ansehen vs. weitergeben), auch wenn
|
||||||
|
* beide dieselbe Datei über denselben FileProvider-Pfad freigeben.
|
||||||
|
*/
|
||||||
|
interface PdfSharer {
|
||||||
|
fun share(pdfFile: File): PdfShareResult
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface PdfShareResult {
|
||||||
|
data object Success : PdfShareResult
|
||||||
|
|
||||||
|
sealed interface Failure : PdfShareResult {
|
||||||
|
data class UriResolutionFailed(val message: String) : Failure
|
||||||
|
data object NoAppAvailable : Failure
|
||||||
|
data class Unexpected(val message: String) : Failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PdfShareResult.Failure.toUserMessage(): String = when (this) {
|
||||||
|
is PdfShareResult.Failure.UriResolutionFailed ->
|
||||||
|
"Die PDF-Datei konnte nicht zum Versenden freigegeben werden."
|
||||||
|
PdfShareResult.Failure.NoAppAvailable ->
|
||||||
|
"Auf diesem Gerät ist keine App zum Versenden von PDFs installiert."
|
||||||
|
is PdfShareResult.Failure.Unexpected ->
|
||||||
|
"Die PDF-Datei konnte nicht versendet werden."
|
||||||
|
}
|
||||||
|
|
||||||
|
class AndroidPdfSharer(private val context: Context) : PdfSharer {
|
||||||
|
|
||||||
|
override fun share(pdfFile: File): PdfShareResult {
|
||||||
|
val authority = "${context.packageName}.fileprovider"
|
||||||
|
val uri = try {
|
||||||
|
FileProvider.getUriForFile(context, authority, pdfFile)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
return PdfShareResult.Failure.UriResolutionFailed(e.message ?: "Uri konnte nicht erzeugt werden.")
|
||||||
|
}
|
||||||
|
|
||||||
|
val intent = Intent(Intent.ACTION_SEND).apply {
|
||||||
|
type = "application/pdf"
|
||||||
|
putExtra(Intent.EXTRA_STREAM, uri)
|
||||||
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}
|
||||||
|
val chooser = Intent.createChooser(intent, "PDF senden an")
|
||||||
|
|
||||||
|
return try {
|
||||||
|
context.startActivity(chooser)
|
||||||
|
PdfShareResult.Success
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
// Bewusst KEIN vorheriges intent.resolveActivity(...) - s. Begründung in
|
||||||
|
// AndroidPdfOpener.open() (Package-Visibility-Einschränkungen ab Android 11).
|
||||||
|
PdfShareResult.Failure.NoAppAvailable
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
PdfShareResult.Failure.Unexpected(e.message ?: "Zugriff auf die PDF-Datei wurde verweigert.")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
PdfShareResult.Failure.Unexpected(e.message ?: "Unbekannter Fehler beim Versenden der PDF-Datei.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+72
-10
@@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.Row
|
|||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
@@ -46,6 +47,7 @@ import androidx.compose.material3.Scaffold
|
|||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.SnackbarResult
|
import androidx.compose.material3.SnackbarResult
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
@@ -66,6 +68,7 @@ import androidx.compose.ui.layout.ContentScale
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleEventObserver
|
import androidx.lifecycle.LifecycleEventObserver
|
||||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
@@ -78,8 +81,10 @@ import de.medapp.wunddoku.domain.model.Photo
|
|||||||
import de.medapp.wunddoku.domain.model.Signature
|
import de.medapp.wunddoku.domain.model.Signature
|
||||||
import de.medapp.wunddoku.pdf.AndroidPdfImageSharer
|
import de.medapp.wunddoku.pdf.AndroidPdfImageSharer
|
||||||
import de.medapp.wunddoku.pdf.AndroidPdfOpener
|
import de.medapp.wunddoku.pdf.AndroidPdfOpener
|
||||||
|
import de.medapp.wunddoku.pdf.AndroidPdfSharer
|
||||||
import de.medapp.wunddoku.pdf.PdfImageShareResult
|
import de.medapp.wunddoku.pdf.PdfImageShareResult
|
||||||
import de.medapp.wunddoku.pdf.PdfOpenResult
|
import de.medapp.wunddoku.pdf.PdfOpenResult
|
||||||
|
import de.medapp.wunddoku.pdf.PdfShareResult
|
||||||
import de.medapp.wunddoku.pdf.toUserMessage
|
import de.medapp.wunddoku.pdf.toUserMessage
|
||||||
import de.medapp.wunddoku.ui.documentation.edit.signature.AddSignatureDialog
|
import de.medapp.wunddoku.ui.documentation.edit.signature.AddSignatureDialog
|
||||||
|
|
||||||
@@ -141,6 +146,7 @@ fun DocumentationEditScreen(
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val pdfOpener = remember(context) { AndroidPdfOpener(context) }
|
val pdfOpener = remember(context) { AndroidPdfOpener(context) }
|
||||||
val pdfImageSharer = remember(context) { AndroidPdfImageSharer(context) }
|
val pdfImageSharer = remember(context) { AndroidPdfImageSharer(context) }
|
||||||
|
val pdfSharer = remember(context) { AndroidPdfSharer(context) }
|
||||||
val snackbarHostState = remember { SnackbarHostState() }
|
val snackbarHostState = remember { SnackbarHostState() }
|
||||||
LaunchedEffect(uiState.pdfExportMessage) {
|
LaunchedEffect(uiState.pdfExportMessage) {
|
||||||
val message = uiState.pdfExportMessage ?: return@LaunchedEffect
|
val message = uiState.pdfExportMessage ?: return@LaunchedEffect
|
||||||
@@ -163,7 +169,7 @@ fun DocumentationEditScreen(
|
|||||||
|
|
||||||
// Änderungswunsch: erzeugte Bilder direkt über den Android-"Senden an"-Dialog verschicken
|
// Änderungswunsch: erzeugte Bilder direkt über den Android-"Senden an"-Dialog verschicken
|
||||||
// können. Sobald das ViewModel frische JPEG-Seiten bereitstellt (s.
|
// können. Sobald das ViewModel frische JPEG-Seiten bereitstellt (s.
|
||||||
// DocumentationEditViewModel.exportAndShareAsImages), öffnet dieser Effekt den System-
|
// DocumentationEditViewModel.shareAsImages), öffnet dieser Effekt den System-
|
||||||
// Share-Dialog - Intent/Activity-Start ist bewusst UI-Aufgabe, nicht Sache des ViewModels.
|
// Share-Dialog - Intent/Activity-Start ist bewusst UI-Aufgabe, nicht Sache des ViewModels.
|
||||||
LaunchedEffect(uiState.imagesToShare) {
|
LaunchedEffect(uiState.imagesToShare) {
|
||||||
val images = uiState.imagesToShare ?: return@LaunchedEffect
|
val images = uiState.imagesToShare ?: return@LaunchedEffect
|
||||||
@@ -173,10 +179,20 @@ fun DocumentationEditScreen(
|
|||||||
}
|
}
|
||||||
viewModel.onImageShareHandled()
|
viewModel.onImageShareHandled()
|
||||||
}
|
}
|
||||||
LaunchedEffect(uiState.imageShareMessage) {
|
// Analog zum Bilderversand oben, nur für die Format-Auswahl "PDF" (s.
|
||||||
val message = uiState.imageShareMessage ?: return@LaunchedEffect
|
// DocumentationEditViewModel.shareAsPdf).
|
||||||
|
LaunchedEffect(uiState.pdfToShare) {
|
||||||
|
val pdfFile = uiState.pdfToShare ?: return@LaunchedEffect
|
||||||
|
when (val shareResult = pdfSharer.share(pdfFile)) {
|
||||||
|
PdfShareResult.Success -> Unit
|
||||||
|
is PdfShareResult.Failure -> snackbarHostState.showSnackbar(shareResult.toUserMessage())
|
||||||
|
}
|
||||||
|
viewModel.onPdfShareHandled()
|
||||||
|
}
|
||||||
|
LaunchedEffect(uiState.shareMessage) {
|
||||||
|
val message = uiState.shareMessage ?: return@LaunchedEffect
|
||||||
snackbarHostState.showSnackbar(message)
|
snackbarHostState.showSnackbar(message)
|
||||||
viewModel.dismissImageShareMessage()
|
viewModel.dismissShareMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@@ -206,14 +222,14 @@ fun DocumentationEditScreen(
|
|||||||
Icon(Icons.Filled.PictureAsPdf, contentDescription = "PDF erzeugen")
|
Icon(Icons.Filled.PictureAsPdf, contentDescription = "PDF erzeugen")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Änderungswunsch: PDF zusätzlich als JPEG speichern/versenden. Exportiert
|
// Änderungswunsch: PDF zusätzlich als JPEG speichern/versenden, wahlweise
|
||||||
// intern frisch als PDF, wandelt seitenweise in JPEG um und öffnet
|
// auch als PDF selbst - Format wird per Dialog abgefragt (s. weiter unten),
|
||||||
// anschließend automatisch den "Senden an"-Dialog (s. LaunchedEffect oben).
|
// BEVOR irgendetwas exportiert wird.
|
||||||
if (uiState.imageShareInProgress) {
|
if (uiState.shareInProgress) {
|
||||||
CircularProgressIndicator(modifier = Modifier.size(24.dp), strokeWidth = 2.dp)
|
CircularProgressIndicator(modifier = Modifier.size(24.dp), strokeWidth = 2.dp)
|
||||||
} else {
|
} else {
|
||||||
IconButton(onClick = viewModel::exportAndShareAsImages) {
|
IconButton(onClick = viewModel::requestShareFormatChoice) {
|
||||||
Icon(Icons.Filled.Share, contentDescription = "Als Bilder senden")
|
Icon(Icons.Filled.Share, contentDescription = "Teilen")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// "Fertig" beendet die Bearbeitung bereits jetzt (Inhalte sind ohnehin per
|
// "Fertig" beendet die Bearbeitung bereits jetzt (Inhalte sind ohnehin per
|
||||||
@@ -346,6 +362,52 @@ fun DocumentationEditScreen(
|
|||||||
onDismiss = { viewedPhoto = null },
|
onDismiss = { viewedPhoto = null },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Änderungswunsch: Format-Auswahl beim Antippen des Teilen-Buttons.
|
||||||
|
if (uiState.showShareFormatDialog) {
|
||||||
|
ShareFormatDialog(
|
||||||
|
onDismiss = viewModel::dismissShareFormatDialog,
|
||||||
|
onChooseImages = viewModel::shareAsImages,
|
||||||
|
onChoosePdf = viewModel::shareAsPdf,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fragt beim Antippen des Teilen-Buttons ab, ob als JPG (Bilder, eine Datei pro Seite) oder als
|
||||||
|
* PDF (die Datei selbst) geteilt werden soll (Änderungswunsch). Erst NACH dieser Auswahl wird
|
||||||
|
* überhaupt exportiert (s. [DocumentationEditViewModel.shareAsImages]/[DocumentationEditViewModel.shareAsPdf]) -
|
||||||
|
* kein unnötiger Export, falls der Nutzer den Dialog stattdessen abbricht.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
private fun ShareFormatDialog(
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onChooseImages: () -> Unit,
|
||||||
|
onChoosePdf: () -> Unit,
|
||||||
|
) {
|
||||||
|
Dialog(onDismissRequest = onDismiss) {
|
||||||
|
Surface(shape = RoundedCornerShape(16.dp), color = MaterialTheme.colorScheme.surface) {
|
||||||
|
Column(modifier = Modifier.fillMaxWidth().padding(20.dp)) {
|
||||||
|
Text("Teilen als", style = MaterialTheme.typography.titleMedium)
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
Text(
|
||||||
|
"Als Bilder (JPG) oder als PDF teilen?",
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.End,
|
||||||
|
) {
|
||||||
|
TextButton(onClick = onDismiss) { Text(stringResource(R.string.action_cancel)) }
|
||||||
|
Spacer(Modifier.width(8.dp))
|
||||||
|
OutlinedButton(onClick = onChooseImages) { Text("JPG") }
|
||||||
|
Spacer(Modifier.width(8.dp))
|
||||||
|
Button(onClick = onChoosePdf) { Text("PDF") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+80
-15
@@ -65,12 +65,17 @@ data class DocumentationEditUiState(
|
|||||||
* referenziert werden kann, während ein neuer Export läuft.
|
* referenziert werden kann, während ein neuer Export läuft.
|
||||||
*/
|
*/
|
||||||
val lastExportedPdfFile: File? = null,
|
val lastExportedPdfFile: File? = null,
|
||||||
/** true, während PDF-Export + JPEG-Konvertierung für den Bilderversand laufen (ein
|
/** true, während PDF-Export + Konvertierung für den Versand laufen - gemeinsamer
|
||||||
* gemeinsamer Ladezustand für beide Schritte, s. [DocumentationEditViewModel.exportAndShareAsImages]). */
|
* Ladezustand für BEIDE Formate (JPG über [DocumentationEditViewModel.shareAsImages],
|
||||||
val imageShareInProgress: Boolean = false,
|
* PDF über [DocumentationEditViewModel.shareAsPdf]), da immer zuerst frisch exportiert
|
||||||
/** Fehlermeldung, falls Export oder Konvertierung fehlgeschlagen sind - erfolgreich
|
* wird. */
|
||||||
* erzeugte Bilder lösen dagegen KEINE Meldung aus, sondern direkt [imagesToShare]. */
|
val shareInProgress: Boolean = false,
|
||||||
val imageShareMessage: String? = null,
|
/** Fehlermeldung, falls Export oder Konvertierung fehlgeschlagen sind - ein erfolgreicher
|
||||||
|
* Export löst dagegen KEINE Meldung aus, sondern direkt [imagesToShare] bzw. [pdfToShare]. */
|
||||||
|
val shareMessage: String? = null,
|
||||||
|
/** true, solange die Auswahl "Als JPG oder PDF teilen?" angezeigt wird (Änderungswunsch:
|
||||||
|
* Format-Auswahl beim Antippen des Teilen-Buttons, bevor irgendetwas exportiert wird). */
|
||||||
|
val showShareFormatDialog: Boolean = false,
|
||||||
/**
|
/**
|
||||||
* Frisch erzeugte JPEG-Seiten, bereit zum Teilen (Änderungswunsch: "alle generierten Bilder
|
* Frisch erzeugte JPEG-Seiten, bereit zum Teilen (Änderungswunsch: "alle generierten Bilder
|
||||||
* über 'Senden an' direkt verschicken können"). Wird vom Screen per LaunchedEffect
|
* über 'Senden an' direkt verschicken können"). Wird vom Screen per LaunchedEffect
|
||||||
@@ -79,6 +84,9 @@ data class DocumentationEditUiState(
|
|||||||
* Activity-Start) ist bewusst UI-Aufgabe, nicht Sache des ViewModels.
|
* Activity-Start) ist bewusst UI-Aufgabe, nicht Sache des ViewModels.
|
||||||
*/
|
*/
|
||||||
val imagesToShare: List<File>? = null,
|
val imagesToShare: List<File>? = null,
|
||||||
|
/** Analog [imagesToShare], nur für die Variante "als PDF teilen" - Gegenstück
|
||||||
|
* [DocumentationEditViewModel.onPdfShareHandled]. */
|
||||||
|
val pdfToShare: File? = null,
|
||||||
) {
|
) {
|
||||||
val blockCount: Int get() = documentation?.blocks?.size ?: 0
|
val blockCount: Int get() = documentation?.blocks?.size ?: 0
|
||||||
|
|
||||||
@@ -373,6 +381,31 @@ class DocumentationEditViewModel(
|
|||||||
_uiState.update { it.copy(pdfExportMessage = null) }
|
_uiState.update { it.copy(pdfExportMessage = null) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Öffnet die Auswahl "Als JPG oder PDF teilen?" (Änderungswunsch: Format-Auswahl beim
|
||||||
|
* Antippen des Teilen-Buttons, BEVOR irgendetwas exportiert wird - vorher wurde beim
|
||||||
|
* Antippen sofort und ausschließlich als JPG geteilt).
|
||||||
|
*/
|
||||||
|
fun requestShareFormatChoice() {
|
||||||
|
_uiState.update { it.copy(showShareFormatDialog = true) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dismissShareFormatDialog() {
|
||||||
|
_uiState.update { it.copy(showShareFormatDialog = false) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Nutzer hat sich für "Als JPG teilen" entschieden. */
|
||||||
|
fun shareAsImages() {
|
||||||
|
_uiState.update { it.copy(showShareFormatDialog = false) }
|
||||||
|
exportAndShareAsImages()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Nutzer hat sich für "Als PDF teilen" entschieden. */
|
||||||
|
fun shareAsPdf() {
|
||||||
|
_uiState.update { it.copy(showShareFormatDialog = false) }
|
||||||
|
exportAndSharePdf()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exportiert die Dokumentation frisch als PDF und wandelt das Ergebnis anschließend
|
* 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
|
* seitenweise in JPEG-Bilder um (Änderungswunsch: "PDF zusätzlich als JPEG speichern/senden
|
||||||
@@ -384,10 +417,10 @@ class DocumentationEditViewModel(
|
|||||||
* der Screen anhand von [DocumentationEditUiState.imagesToShare] - Intent/Activity-Start ist
|
* der Screen anhand von [DocumentationEditUiState.imagesToShare] - Intent/Activity-Start ist
|
||||||
* UI-Aufgabe (s. [de.medapp.wunddoku.pdf.PdfImageSharer]).
|
* UI-Aufgabe (s. [de.medapp.wunddoku.pdf.PdfImageSharer]).
|
||||||
*/
|
*/
|
||||||
fun exportAndShareAsImages() {
|
private fun exportAndShareAsImages() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(imageShareInProgress = true, imageShareMessage = null, imagesToShare = null)
|
it.copy(shareInProgress = true, shareMessage = null, imagesToShare = null)
|
||||||
}
|
}
|
||||||
|
|
||||||
val exportResult = repository.exportPdf(documentationId)
|
val exportResult = repository.exportPdf(documentationId)
|
||||||
@@ -396,8 +429,8 @@ class DocumentationEditViewModel(
|
|||||||
is PdfExportResult.Failure -> {
|
is PdfExportResult.Failure -> {
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
imageShareInProgress = false,
|
shareInProgress = false,
|
||||||
imageShareMessage = "PDF-Erzeugung fehlgeschlagen: ${exportResult.error.toUserMessage()}",
|
shareMessage = "PDF-Erzeugung fehlgeschlagen: ${exportResult.error.toUserMessage()}",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return@launch
|
return@launch
|
||||||
@@ -406,13 +439,40 @@ class DocumentationEditViewModel(
|
|||||||
|
|
||||||
when (val imagesResult = repository.convertPdfToImages(documentationId, pdfFile)) {
|
when (val imagesResult = repository.convertPdfToImages(documentationId, pdfFile)) {
|
||||||
is PdfToImagesResult.Success -> _uiState.update {
|
is PdfToImagesResult.Success -> _uiState.update {
|
||||||
it.copy(imageShareInProgress = false, imagesToShare = imagesResult.imageFiles)
|
it.copy(shareInProgress = false, imagesToShare = imagesResult.imageFiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
is PdfToImagesResult.Failure -> _uiState.update {
|
is PdfToImagesResult.Failure -> _uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
imageShareInProgress = false,
|
shareInProgress = false,
|
||||||
imageShareMessage = "Bildexport fehlgeschlagen: ${imagesResult.toUserMessage()}",
|
shareMessage = "Bildexport fehlgeschlagen: ${imagesResult.toUserMessage()}",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exportiert die Dokumentation frisch als PDF und stellt sie zum Teilen bereit
|
||||||
|
* (Änderungswunsch: Format-Auswahl "PDF" statt "JPG") - ohne die JPEG-Konvertierung, die
|
||||||
|
* geteilte Datei ist die PDF selbst. Das eigentliche Teilen übernimmt der Screen anhand von
|
||||||
|
* [DocumentationEditUiState.pdfToShare] (s. [de.medapp.wunddoku.pdf.PdfSharer]).
|
||||||
|
*/
|
||||||
|
private fun exportAndSharePdf() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(shareInProgress = true, shareMessage = null, pdfToShare = null)
|
||||||
|
}
|
||||||
|
|
||||||
|
when (val exportResult = repository.exportPdf(documentationId)) {
|
||||||
|
is PdfExportResult.Success -> _uiState.update {
|
||||||
|
it.copy(shareInProgress = false, pdfToShare = exportResult.file)
|
||||||
|
}
|
||||||
|
|
||||||
|
is PdfExportResult.Failure -> _uiState.update {
|
||||||
|
it.copy(
|
||||||
|
shareInProgress = false,
|
||||||
|
shareMessage = "PDF-Erzeugung fehlgeschlagen: ${exportResult.error.toUserMessage()}",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -426,8 +486,13 @@ class DocumentationEditViewModel(
|
|||||||
_uiState.update { it.copy(imagesToShare = null) }
|
_uiState.update { it.copy(imagesToShare = null) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun dismissImageShareMessage() {
|
/** Analog [onImageShareHandled], nur für [DocumentationEditUiState.pdfToShare]. */
|
||||||
_uiState.update { it.copy(imageShareMessage = null) }
|
fun onPdfShareHandled() {
|
||||||
|
_uiState.update { it.copy(pdfToShare = null) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dismissShareMessage() {
|
||||||
|
_uiState.update { it.copy(shareMessage = null) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PdfExportError.toUserMessage(): String = when (this) {
|
private fun PdfExportError.toUserMessage(): String = when (this) {
|
||||||
|
|||||||
Reference in New Issue
Block a user