diff --git a/app/src/main/java/org/fairscan/app/MainActivity.kt b/app/src/main/java/org/fairscan/app/MainActivity.kt index e7a7bea..cea3b70 100644 --- a/app/src/main/java/org/fairscan/app/MainActivity.kt +++ b/app/src/main/java/org/fairscan/app/MainActivity.kt @@ -187,7 +187,7 @@ class MainActivity : ComponentActivity() { pdfActions = ExportActions( prepareExportIfNeeded = exportViewModel::prepareExportIfNeeded, setFilename = exportViewModel::setFilename, - share = { share(exportViewModel.applyRenaming(), exportViewModel) }, + share = { exportViewModel.onShareClicked() }, save = { exportViewModel.onSaveClicked() }, open = { item -> openUri(item.uri, item.format.mimeType) }, ), @@ -324,6 +324,9 @@ class MainActivity : ComponentActivity() { exportViewModel.onRequestSave(context) } } + is ExportEvent.Share -> { + share(event.result) + } } } } @@ -343,10 +346,8 @@ class MainActivity : ComponentActivity() { } } - private fun share(result: ExportResult?, viewModel: ExportViewModel) { - if (result == null || result.files.isEmpty()) return - - viewModel.setAsShared() + private fun share(result: ExportResult) { + if (result.files.isEmpty()) return val uris = result.files.map(::uriForFile) val intent = Intent().apply { diff --git a/app/src/main/java/org/fairscan/app/ui/screens/export/ExportScreen.kt b/app/src/main/java/org/fairscan/app/ui/screens/export/ExportScreen.kt index ef90f85..77d9c93 100644 --- a/app/src/main/java/org/fairscan/app/ui/screens/export/ExportScreen.kt +++ b/app/src/main/java/org/fairscan/app/ui/screens/export/ExportScreen.kt @@ -584,7 +584,7 @@ private fun ErrorBar(error: ExportError) { @Composable private fun ExportError.toDisplayText(): Pair { return when (this) { - is ExportError.OnPrepare -> { + is ExportError.OnPrepareOrShare -> { val summary = message val details = throwable.message summary to details @@ -684,7 +684,7 @@ fun PreviewExportScreenAfterSave() { fun ExportScreenPreviewWithError() { ExportPreviewToCustomize( ExportUiState(error = - ExportError.OnPrepare("PDF generation failed", IOException("Boom"))) + ExportError.OnPrepareOrShare("PDF generation failed", IOException("Boom"))) ) } diff --git a/app/src/main/java/org/fairscan/app/ui/screens/export/ExportUiState.kt b/app/src/main/java/org/fairscan/app/ui/screens/export/ExportUiState.kt index 59c6e26..f456562 100644 --- a/app/src/main/java/org/fairscan/app/ui/screens/export/ExportUiState.kt +++ b/app/src/main/java/org/fairscan/app/ui/screens/export/ExportUiState.kt @@ -48,7 +48,7 @@ data class SaveDir( sealed class ExportError { - data class OnPrepare( + data class OnPrepareOrShare( val message: String, val throwable: Throwable, ) : ExportError() diff --git a/app/src/main/java/org/fairscan/app/ui/screens/export/ExportViewModel.kt b/app/src/main/java/org/fairscan/app/ui/screens/export/ExportViewModel.kt index 4f06914..ce8d073 100644 --- a/app/src/main/java/org/fairscan/app/ui/screens/export/ExportViewModel.kt +++ b/app/src/main/java/org/fairscan/app/ui/screens/export/ExportViewModel.kt @@ -61,6 +61,7 @@ import kotlin.coroutines.suspendCoroutine sealed interface ExportEvent { data object RequestSave : ExportEvent + data class Share(val result: ExportResult) : ExportEvent } class ExportViewModel(container: AppContainer, val imageRepository: ImageRepository): ViewModel() { @@ -160,7 +161,7 @@ class ExportViewModel(container: AppContainer, val imageRepository: ImageReposit val message = "Failed to prepare $exportFormat export" logger.e("FairScan", message, e) _uiState.update { - it.copy(error = ExportError.OnPrepare(message, e)) + it.copy(error = ExportError.OnPrepareOrShare(message, e)) } } finally { _uiState.update { it.copy(isGenerating = false) } @@ -184,29 +185,27 @@ class ExportViewModel(container: AppContainer, val imageRepository: ImageReposit ExportResult.Jpeg(files, sizeInBytes) } - fun setAsShared() { - _uiState.update { it.copy(hasShared = true) } + private fun renameFile(source: File, target: File) { + if (source.absolutePath == target.absolutePath) return + if (target.exists() && !target.delete()) { + throw IOException("Cannot delete existing file ${target.absolutePath}") + } + if (!source.renameTo(target)) { + throw IOException("Failed to rename ${source.name} to ${target.name}") + } } - fun applyRenaming(): ExportResult? { - val result = _uiState.value.result ?: return null + private fun applyRenaming(): ExportResult { + val result = _uiState.value.result + ?: throw IllegalStateException("Export result missing") ensureValidFilename() val filename = _uiState.value.filename - when (result) { + val updated = when (result) { is ExportResult.Pdf -> { val fileName = FileManager.addPdfExtensionIfMissing(filename) val newFile = File(result.file.parentFile, fileName) - val tempFile = result.file - if (tempFile.absolutePath != newFile.absolutePath) { - if (newFile.exists()) newFile.delete() - val success = tempFile.renameTo(newFile) - if (!success) return null - _uiState.update { - it.copy(result = ExportResult.Pdf( - newFile, result.sizeInBytes, result.pageCount) - ) - } - } + renameFile(result.file, newFile) + ExportResult.Pdf(newFile, result.sizeInBytes, result.pageCount) } is ExportResult.Jpeg -> { val base = filename.removeSuffix(".jpg") @@ -214,17 +213,28 @@ class ExportViewModel(container: AppContainer, val imageRepository: ImageReposit val renamedFiles = files.mapIndexed { index, file -> val indexSuffix = if (files.size == 1) "" else "_${index + 1}" val newFile = File(file.parentFile, "${base}${indexSuffix}.jpg") - if (file.absolutePath != newFile.absolutePath) { - if (newFile.exists()) newFile.delete() - file.renameTo(newFile) - } + renameFile(file, newFile) newFile } - val updated = result.copy(jpegFiles = renamedFiles) - _uiState.update { it.copy(result = updated) } + result.copy(jpegFiles = renamedFiles) + } + } + _uiState.update { it.copy(result = updated) } + return updated + } + + fun onShareClicked() { + viewModelScope.launch { + try { + val result = applyRenaming() + _events.emit(ExportEvent.Share(result)) + _uiState.update { it.copy(hasShared = true) } + } catch (e: Exception) { + val message = "Failed to prepare share" + logger.e("FairScan", message, e) + _uiState.update { it.copy(error = ExportError.OnPrepareOrShare(message, e)) } } } - return _uiState.value.result } fun onSaveClicked() {