Export PDF to preferred dir if it was defined
This commit is contained in:
committed by
pynicolas
parent
7c9267a866
commit
53c9bc3630
@@ -117,6 +117,7 @@ dependencies {
|
||||
implementation(libs.androidx.camera.view)
|
||||
implementation(libs.androidx.datastore)
|
||||
implementation(libs.androidx.datastore.preferences)
|
||||
implementation(libs.androidx.documentfile)
|
||||
implementation(libs.protobuf.javalite)
|
||||
implementation(libs.litert)
|
||||
implementation(libs.litert.support)
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.fairscan.app
|
||||
import android.Manifest
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ClipData
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
@@ -297,13 +298,15 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
private fun openPdf(fileUri: Uri?) {
|
||||
if (fileUri == null) return
|
||||
val uri = FileProvider.getUriForFile(
|
||||
this,
|
||||
"${applicationContext.packageName}.fileprovider",
|
||||
fileUri.toFile()
|
||||
)
|
||||
val uriToOpen: Uri =
|
||||
if (fileUri.scheme == ContentResolver.SCHEME_CONTENT) {
|
||||
fileUri
|
||||
} else {
|
||||
val authority = "${applicationContext.packageName}.fileprovider"
|
||||
FileProvider.getUriForFile(this, authority, fileUri.toFile())
|
||||
}
|
||||
val openIntent = Intent(Intent.ACTION_VIEW).apply {
|
||||
setDataAndType(uri, PDF_MIME_TYPE)
|
||||
setDataAndType(uriToOpen, PDF_MIME_TYPE)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -221,7 +221,7 @@ private fun TextFieldAndPdfInfos(
|
||||
}
|
||||
|
||||
if (uiState.savedFileUri != null) {
|
||||
SavedPdfBar(onOpen)
|
||||
SavedPdfBar(uiState, onOpen)
|
||||
}
|
||||
if (uiState.errorMessage != null) {
|
||||
ErrorBar(uiState.errorMessage)
|
||||
@@ -335,7 +335,8 @@ fun ExportButton(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SavedPdfBar(onOpen: () -> Unit) {
|
||||
private fun SavedPdfBar(uiState: PdfGenerationUiState, onOpen: () -> Unit) {
|
||||
val dirName = uiState.exportDirName?:stringResource(R.string.download_dirname)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Absolute.SpaceBetween,
|
||||
@@ -345,7 +346,7 @@ private fun SavedPdfBar(onOpen: () -> Unit) {
|
||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.pdf_saved_to),
|
||||
text = stringResource(R.string.pdf_saved_to, dirName),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
@@ -428,6 +429,7 @@ fun PreviewExportScreenAfterSaveHorizontal() {
|
||||
uiState = PdfGenerationUiState(
|
||||
generatedPdf = GeneratedPdf(file, 442897L, 3),
|
||||
savedFileUri = file.toUri(),
|
||||
exportDirName = "MyVeryVeryLongDirectoryName"
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ data class PdfGenerationUiState(
|
||||
val generatedPdf: GeneratedPdf? = null,
|
||||
val desiredFilename: String = "",
|
||||
val savedFileUri: Uri? = null,
|
||||
val exportDirName: String? = null,
|
||||
val hasSharedPdf: Boolean = false,
|
||||
val errorMessage: String? = null,
|
||||
) {
|
||||
|
||||
@@ -16,7 +16,9 @@ package org.fairscan.app.ui.screens.export
|
||||
|
||||
import android.content.Context
|
||||
import android.media.MediaScannerConnection
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -27,6 +29,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
@@ -36,6 +39,7 @@ import org.fairscan.app.data.GeneratedPdf
|
||||
import org.fairscan.app.data.PdfFileManager
|
||||
import org.fairscan.app.ui.screens.home.HomeViewModel
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
|
||||
private const val PDF_MIME_TYPE = "application/pdf"
|
||||
|
||||
@@ -48,6 +52,7 @@ class ExportViewModel(container: AppContainer): ViewModel() {
|
||||
|
||||
private val pdfFileManager = container.pdfFileManager
|
||||
private val imageRepository = container.imageRepository
|
||||
private val settingsRepository = container.settingsRepository
|
||||
private val logger = container.logger
|
||||
|
||||
private val _events = MutableSharedFlow<ExportEvent>()
|
||||
@@ -121,12 +126,6 @@ class ExportViewModel(container: AppContainer): ViewModel() {
|
||||
return _pdfUiState.value.generatedPdf
|
||||
}
|
||||
|
||||
fun saveFile(pdfFile: File): File {
|
||||
val copiedFile = pdfFileManager.copyToExternalDir(pdfFile)
|
||||
_pdfUiState.update { it.copy(savedFileUri = copiedFile.toUri()) }
|
||||
return copiedFile
|
||||
}
|
||||
|
||||
fun onSavePdfClicked() {
|
||||
viewModelScope.launch {
|
||||
_events.emit(ExportEvent.RequestSavePdf)
|
||||
@@ -142,13 +141,30 @@ class ExportViewModel(container: AppContainer): ViewModel() {
|
||||
private suspend fun performPdfSave(context: Context, homeViewModel: HomeViewModel) {
|
||||
try {
|
||||
val pdf = getFinalPdf() ?: return
|
||||
val targetFile = saveFile(pdf.file)
|
||||
|
||||
mediaScan(context, targetFile)
|
||||
val exportDir = settingsRepository.exportDirUri.first()
|
||||
var fileInDownloads: File? = null
|
||||
|
||||
val savedUri: Uri =
|
||||
if (exportDir == null) {
|
||||
fileInDownloads = pdfFileManager.copyToExternalDir(pdf.file)
|
||||
fileInDownloads.toUri()
|
||||
} else {
|
||||
copyViaSaf(context, pdf.file, exportDir.toUri())
|
||||
}
|
||||
|
||||
_pdfUiState.update {
|
||||
it.copy(
|
||||
savedFileUri = savedUri,
|
||||
exportDirName = resolveExportDirName(context, exportDir?.toUri()))
|
||||
}
|
||||
|
||||
fileInDownloads?.let { mediaScan(context, it) }
|
||||
|
||||
// TODO remove that call: that should be handled through the ExportEvent
|
||||
homeViewModel.addRecentDocument(
|
||||
targetFile.absolutePath,
|
||||
// FIXME This is not a file path
|
||||
savedUri.toString(),
|
||||
pdf.pageCount
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
@@ -167,10 +183,40 @@ class ExportViewModel(container: AppContainer): ViewModel() {
|
||||
) { _, _ -> continuation.resume(Unit) {} }
|
||||
}
|
||||
|
||||
private fun copyViaSaf(
|
||||
context: Context,
|
||||
source: File,
|
||||
exportDirUri: Uri,
|
||||
): Uri {
|
||||
val resolver = context.contentResolver
|
||||
|
||||
val tree = DocumentFile.fromTreeUri(context, exportDirUri)
|
||||
?: throw IllegalStateException("Invalid SAF directory")
|
||||
|
||||
// Name collisions are handled automatically by SAF provider
|
||||
val target = tree.createFile(PDF_MIME_TYPE, source.name)
|
||||
?: throw IllegalStateException("Unable to create SAF file")
|
||||
|
||||
resolver.openOutputStream(target.uri)?.use { output ->
|
||||
FileInputStream(source).use { input ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
} ?: throw IllegalStateException("Failed to open SAF output stream")
|
||||
|
||||
return target.uri
|
||||
}
|
||||
|
||||
fun cleanUpOldPdfs(thresholdInMillis: Int) {
|
||||
pdfFileManager.cleanUpOldFiles(thresholdInMillis)
|
||||
}
|
||||
|
||||
private fun resolveExportDirName(context: Context, exportDirUri: Uri?): String? {
|
||||
return if (exportDirUri == null) {
|
||||
null
|
||||
} else {
|
||||
DocumentFile.fromTreeUri(context, exportDirUri)?.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class PdfGenerationActions(
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<string name="delete_page_warning">Chcete smazat tuto stránku?</string>
|
||||
<string name="developer">Vývojář</string>
|
||||
<string name="discard_scan">Zrušit skenování</string>
|
||||
<string name="download_dirname">stažených</string>
|
||||
<string name="end_scan">Ukončit skenování</string>
|
||||
<string name="error">Chyba: %1$s</string>
|
||||
<string name="error_no_document">Nebyl rozpoznán žádná dokument</string>
|
||||
@@ -33,7 +34,7 @@
|
||||
<string name="new_document_warning">Toto skenování bude ztraceno. Chcete pokračovat?</string>
|
||||
<string name="open">Otevřít</string>
|
||||
<string name="open_pdf">Otevřít PDF</string>
|
||||
<string name="pdf_saved_to">PDF bylo uloženo do stažených</string>
|
||||
<string name="pdf_saved_to">PDF bylo uloženo do %1s</string>
|
||||
<string name="resume">Obnovit</string>
|
||||
<string name="save">Uložit</string>
|
||||
<string name="scan_button">Nové skenování</string>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<string name="delete_page_warning">Möchten Sie diese Seite löschen?</string>
|
||||
<string name="developer">Entwickler</string>
|
||||
<string name="discard_scan">Löschen</string>
|
||||
<string name="download_dirname">Downloads</string>
|
||||
<string name="end_scan">Scan beenden</string>
|
||||
<string name="error">Fehler: %1$s</string>
|
||||
<string name="error_no_document">Kein Dokument erkannt</string>
|
||||
@@ -33,7 +34,7 @@
|
||||
<string name="new_document_warning">Das aktuelle Dokument geht verloren. Möchten Sie fortfahren?</string>
|
||||
<string name="open">Öffnen</string>
|
||||
<string name="open_pdf">PDF öffnen</string>
|
||||
<string name="pdf_saved_to">PDF gespeichert in Downloads</string>
|
||||
<string name="pdf_saved_to">PDF gespeichert in %1s</string>
|
||||
<string name="resume">Fortsetzen</string>
|
||||
<string name="save">Speichern</string>
|
||||
<string name="scan_button">Neuer Scan</string>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<string name="delete_page_warning">¿Quieres eliminar esta página?</string>
|
||||
<string name="developer">Desarrollador</string>
|
||||
<string name="discard_scan">Descartar escaneo</string>
|
||||
<string name="download_dirname">Descargas</string>
|
||||
<string name="end_scan">Finalizar escaneo</string>
|
||||
<string name="error">Error: %1$s</string>
|
||||
<string name="error_no_document">No se detectó ningún documento</string>
|
||||
@@ -33,7 +34,7 @@
|
||||
<string name="new_document_warning">El escaneo actual se perderá. ¿Deseas continuar?</string>
|
||||
<string name="open">Abrir</string>
|
||||
<string name="open_pdf">Abrir PDF</string>
|
||||
<string name="pdf_saved_to">PDF guardado en Descargas</string>
|
||||
<string name="pdf_saved_to">PDF guardado en %1s</string>
|
||||
<string name="resume">Reanudar</string>
|
||||
<string name="save">Guardar</string>
|
||||
<string name="scan_button">Nuevo escaneo</string>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<string name="delete_page_warning">Voulez-vous supprimer cette page ?</string>
|
||||
<string name="developer">Développeur</string>
|
||||
<string name="discard_scan">Supprimer le scan</string>
|
||||
<string name="download_dirname">Téléchargements</string>
|
||||
<string name="end_scan">Terminer le scan</string>
|
||||
<string name="error">Erreur : %1$s</string>
|
||||
<string name="error_no_document">Aucun document détecté</string>
|
||||
@@ -33,7 +34,7 @@
|
||||
<string name="new_document_warning">Le scan en cours sera perdu. Voulez-vous continuer ?</string>
|
||||
<string name="open">Ouvrir</string>
|
||||
<string name="open_pdf">Ouvrir le PDF</string>
|
||||
<string name="pdf_saved_to">PDF enregistré dans Téléchargements</string>
|
||||
<string name="pdf_saved_to">PDF enregistré dans %1s</string>
|
||||
<string name="resume">Reprendre</string>
|
||||
<string name="save">Enregistrer</string>
|
||||
<string name="scan_button">Nouveau scan</string>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<string name="delete_page_warning">Vuoi eliminare questa pagina?</string>
|
||||
<string name="developer">Sviluppatore</string>
|
||||
<string name="discard_scan">Scarta scansione</string>
|
||||
<string name="download_dirname">Download</string>
|
||||
<string name="end_scan">Termina scansione</string>
|
||||
<string name="error">Errore: %1$s</string>
|
||||
<string name="error_no_document">Nessun documento rilevato</string>
|
||||
@@ -33,7 +34,7 @@
|
||||
<string name="new_document_warning">La scansiona attuale verrà persa. Vuoi continuare?</string>
|
||||
<string name="open">Apri</string>
|
||||
<string name="open_pdf">Apri PDF</string>
|
||||
<string name="pdf_saved_to">PDF salvato in Download</string>
|
||||
<string name="pdf_saved_to">PDF salvato in %1s</string>
|
||||
<string name="resume">Riprendi</string>
|
||||
<string name="save">Salva</string>
|
||||
<string name="scan_button">Nuova scansione</string>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<string name="delete_page_warning">Deseja excluir esta página?</string>
|
||||
<string name="developer">Desenvolvedor</string>
|
||||
<string name="discard_scan">Descartar digitalização</string>
|
||||
<string name="download_dirname">Downloads</string>
|
||||
<string name="end_scan">Finalizar digitalização</string>
|
||||
<string name="error">Erro: %1$s</string>
|
||||
<string name="error_no_document">Nenhum documento detectado</string>
|
||||
@@ -33,7 +34,7 @@
|
||||
<string name="new_document_warning">A digitalização atual será perdida. Deseja continuar?</string>
|
||||
<string name="open">Abrir</string>
|
||||
<string name="open_pdf">Abrir PDF</string>
|
||||
<string name="pdf_saved_to">PDF salvo em Downloads</string>
|
||||
<string name="pdf_saved_to">PDF salvo em %1s</string>
|
||||
<string name="resume">Retomar</string>
|
||||
<string name="save">Salvar</string>
|
||||
<string name="scan_button">Nova digitalização</string>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<string name="delete_page_warning">Вы желаете удалить эту страницу?</string>
|
||||
<string name="developer">Разработчик</string>
|
||||
<string name="discard_scan">Отказаться</string>
|
||||
<string name="download_dirname">Download</string>
|
||||
<string name="end_scan">Закончить</string>
|
||||
<string name="error">Ошибка: %1$s</string>
|
||||
<string name="error_no_document">Документ не обнаружен</string>
|
||||
@@ -33,7 +34,7 @@
|
||||
<string name="new_document_warning">Результаты текущего сканирования будут потеряны. Желаете продолжить?</string>
|
||||
<string name="open">Открыть</string>
|
||||
<string name="open_pdf">Открыть PDF</string>
|
||||
<string name="pdf_saved_to">PDF сохранен в Download</string>
|
||||
<string name="pdf_saved_to">PDF сохранен в %1s</string>
|
||||
<string name="resume">Продолжить</string>
|
||||
<string name="save">Сохранить</string>
|
||||
<string name="scan_button">Начать</string>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<string name="delete_page_warning">是否要删除此页面?</string>
|
||||
<string name="developer">开发者</string>
|
||||
<string name="discard_scan">放弃扫描</string>
|
||||
<string name="download_dirname">下载</string>
|
||||
<string name="end_scan">结束扫描</string>
|
||||
<string name="error">错误: %1$s</string>
|
||||
<string name="error_no_document">未检测到任何文档</string>
|
||||
@@ -33,7 +34,7 @@
|
||||
<string name="new_document_warning">当前扫描将丢失。是否继续?</string>
|
||||
<string name="open">打开</string>
|
||||
<string name="open_pdf">打开 PDF</string>
|
||||
<string name="pdf_saved_to">PDF 保存到</string>
|
||||
<string name="pdf_saved_to">PDF 已保存到 %1$s</string>
|
||||
<string name="resume">恢复</string>
|
||||
<string name="save">保存</string>
|
||||
<string name="scan_button">新建扫描</string>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<string name="delete_page_warning">Do you want to delete this page?</string>
|
||||
<string name="developer">Developer</string>
|
||||
<string name="discard_scan">Discard scan</string>
|
||||
<string name="download_dirname">Downloads</string>
|
||||
<string name="end_scan">End scan</string>
|
||||
<string name="error">Error: %1$s</string>
|
||||
<string name="error_no_document">No document detected</string>
|
||||
@@ -34,7 +35,7 @@
|
||||
<string name="new_document_warning">The current scan will be lost. Do you want to continue?</string>
|
||||
<string name="open">Open</string>
|
||||
<string name="open_pdf">Open PDF</string>
|
||||
<string name="pdf_saved_to">PDF saved in Downloads</string>
|
||||
<string name="pdf_saved_to">PDF saved in %1s</string>
|
||||
<string name="resume">Resume</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="scan_button">New Scan</string>
|
||||
|
||||
Reference in New Issue
Block a user