Allow export to JPEG (#68)
* SettingScreen: export format * Allow export to JPEG * Adjust messages in UI to work with PDF and JPEG * Message for file size should depend on number of files * Fix call to MediaScanner to avoid crash when scanning multiple files * Fix strange handling of Open button
This commit is contained in:
@@ -23,7 +23,7 @@ import androidx.lifecycle.viewmodel.CreationExtras
|
|||||||
import org.fairscan.app.data.FileLogger
|
import org.fairscan.app.data.FileLogger
|
||||||
import org.fairscan.app.data.ImageRepository
|
import org.fairscan.app.data.ImageRepository
|
||||||
import org.fairscan.app.data.LogRepository
|
import org.fairscan.app.data.LogRepository
|
||||||
import org.fairscan.app.data.PdfFileManager
|
import org.fairscan.app.data.FileManager
|
||||||
import org.fairscan.app.data.recentDocumentsDataStore
|
import org.fairscan.app.data.recentDocumentsDataStore
|
||||||
import org.fairscan.app.domain.ImageSegmentationService
|
import org.fairscan.app.domain.ImageSegmentationService
|
||||||
import org.fairscan.app.platform.AndroidPdfWriter
|
import org.fairscan.app.platform.AndroidPdfWriter
|
||||||
@@ -51,8 +51,9 @@ class AppContainer(context: Context) {
|
|||||||
private val density = context.resources.displayMetrics.density
|
private val density = context.resources.displayMetrics.density
|
||||||
private val thumbnailSizePx = (THUMBNAIL_SIZE_DP * density).toInt()
|
private val thumbnailSizePx = (THUMBNAIL_SIZE_DP * density).toInt()
|
||||||
val imageRepository = ImageRepository(context.filesDir, OpenCvTransformations(), thumbnailSizePx)
|
val imageRepository = ImageRepository(context.filesDir, OpenCvTransformations(), thumbnailSizePx)
|
||||||
val pdfFileManager = PdfFileManager(
|
val preparationDir = File(context.cacheDir, "pdfs")
|
||||||
File(context.cacheDir, "pdfs"),
|
val fileManager = FileManager(
|
||||||
|
preparationDir,
|
||||||
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
|
||||||
AndroidPdfWriter()
|
AndroidPdfWriter()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -45,12 +45,10 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.core.content.ContextCompat.checkSelfPermission
|
import androidx.core.content.ContextCompat.checkSelfPermission
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.core.net.toFile
|
import androidx.core.net.toFile
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.fairscan.app.data.GeneratedPdf
|
|
||||||
import org.fairscan.app.ui.Navigation
|
import org.fairscan.app.ui.Navigation
|
||||||
import org.fairscan.app.ui.Screen
|
import org.fairscan.app.ui.Screen
|
||||||
import org.fairscan.app.ui.components.rememberCameraPermissionState
|
import org.fairscan.app.ui.components.rememberCameraPermissionState
|
||||||
@@ -63,18 +61,18 @@ import org.fairscan.app.ui.screens.camera.CameraEvent
|
|||||||
import org.fairscan.app.ui.screens.camera.CameraScreen
|
import org.fairscan.app.ui.screens.camera.CameraScreen
|
||||||
import org.fairscan.app.ui.screens.camera.CameraViewModel
|
import org.fairscan.app.ui.screens.camera.CameraViewModel
|
||||||
import org.fairscan.app.ui.screens.export.ExportEvent
|
import org.fairscan.app.ui.screens.export.ExportEvent
|
||||||
|
import org.fairscan.app.ui.screens.export.ExportResult
|
||||||
import org.fairscan.app.ui.screens.export.ExportScreenWrapper
|
import org.fairscan.app.ui.screens.export.ExportScreenWrapper
|
||||||
import org.fairscan.app.ui.screens.export.ExportViewModel
|
import org.fairscan.app.ui.screens.export.ExportViewModel
|
||||||
import org.fairscan.app.ui.screens.export.PdfGenerationActions
|
import org.fairscan.app.ui.screens.export.ExportActions
|
||||||
import org.fairscan.app.ui.screens.home.HomeScreen
|
import org.fairscan.app.ui.screens.home.HomeScreen
|
||||||
import org.fairscan.app.ui.screens.home.HomeViewModel
|
import org.fairscan.app.ui.screens.home.HomeViewModel
|
||||||
|
import org.fairscan.app.ui.screens.settings.ExportFormat
|
||||||
import org.fairscan.app.ui.screens.settings.SettingsScreen
|
import org.fairscan.app.ui.screens.settings.SettingsScreen
|
||||||
import org.fairscan.app.ui.screens.settings.SettingsViewModel
|
import org.fairscan.app.ui.screens.settings.SettingsViewModel
|
||||||
import org.fairscan.app.ui.theme.FairScanTheme
|
import org.fairscan.app.ui.theme.FairScanTheme
|
||||||
import org.opencv.android.OpenCVLoader
|
import org.opencv.android.OpenCVLoader
|
||||||
|
|
||||||
private const val PDF_MIME_TYPE = "application/pdf"
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -89,7 +87,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
val settingsViewModel: SettingsViewModel
|
val settingsViewModel: SettingsViewModel
|
||||||
by viewModels { appContainer.settingsViewModelFactory }
|
by viewModels { appContainer.settingsViewModelFactory }
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
exportViewModel.cleanUpOldPdfs(1000 * 3600)
|
exportViewModel.cleanUpOldPreparedFiles(1000 * 3600)
|
||||||
}
|
}
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
@@ -97,6 +95,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
val currentScreen by viewModel.currentScreen.collectAsStateWithLifecycle()
|
val currentScreen by viewModel.currentScreen.collectAsStateWithLifecycle()
|
||||||
val liveAnalysisState by cameraViewModel.liveAnalysisState.collectAsStateWithLifecycle()
|
val liveAnalysisState by cameraViewModel.liveAnalysisState.collectAsStateWithLifecycle()
|
||||||
val document by viewModel.documentUiModel.collectAsStateWithLifecycle()
|
val document by viewModel.documentUiModel.collectAsStateWithLifecycle()
|
||||||
|
val exportUiState by exportViewModel.uiState.collectAsStateWithLifecycle()
|
||||||
val cameraPermission = rememberCameraPermissionState()
|
val cameraPermission = rememberCameraPermissionState()
|
||||||
CollectCameraEvents(cameraViewModel, viewModel)
|
CollectCameraEvents(cameraViewModel, viewModel)
|
||||||
CollectExportEvents(context, exportViewModel)
|
CollectExportEvents(context, exportViewModel)
|
||||||
@@ -113,7 +112,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
navigation = navigation,
|
navigation = navigation,
|
||||||
onClearScan = { viewModel.startNewDocument() },
|
onClearScan = { viewModel.startNewDocument() },
|
||||||
recentDocuments = recentDocs,
|
recentDocuments = recentDocs,
|
||||||
onOpenPdf = { fileUri -> openPdf(fileUri) }
|
onOpenPdf = { fileUri -> openUri(fileUri, ExportFormat.PDF.mimeType) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is Screen.Main.Camera -> {
|
is Screen.Main.Camera -> {
|
||||||
@@ -140,13 +139,13 @@ class MainActivity : ComponentActivity() {
|
|||||||
is Screen.Main.Export -> {
|
is Screen.Main.Export -> {
|
||||||
ExportScreenWrapper(
|
ExportScreenWrapper(
|
||||||
navigation = navigation,
|
navigation = navigation,
|
||||||
pdfActions = PdfGenerationActions(
|
uiState = exportUiState,
|
||||||
startGeneration = exportViewModel::startPdfGeneration,
|
pdfActions = ExportActions(
|
||||||
|
initializeExportScreen = exportViewModel::initializeExportScreen,
|
||||||
setFilename = exportViewModel::setFilename,
|
setFilename = exportViewModel::setFilename,
|
||||||
uiStateFlow = exportViewModel.pdfUiState,
|
share = { share(exportViewModel.applyRenaming(), exportViewModel) },
|
||||||
sharePdf = { sharePdf(exportViewModel.getFinalPdf(), exportViewModel) },
|
save = { exportViewModel.onSaveClicked() },
|
||||||
savePdf = { exportViewModel.onSavePdfClicked() },
|
open = { item -> openUri(item.uri, item.format.mimeType) }
|
||||||
openPdf = { openPdf(exportViewModel.pdfUiState.value.savedFileUri) }
|
|
||||||
),
|
),
|
||||||
onCloseScan = {
|
onCloseScan = {
|
||||||
viewModel.startNewDocument()
|
viewModel.startNewDocument()
|
||||||
@@ -188,7 +187,8 @@ class MainActivity : ComponentActivity() {
|
|||||||
settingsUiState,
|
settingsUiState,
|
||||||
onChooseDirectoryClick = { launcher.launch(null) },
|
onChooseDirectoryClick = { launcher.launch(null) },
|
||||||
onResetExportDirClick = { settingsViewModel.setExportDirUri(null) },
|
onResetExportDirClick = { settingsViewModel.setExportDirUri(null) },
|
||||||
onBack = nav.back
|
onExportFormatChanged = { format -> settingsViewModel.setExportFormat(format) },
|
||||||
|
onBack = nav.back,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,7 +222,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
ActivityResultContracts.RequestPermission()
|
ActivityResultContracts.RequestPermission()
|
||||||
) { isGranted ->
|
) { isGranted ->
|
||||||
if (isGranted) {
|
if (isGranted) {
|
||||||
exportViewModel.onSavePdfClicked()
|
exportViewModel.onSaveClicked()
|
||||||
} else {
|
} else {
|
||||||
val message = getString(R.string.storage_permission_denied)
|
val message = getString(R.string.storage_permission_denied)
|
||||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
|
||||||
@@ -231,9 +231,9 @@ class MainActivity : ComponentActivity() {
|
|||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
exportViewModel.events.collect { event ->
|
exportViewModel.events.collect { event ->
|
||||||
when (event) {
|
when (event) {
|
||||||
ExportEvent.RequestSavePdf -> {
|
ExportEvent.RequestSave -> {
|
||||||
checkPermissionThen(storagePermissionLauncher) {
|
checkPermissionThen(storagePermissionLauncher) {
|
||||||
exportViewModel.onRequestPdfSave(context)
|
exportViewModel.onRequestSave(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,25 +260,36 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sharePdf(generatedPdf: GeneratedPdf?, viewModel: ExportViewModel) {
|
private fun share(result: ExportResult?, viewModel: ExportViewModel) {
|
||||||
if (generatedPdf == null)
|
if (result == null || result.files.isEmpty()) return
|
||||||
return
|
|
||||||
viewModel.setPdfAsShared()
|
viewModel.setAsShared()
|
||||||
val file = generatedPdf.file
|
|
||||||
val authority = "${applicationContext.packageName}.fileprovider"
|
val authority = "${applicationContext.packageName}.fileprovider"
|
||||||
val fileUri = FileProvider.getUriForFile(this, authority, file)
|
val uris = result.files.map { file ->
|
||||||
val shareIntent = Intent(Intent.ACTION_SEND).apply {
|
FileProvider.getUriForFile(this, authority, file)
|
||||||
type = PDF_MIME_TYPE
|
}
|
||||||
putExtra(Intent.EXTRA_STREAM, fileUri)
|
val intent = Intent().apply {
|
||||||
|
action = if (uris.size == 1) Intent.ACTION_SEND else Intent.ACTION_SEND_MULTIPLE
|
||||||
|
type = result.format.mimeType
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
|
||||||
|
if (uris.size == 1) {
|
||||||
|
putExtra(Intent.EXTRA_STREAM, uris[0])
|
||||||
|
} else {
|
||||||
|
putParcelableArrayListExtra(Intent.EXTRA_STREAM, ArrayList(uris))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val chooser = Intent.createChooser(intent, getString(R.string.share_document))
|
||||||
|
|
||||||
|
val resolveInfos = packageManager.queryIntentActivities(chooser, PackageManager.MATCH_DEFAULT_ONLY)
|
||||||
|
for (info in resolveInfos) {
|
||||||
|
val pkg = info.activityInfo.packageName
|
||||||
|
for (uri in uris) {
|
||||||
|
grantUriPermission(pkg, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val chooser = Intent.createChooser(shareIntent, getString(R.string.share_pdf))
|
|
||||||
val resInfoList = packageManager.queryIntentActivities(chooser, PackageManager.MATCH_DEFAULT_ONLY)
|
|
||||||
for (resInfo in resInfoList) {
|
|
||||||
val packageName = resInfo.activityInfo.packageName
|
|
||||||
grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
||||||
}
|
|
||||||
startActivity(chooser)
|
startActivity(chooser)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +306,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openPdf(fileUri: Uri?) {
|
private fun openUri(fileUri: Uri?, mimeType: String) {
|
||||||
if (fileUri == null) return
|
if (fileUri == null) return
|
||||||
val uriToOpen: Uri =
|
val uriToOpen: Uri =
|
||||||
if (fileUri.scheme == ContentResolver.SCHEME_CONTENT) {
|
if (fileUri.scheme == ContentResolver.SCHEME_CONTENT) {
|
||||||
@@ -305,13 +316,13 @@ class MainActivity : ComponentActivity() {
|
|||||||
FileProvider.getUriForFile(this, authority, fileUri.toFile())
|
FileProvider.getUriForFile(this, authority, fileUri.toFile())
|
||||||
}
|
}
|
||||||
val openIntent = Intent(Intent.ACTION_VIEW).apply {
|
val openIntent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
setDataAndType(uriToOpen, PDF_MIME_TYPE)
|
setDataAndType(uriToOpen, mimeType)
|
||||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
startActivity(Intent.createChooser(openIntent, getString(R.string.open_pdf)))
|
startActivity(Intent.createChooser(openIntent, getString(R.string.open_file)))
|
||||||
} catch (_: ActivityNotFoundException) {
|
} catch (_: ActivityNotFoundException) {
|
||||||
Toast.makeText(this, getString(R.string.error_no_pdf_app), Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, getString(R.string.error_no_app), Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ fun interface PdfWriter {
|
|||||||
fun writePdfFromJpegs(jpegs: Sequence<ByteArray>, outputStream: OutputStream): Int
|
fun writePdfFromJpegs(jpegs: Sequence<ByteArray>, outputStream: OutputStream): Int
|
||||||
}
|
}
|
||||||
|
|
||||||
class PdfFileManager(
|
class FileManager(
|
||||||
private val pdfDir: File,
|
private val pdfDir: File,
|
||||||
private val externalDir: File,
|
private val externalDir: File,
|
||||||
private val pdfWriter: PdfWriter
|
private val pdfWriter: PdfWriter
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
fun addExtensionIfMissing(fileName: String): String {
|
fun addPdfExtensionIfMissing(fileName: String): String {
|
||||||
return if (fileName.lowercase().endsWith(".pdf"))
|
return if (fileName.lowercase().endsWith(".pdf"))
|
||||||
fileName
|
fileName
|
||||||
else
|
else
|
||||||
@@ -111,11 +111,12 @@ class ImageRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getContent(id: String): ByteArray? {
|
fun getContent(id: String): ByteArray? {
|
||||||
val file = File(scanDir, id)
|
return getFileFor(id)?.readBytes()
|
||||||
if (file.exists()) {
|
|
||||||
return file.readBytes()
|
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
|
fun getFileFor(id: String): File? {
|
||||||
|
val file = File(scanDir, id)
|
||||||
|
return if (file.exists()) file else null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getThumbnail(id: String): ByteArray? {
|
fun getThumbnail(id: String): ByteArray? {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.automirrored.filled.RotateLeft
|
import androidx.compose.material.icons.automirrored.filled.RotateLeft
|
||||||
import androidx.compose.material.icons.automirrored.filled.RotateRight
|
import androidx.compose.material.icons.automirrored.filled.RotateRight
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.PictureAsPdf
|
import androidx.compose.material.icons.filled.Description
|
||||||
import androidx.compose.material.icons.outlined.Delete
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -55,8 +55,8 @@ import androidx.compose.ui.unit.dp
|
|||||||
import kotlinx.collections.immutable.toImmutableList
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import net.engawapg.lib.zoomable.ZoomState
|
import net.engawapg.lib.zoomable.ZoomState
|
||||||
import net.engawapg.lib.zoomable.zoomable
|
import net.engawapg.lib.zoomable.zoomable
|
||||||
import org.fairscan.app.ui.Navigation
|
|
||||||
import org.fairscan.app.R
|
import org.fairscan.app.R
|
||||||
|
import org.fairscan.app.ui.Navigation
|
||||||
import org.fairscan.app.ui.components.CommonPageListState
|
import org.fairscan.app.ui.components.CommonPageListState
|
||||||
import org.fairscan.app.ui.components.ConfirmationDialog
|
import org.fairscan.app.ui.components.ConfirmationDialog
|
||||||
import org.fairscan.app.ui.components.MainActionButton
|
import org.fairscan.app.ui.components.MainActionButton
|
||||||
@@ -225,8 +225,8 @@ private fun BottomBar(
|
|||||||
) {
|
) {
|
||||||
MainActionButton(
|
MainActionButton(
|
||||||
onClick = navigation.toExportScreen,
|
onClick = navigation.toExportScreen,
|
||||||
icon = Icons.Default.PictureAsPdf,
|
icon = Icons.Default.Description,
|
||||||
text = stringResource(R.string.export_pdf),
|
text = stringResource(R.string.export),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ import androidx.compose.material3.TopAppBar
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
@@ -61,13 +60,13 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalResources
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import org.fairscan.app.R
|
import org.fairscan.app.R
|
||||||
import org.fairscan.app.data.GeneratedPdf
|
|
||||||
import org.fairscan.app.ui.Navigation
|
import org.fairscan.app.ui.Navigation
|
||||||
import org.fairscan.app.ui.components.AppOverflowMenu
|
import org.fairscan.app.ui.components.AppOverflowMenu
|
||||||
import org.fairscan.app.ui.components.BackButton
|
import org.fairscan.app.ui.components.BackButton
|
||||||
@@ -76,6 +75,8 @@ import org.fairscan.app.ui.components.NewDocumentDialog
|
|||||||
import org.fairscan.app.ui.components.isLandscape
|
import org.fairscan.app.ui.components.isLandscape
|
||||||
import org.fairscan.app.ui.components.pageCountText
|
import org.fairscan.app.ui.components.pageCountText
|
||||||
import org.fairscan.app.ui.dummyNavigation
|
import org.fairscan.app.ui.dummyNavigation
|
||||||
|
import org.fairscan.app.ui.screens.settings.ExportFormat
|
||||||
|
import org.fairscan.app.ui.screens.settings.ExportFormat.PDF
|
||||||
import org.fairscan.app.ui.theme.FairScanTheme
|
import org.fairscan.app.ui.theme.FairScanTheme
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
@@ -85,17 +86,17 @@ import java.util.Locale
|
|||||||
@Composable
|
@Composable
|
||||||
fun ExportScreenWrapper(
|
fun ExportScreenWrapper(
|
||||||
navigation: Navigation,
|
navigation: Navigation,
|
||||||
pdfActions: PdfGenerationActions,
|
uiState: ExportUiState,
|
||||||
|
pdfActions: ExportActions,
|
||||||
onCloseScan: () -> Unit,
|
onCloseScan: () -> Unit,
|
||||||
) {
|
) {
|
||||||
BackHandler { navigation.back() }
|
BackHandler { navigation.back() }
|
||||||
|
|
||||||
val showConfirmationDialog = rememberSaveable { mutableStateOf(false) }
|
val showConfirmationDialog = rememberSaveable { mutableStateOf(false) }
|
||||||
val filename = remember { mutableStateOf(defaultFilename()) }
|
val filename = remember { mutableStateOf(defaultFilename()) }
|
||||||
val uiState by pdfActions.uiStateFlow.collectAsState()
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
pdfActions.setFilename(filename.value)
|
pdfActions.setFilename(filename.value)
|
||||||
pdfActions.startGeneration()
|
pdfActions.initializeExportScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
val onFilenameChange = { newName:String ->
|
val onFilenameChange = { newName:String ->
|
||||||
@@ -116,15 +117,15 @@ fun ExportScreenWrapper(
|
|||||||
navigation = navigation,
|
navigation = navigation,
|
||||||
onShare = {
|
onShare = {
|
||||||
ensureCorrectFileName()
|
ensureCorrectFileName()
|
||||||
pdfActions.sharePdf()
|
pdfActions.share()
|
||||||
},
|
},
|
||||||
onSave = {
|
onSave = {
|
||||||
ensureCorrectFileName()
|
ensureCorrectFileName()
|
||||||
pdfActions.savePdf()
|
pdfActions.save()
|
||||||
},
|
},
|
||||||
onOpen = { pdfActions.openPdf() },
|
onOpen = pdfActions.open,
|
||||||
onCloseScan = {
|
onCloseScan = {
|
||||||
if (uiState.hasSavedOrSharedPdf)
|
if (uiState.hasSavedOrShared)
|
||||||
onCloseScan()
|
onCloseScan()
|
||||||
else
|
else
|
||||||
showConfirmationDialog.value = true
|
showConfirmationDialog.value = true
|
||||||
@@ -141,17 +142,17 @@ fun ExportScreenWrapper(
|
|||||||
fun ExportScreen(
|
fun ExportScreen(
|
||||||
filename: MutableState<String>,
|
filename: MutableState<String>,
|
||||||
onFilenameChange: (String) -> Unit,
|
onFilenameChange: (String) -> Unit,
|
||||||
uiState: PdfGenerationUiState,
|
uiState: ExportUiState,
|
||||||
navigation: Navigation,
|
navigation: Navigation,
|
||||||
onShare: () -> Unit,
|
onShare: () -> Unit,
|
||||||
onSave: () -> Unit,
|
onSave: () -> Unit,
|
||||||
onOpen: () -> Unit,
|
onOpen: (SavedItem) -> Unit,
|
||||||
onCloseScan: () -> Unit,
|
onCloseScan: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text(stringResource(R.string.export_pdf)) },
|
title = { Text(stringResource(R.string.export_as, uiState.format)) },
|
||||||
navigationIcon = { BackButton(navigation.back) },
|
navigationIcon = { BackButton(navigation.back) },
|
||||||
actions = {
|
actions = {
|
||||||
AppOverflowMenu(navigation)
|
AppOverflowMenu(navigation)
|
||||||
@@ -195,12 +196,12 @@ fun ExportScreen(
|
|||||||
private fun TextFieldAndPdfInfos(
|
private fun TextFieldAndPdfInfos(
|
||||||
filename: MutableState<String>,
|
filename: MutableState<String>,
|
||||||
onFilenameChange: (String) -> Unit,
|
onFilenameChange: (String) -> Unit,
|
||||||
uiState: PdfGenerationUiState,
|
uiState: ExportUiState,
|
||||||
onOpen: () -> Unit,
|
onOpen: (SavedItem) -> Unit,
|
||||||
) {
|
) {
|
||||||
FilenameTextField(filename, onFilenameChange)
|
FilenameTextField(filename, onFilenameChange)
|
||||||
|
|
||||||
val pdf = uiState.generatedPdf
|
val result = uiState.result
|
||||||
|
|
||||||
// PDF infos
|
// PDF infos
|
||||||
Column(
|
Column(
|
||||||
@@ -208,20 +209,22 @@ private fun TextFieldAndPdfInfos(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
if (uiState.isGenerating) {
|
if (uiState.isGenerating) {
|
||||||
Text(stringResource(R.string.creating_pdf), fontStyle = FontStyle.Italic)
|
Text(stringResource(R.string.creating_export), fontStyle = FontStyle.Italic)
|
||||||
} else if (pdf != null) {
|
} else if (result != null) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val formattedFileSize = formatFileSize(pdf.sizeInBytes, context)
|
val formattedFileSize = formatFileSize(result.sizeInBytes, context)
|
||||||
Text(text = pageCountText(pdf.pageCount))
|
Text(text = pageCountText(result.pageCount))
|
||||||
|
val sizeMessageKey =
|
||||||
|
if (result.files.size == 1) R.string.file_size else R.string.file_size_total
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.file_size, formattedFileSize),
|
text = stringResource(sizeMessageKey, formattedFileSize),
|
||||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
|
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uiState.savedFileUri != null) {
|
if (uiState.savedBundle != null) {
|
||||||
SavedPdfBar(uiState, onOpen)
|
SaveInfoBar(uiState.savedBundle, onOpen)
|
||||||
}
|
}
|
||||||
if (uiState.errorMessage != null) {
|
if (uiState.errorMessage != null) {
|
||||||
ErrorBar(uiState.errorMessage)
|
ErrorBar(uiState.errorMessage)
|
||||||
@@ -257,7 +260,7 @@ private fun FilenameTextField(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun MainActions(
|
private fun MainActions(
|
||||||
uiState: PdfGenerationUiState,
|
uiState: ExportUiState,
|
||||||
onShare: () -> Unit,
|
onShare: () -> Unit,
|
||||||
onSave: () -> Unit,
|
onSave: () -> Unit,
|
||||||
onCloseScan: () -> Unit,
|
onCloseScan: () -> Unit,
|
||||||
@@ -271,16 +274,16 @@ private fun MainActions(
|
|||||||
) {
|
) {
|
||||||
ExportButton(
|
ExportButton(
|
||||||
onClick = onShare,
|
onClick = onShare,
|
||||||
enabled = uiState.generatedPdf != null,
|
enabled = uiState.result != null,
|
||||||
isPrimary = !uiState.hasSavedOrSharedPdf,
|
isPrimary = !uiState.hasSavedOrShared,
|
||||||
icon = Icons.Default.Share,
|
icon = Icons.Default.Share,
|
||||||
text = stringResource(R.string.share),
|
text = stringResource(R.string.share),
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
ExportButton(
|
ExportButton(
|
||||||
onClick = onSave,
|
onClick = onSave,
|
||||||
enabled = uiState.generatedPdf != null,
|
enabled = uiState.result != null,
|
||||||
isPrimary = !uiState.hasSavedOrSharedPdf,
|
isPrimary = !uiState.hasSavedOrShared,
|
||||||
icon = Icons.Default.Download,
|
icon = Icons.Default.Download,
|
||||||
text = stringResource(R.string.save),
|
text = stringResource(R.string.save),
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
@@ -291,7 +294,7 @@ private fun MainActions(
|
|||||||
text = stringResource(R.string.end_scan),
|
text = stringResource(R.string.end_scan),
|
||||||
onClick = onCloseScan,
|
onClick = onCloseScan,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
isPrimary = uiState.hasSavedOrSharedPdf,
|
isPrimary = uiState.hasSavedOrShared,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,8 +338,8 @@ fun ExportButton(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SavedPdfBar(uiState: PdfGenerationUiState, onOpen: () -> Unit) {
|
private fun SaveInfoBar(savedBundle: SavedBundle, onOpen: (SavedItem) -> Unit) {
|
||||||
val dirName = uiState.exportDirName?:stringResource(R.string.download_dirname)
|
val dirName = savedBundle.exportDirName?:stringResource(R.string.download_dirname)
|
||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.Absolute.SpaceBetween,
|
horizontalArrangement = Arrangement.Absolute.SpaceBetween,
|
||||||
@@ -345,19 +348,28 @@ private fun SavedPdfBar(uiState: PdfGenerationUiState, onOpen: () -> Unit) {
|
|||||||
.background(MaterialTheme.colorScheme.surfaceVariant)
|
.background(MaterialTheme.colorScheme.surfaceVariant)
|
||||||
.padding(vertical = 8.dp, horizontal = 16.dp),
|
.padding(vertical = 8.dp, horizontal = 16.dp),
|
||||||
) {
|
) {
|
||||||
|
val items = savedBundle.items
|
||||||
|
val nbFiles = items.size
|
||||||
|
val firstFileName = items[0].fileName
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.pdf_saved_to, dirName),
|
text = LocalResources.current.getQuantityString(
|
||||||
|
R.plurals.files_saved_to,
|
||||||
|
nbFiles,
|
||||||
|
nbFiles, firstFileName, dirName
|
||||||
|
),
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
)
|
)
|
||||||
|
if (nbFiles == 1) {
|
||||||
Spacer(Modifier.width(8.dp))
|
Spacer(Modifier.width(8.dp))
|
||||||
MainActionButton(
|
MainActionButton(
|
||||||
onClick = onOpen,
|
onClick = { onOpen(items[0]) },
|
||||||
text = stringResource(R.string.open),
|
text = stringResource(R.string.open),
|
||||||
icon = Icons.AutoMirrored.Filled.OpenInNew,
|
icon = Icons.AutoMirrored.Filled.OpenInNew,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ErrorBar(errorMessage: String) {
|
private fun ErrorBar(errorMessage: String) {
|
||||||
@@ -386,7 +398,7 @@ fun formatFileSize(sizeInBytes: Long?, context: Context): String {
|
|||||||
@Composable
|
@Composable
|
||||||
fun PreviewExportScreenDuringGeneration() {
|
fun PreviewExportScreenDuringGeneration() {
|
||||||
ExportPreviewToCustomize(
|
ExportPreviewToCustomize(
|
||||||
uiState = PdfGenerationUiState(isGenerating = true)
|
uiState = ExportUiState(isGenerating = true)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,8 +407,8 @@ fun PreviewExportScreenDuringGeneration() {
|
|||||||
fun PreviewExportScreenAfterGeneration() {
|
fun PreviewExportScreenAfterGeneration() {
|
||||||
val file = File("fake.pdf")
|
val file = File("fake.pdf")
|
||||||
ExportPreviewToCustomize(
|
ExportPreviewToCustomize(
|
||||||
uiState = PdfGenerationUiState(
|
uiState = ExportUiState(
|
||||||
generatedPdf = GeneratedPdf(file, 442897L, 3),
|
result = ExportResult.Pdf(file, 442897L, 3),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -406,9 +418,11 @@ fun PreviewExportScreenAfterGeneration() {
|
|||||||
fun PreviewExportScreenAfterSave() {
|
fun PreviewExportScreenAfterSave() {
|
||||||
val file = File("fake.pdf")
|
val file = File("fake.pdf")
|
||||||
ExportPreviewToCustomize(
|
ExportPreviewToCustomize(
|
||||||
uiState = PdfGenerationUiState(
|
uiState = ExportUiState(
|
||||||
generatedPdf = GeneratedPdf(file, 442897L, 3),
|
result = ExportResult.Pdf(file, 442897L, 3),
|
||||||
savedFileUri = file.toUri(),
|
savedBundle = SavedBundle(
|
||||||
|
listOf(SavedItem(file.toUri(), defaultFilename() + ".pdf", PDF))
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -417,7 +431,7 @@ fun PreviewExportScreenAfterSave() {
|
|||||||
@Composable
|
@Composable
|
||||||
fun ExportScreenPreviewWithError() {
|
fun ExportScreenPreviewWithError() {
|
||||||
ExportPreviewToCustomize(
|
ExportPreviewToCustomize(
|
||||||
PdfGenerationUiState(errorMessage = "PDF generation failed")
|
ExportUiState(errorMessage = "PDF generation failed")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,16 +440,17 @@ fun ExportScreenPreviewWithError() {
|
|||||||
fun PreviewExportScreenAfterSaveHorizontal() {
|
fun PreviewExportScreenAfterSaveHorizontal() {
|
||||||
val file = File("fake.pdf")
|
val file = File("fake.pdf")
|
||||||
ExportPreviewToCustomize(
|
ExportPreviewToCustomize(
|
||||||
uiState = PdfGenerationUiState(
|
uiState = ExportUiState(
|
||||||
generatedPdf = GeneratedPdf(file, 442897L, 3),
|
result = ExportResult.Pdf(file, 442897L, 3),
|
||||||
savedFileUri = file.toUri(),
|
savedBundle = SavedBundle(
|
||||||
exportDirName = "MyVeryVeryLongDirectoryName"
|
listOf(SavedItem(file.toUri(), "my_file.pdf", PDF)),
|
||||||
|
exportDirName="MyVeryVeryLongDirectoryName"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ExportPreviewToCustomize(uiState: PdfGenerationUiState) {
|
fun ExportPreviewToCustomize(uiState: ExportUiState) {
|
||||||
FairScanTheme {
|
FairScanTheme {
|
||||||
ExportScreen(
|
ExportScreen(
|
||||||
filename = remember { mutableStateOf("Scan 2025-07-02 17.40.42") },
|
filename = remember { mutableStateOf("Scan 2025-07-02 17.40.42") },
|
||||||
|
|||||||
@@ -15,16 +15,28 @@
|
|||||||
package org.fairscan.app.ui.screens.export
|
package org.fairscan.app.ui.screens.export
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import org.fairscan.app.data.GeneratedPdf
|
import org.fairscan.app.ui.screens.settings.ExportFormat
|
||||||
|
|
||||||
data class PdfGenerationUiState(
|
data class ExportUiState(
|
||||||
|
val format: ExportFormat = ExportFormat.PDF,
|
||||||
val isGenerating: Boolean = false,
|
val isGenerating: Boolean = false,
|
||||||
val generatedPdf: GeneratedPdf? = null,
|
val result: ExportResult? = null,
|
||||||
val desiredFilename: String = "",
|
val desiredFilename: String = "",
|
||||||
val savedFileUri: Uri? = null,
|
val savedBundle: SavedBundle? = null,
|
||||||
val exportDirName: String? = null,
|
val hasShared: Boolean = false,
|
||||||
val hasSharedPdf: Boolean = false,
|
|
||||||
val errorMessage: String? = null,
|
val errorMessage: String? = null,
|
||||||
) {
|
) {
|
||||||
val hasSavedOrSharedPdf get() = savedFileUri != null || hasSharedPdf
|
val hasSavedOrShared get() = savedBundle != null || hasShared
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class SavedItem(
|
||||||
|
val uri: Uri,
|
||||||
|
val fileName: String,
|
||||||
|
val format: ExportFormat,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SavedBundle(
|
||||||
|
val items: List<SavedItem>,
|
||||||
|
val exportDir: Uri? = null,
|
||||||
|
val exportDirName: String? = null,
|
||||||
|
)
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import androidx.documentfile.provider.DocumentFile
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@@ -32,26 +31,25 @@ import kotlinx.coroutines.flow.asStateFlow
|
|||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.fairscan.app.AppContainer
|
import org.fairscan.app.AppContainer
|
||||||
import org.fairscan.app.RecentDocument
|
import org.fairscan.app.RecentDocument
|
||||||
import org.fairscan.app.data.GeneratedPdf
|
import org.fairscan.app.data.FileManager
|
||||||
import org.fairscan.app.data.PdfFileManager
|
import org.fairscan.app.ui.screens.settings.ExportFormat
|
||||||
import org.fairscan.app.ui.screens.home.HomeViewModel
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
import kotlin.coroutines.resume
|
||||||
private const val PDF_MIME_TYPE = "application/pdf"
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
sealed interface ExportEvent {
|
sealed interface ExportEvent {
|
||||||
data object RequestSavePdf : ExportEvent
|
data object RequestSave : ExportEvent
|
||||||
data object SaveError : ExportEvent
|
data object SaveError : ExportEvent
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExportViewModel(container: AppContainer): ViewModel() {
|
class ExportViewModel(container: AppContainer): ViewModel() {
|
||||||
|
|
||||||
private val pdfFileManager = container.pdfFileManager
|
private val preparationDir = container.preparationDir
|
||||||
|
private val fileManager = container.fileManager
|
||||||
private val imageRepository = container.imageRepository
|
private val imageRepository = container.imageRepository
|
||||||
private val settingsRepository = container.settingsRepository
|
private val settingsRepository = container.settingsRepository
|
||||||
private val recentDocumentsDataStore = container.recentDocumentsDataStore
|
private val recentDocumentsDataStore = container.recentDocumentsDataStore
|
||||||
@@ -60,134 +58,166 @@ class ExportViewModel(container: AppContainer): ViewModel() {
|
|||||||
private val _events = MutableSharedFlow<ExportEvent>()
|
private val _events = MutableSharedFlow<ExportEvent>()
|
||||||
val events = _events.asSharedFlow()
|
val events = _events.asSharedFlow()
|
||||||
|
|
||||||
private suspend fun generatePdf(): GeneratedPdf = withContext(Dispatchers.IO) {
|
private suspend fun generatePdf(): ExportResult.Pdf = withContext(Dispatchers.IO) {
|
||||||
val imageIds = imageRepository.imageIds()
|
val imageIds = imageRepository.imageIds()
|
||||||
val jpegs = imageIds.asSequence()
|
val jpegs = imageIds.asSequence()
|
||||||
.map { id -> imageRepository.getContent(id) }
|
.mapNotNull { id -> imageRepository.getContent(id) }
|
||||||
.filterNotNull()
|
val pdf = fileManager.generatePdf(jpegs)
|
||||||
return@withContext pdfFileManager.generatePdf(jpegs)
|
return@withContext ExportResult.Pdf(pdf.file, pdf.sizeInBytes, pdf.pageCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val _pdfUiState = MutableStateFlow(PdfGenerationUiState())
|
private val _uiState = MutableStateFlow(ExportUiState())
|
||||||
val pdfUiState: StateFlow<PdfGenerationUiState> = _pdfUiState.asStateFlow()
|
val uiState: StateFlow<ExportUiState> = _uiState.asStateFlow()
|
||||||
|
|
||||||
private var generationJob: Job? = null
|
private var preparationJob: Job? = null
|
||||||
private var desiredFilename: String = ""
|
private var desiredFilename: String = ""
|
||||||
|
private var exportFormat = ExportFormat.PDF
|
||||||
|
|
||||||
fun setFilename(name: String) {
|
fun setFilename(name: String) {
|
||||||
desiredFilename = name
|
desiredFilename = name
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startPdfGeneration() {
|
fun initializeExportScreen() {
|
||||||
cancelPdfGeneration()
|
cancelPreparation()
|
||||||
generationJob = viewModelScope.launch {
|
|
||||||
|
preparationJob = viewModelScope.launch {
|
||||||
|
exportFormat = settingsRepository.exportFormat.first()
|
||||||
|
_uiState.update { it.copy(format = exportFormat) }
|
||||||
try {
|
try {
|
||||||
val result = generatePdf()
|
val result = if (exportFormat == ExportFormat.JPEG) {
|
||||||
_pdfUiState.update {
|
val jpegFiles = imageRepository.imageIds()
|
||||||
it.copy(
|
.mapNotNull { id -> imageRepository.getFileFor(id) }
|
||||||
isGenerating = false,
|
.map { f -> f.copyTo(File(preparationDir, f.name), overwrite = true) }
|
||||||
generatedPdf = result
|
val sizeInBytes = jpegFiles.sumOf { it.length() }
|
||||||
)
|
ExportResult.Jpeg(jpegFiles, sizeInBytes)
|
||||||
|
} else {
|
||||||
|
generatePdf()
|
||||||
|
}
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(isGenerating = false, result = result)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.e("FairScan", "PDF generation failed", e)
|
val message = "Failed to prepare $exportFormat export"
|
||||||
_pdfUiState.update {
|
logger.e("FairScan", message, e)
|
||||||
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
isGenerating = false,
|
isGenerating = false,
|
||||||
errorMessage = "PDF generation failed"
|
errorMessage = message
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelPdfGeneration() {
|
fun cancelPreparation() {
|
||||||
generationJob?.cancel()
|
preparationJob?.cancel()
|
||||||
_pdfUiState.value = PdfGenerationUiState()
|
_uiState.value = ExportUiState()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setPdfAsShared() {
|
fun setAsShared() {
|
||||||
_pdfUiState.update { it.copy(hasSharedPdf = true) }
|
_uiState.update { it.copy(hasShared = true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFinalPdf(): GeneratedPdf? {
|
fun applyRenaming(): ExportResult? {
|
||||||
val tempPdf = _pdfUiState.value.generatedPdf ?: return null
|
val result = _uiState.value.result ?: return null
|
||||||
val tempFile = tempPdf.file
|
when (result) {
|
||||||
val fileName = PdfFileManager.addExtensionIfMissing(desiredFilename)
|
is ExportResult.Pdf -> {
|
||||||
val newFile = File(tempFile.parentFile, fileName)
|
val fileName = FileManager.addPdfExtensionIfMissing(desiredFilename)
|
||||||
|
val newFile = File(result.file.parentFile, fileName)
|
||||||
|
val tempFile = result.file
|
||||||
if (tempFile.absolutePath != newFile.absolutePath) {
|
if (tempFile.absolutePath != newFile.absolutePath) {
|
||||||
if (newFile.exists()) newFile.delete()
|
if (newFile.exists()) newFile.delete()
|
||||||
val success = tempFile.renameTo(newFile)
|
val success = tempFile.renameTo(newFile)
|
||||||
if (!success) return null
|
if (!success) return null
|
||||||
_pdfUiState.update {
|
_uiState.update {
|
||||||
it.copy(generatedPdf = GeneratedPdf(
|
it.copy(result = ExportResult.Pdf(
|
||||||
newFile, tempPdf.sizeInBytes, tempPdf.pageCount)
|
newFile, result.sizeInBytes, result.pageCount)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _pdfUiState.value.generatedPdf
|
}
|
||||||
|
is ExportResult.Jpeg -> {
|
||||||
|
val base = desiredFilename.removeSuffix(".jpg")
|
||||||
|
val renamedFiles = result.files.mapIndexed { index, file ->
|
||||||
|
val newFile = File(file.parentFile, "${base}_${index + 1}.jpg")
|
||||||
|
if (newFile.exists()) newFile.delete()
|
||||||
|
file.renameTo(newFile)
|
||||||
|
newFile
|
||||||
|
}
|
||||||
|
val updated = result.copy(jpegFiles = renamedFiles)
|
||||||
|
_uiState.update { it.copy(result = updated) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _uiState.value.result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onSavePdfClicked() {
|
fun onSaveClicked() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_events.emit(ExportEvent.RequestSavePdf)
|
_events.emit(ExportEvent.RequestSave)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onRequestPdfSave(context: Context) {
|
fun onRequestSave(context: Context) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
performPdfSave(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun performPdfSave(context: Context) {
|
|
||||||
try {
|
try {
|
||||||
val pdf = getFinalPdf() ?: return
|
save(context)
|
||||||
|
|
||||||
val exportDir = settingsRepository.exportDirUri.first()
|
|
||||||
var fileInDownloads: File? = null
|
|
||||||
|
|
||||||
var savedName: String
|
|
||||||
val savedUri: Uri
|
|
||||||
if (exportDir == null) {
|
|
||||||
fileInDownloads = pdfFileManager.copyToExternalDir(pdf.file)
|
|
||||||
savedUri = fileInDownloads.toUri()
|
|
||||||
savedName = fileInDownloads.name
|
|
||||||
} else {
|
|
||||||
val saved = copyViaSaf(context, pdf.file, exportDir.toUri())
|
|
||||||
savedUri = saved.uri
|
|
||||||
savedName = saved.name?:pdf.file.name
|
|
||||||
}
|
|
||||||
|
|
||||||
_pdfUiState.update {
|
|
||||||
it.copy(
|
|
||||||
savedFileUri = savedUri,
|
|
||||||
exportDirName = resolveExportDirName(context, exportDir?.toUri()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fileInDownloads?.let { mediaScan(context, it) }
|
|
||||||
|
|
||||||
addRecentDocument(savedUri, savedName, pdf.pageCount)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logger.e("FairScan", "Failed to save PDF", e)
|
logger.e("FairScan", "Failed to save PDF", e)
|
||||||
_events.emit(ExportEvent.SaveError)
|
_events.emit(ExportEvent.SaveError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
private suspend fun save(context:Context) {
|
||||||
private suspend fun mediaScan(context: Context, file: File) =
|
val result = applyRenaming() ?: return
|
||||||
suspendCancellableCoroutine { continuation ->
|
val exportDir = settingsRepository.exportDirUri.first()?.toUri()
|
||||||
|
val savedItems = mutableListOf<SavedItem>()
|
||||||
|
val filesForMediaScan = mutableListOf<File>()
|
||||||
|
|
||||||
|
for (file in result.files) {
|
||||||
|
val saved = if (exportDir == null) {
|
||||||
|
val out = fileManager.copyToExternalDir(file)
|
||||||
|
filesForMediaScan.add(out)
|
||||||
|
SavedItem(out.toUri(), out.name, exportFormat)
|
||||||
|
} else {
|
||||||
|
val safFile = copyViaSaf(context, file, exportDir, exportFormat)
|
||||||
|
SavedItem(safFile.uri, safFile.name ?: file.name, exportFormat)
|
||||||
|
}
|
||||||
|
savedItems += saved
|
||||||
|
}
|
||||||
|
|
||||||
|
val exportDirName = resolveExportDirName(context, exportDir)
|
||||||
|
val bundle = SavedBundle(savedItems, exportDir, exportDirName)
|
||||||
|
_uiState.update { it.copy(savedBundle = bundle) }
|
||||||
|
|
||||||
|
if (exportFormat == ExportFormat.PDF) {
|
||||||
|
savedItems.forEach { item ->
|
||||||
|
addRecentDocument(item.uri, item.fileName, result.pageCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filesForMediaScan.forEach { f -> mediaScan(context, f, exportFormat.mimeType) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun mediaScan(
|
||||||
|
context: Context,
|
||||||
|
file: File,
|
||||||
|
mimeType: String
|
||||||
|
): Uri? = suspendCoroutine { cont ->
|
||||||
MediaScannerConnection.scanFile(
|
MediaScannerConnection.scanFile(
|
||||||
context,
|
context,
|
||||||
arrayOf(file.absolutePath),
|
arrayOf(file.absolutePath),
|
||||||
arrayOf(PDF_MIME_TYPE)
|
arrayOf(mimeType)
|
||||||
) { _, _ -> continuation.resume(Unit) {} }
|
) { _, uri ->
|
||||||
|
cont.resume(uri)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copyViaSaf(
|
private fun copyViaSaf(
|
||||||
context: Context,
|
context: Context,
|
||||||
source: File,
|
source: File,
|
||||||
exportDirUri: Uri,
|
exportDirUri: Uri,
|
||||||
|
exportFormat: ExportFormat,
|
||||||
): DocumentFile {
|
): DocumentFile {
|
||||||
val resolver = context.contentResolver
|
val resolver = context.contentResolver
|
||||||
|
|
||||||
@@ -195,7 +225,7 @@ class ExportViewModel(container: AppContainer): ViewModel() {
|
|||||||
?: throw IllegalStateException("Invalid SAF directory")
|
?: throw IllegalStateException("Invalid SAF directory")
|
||||||
|
|
||||||
// Name collisions are handled automatically by SAF provider
|
// Name collisions are handled automatically by SAF provider
|
||||||
val target = tree.createFile(PDF_MIME_TYPE, source.name)
|
val target = tree.createFile(exportFormat.mimeType, source.name)
|
||||||
?: throw IllegalStateException("Unable to create SAF file")
|
?: throw IllegalStateException("Unable to create SAF file")
|
||||||
|
|
||||||
resolver.openOutputStream(target.uri)?.use { output ->
|
resolver.openOutputStream(target.uri)?.use { output ->
|
||||||
@@ -207,8 +237,8 @@ class ExportViewModel(container: AppContainer): ViewModel() {
|
|||||||
return target
|
return target
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cleanUpOldPdfs(thresholdInMillis: Int) {
|
fun cleanUpOldPreparedFiles(thresholdInMillis: Int) {
|
||||||
pdfFileManager.cleanUpOldFiles(thresholdInMillis)
|
fileManager.cleanUpOldFiles(thresholdInMillis)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun resolveExportDirName(context: Context, exportDirUri: Uri?): String? {
|
private fun resolveExportDirName(context: Context, exportDirUri: Uri?): String? {
|
||||||
@@ -241,11 +271,35 @@ class ExportViewModel(container: AppContainer): ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PdfGenerationActions(
|
sealed class ExportResult {
|
||||||
val startGeneration: () -> Unit,
|
abstract val files: List<File>
|
||||||
|
abstract val sizeInBytes: Long
|
||||||
|
abstract val pageCount: Int
|
||||||
|
abstract val format: ExportFormat
|
||||||
|
|
||||||
|
data class Pdf(
|
||||||
|
val file: File,
|
||||||
|
override val sizeInBytes: Long,
|
||||||
|
override val pageCount: Int,
|
||||||
|
) : ExportResult() {
|
||||||
|
override val files get() = listOf(file)
|
||||||
|
override val format: ExportFormat = ExportFormat.PDF
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Jpeg(
|
||||||
|
val jpegFiles: List<File>,
|
||||||
|
override val sizeInBytes: Long,
|
||||||
|
) : ExportResult() {
|
||||||
|
override val files get() = jpegFiles
|
||||||
|
override val pageCount get() = jpegFiles.size
|
||||||
|
override val format: ExportFormat = ExportFormat.JPEG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ExportActions(
|
||||||
|
val initializeExportScreen: () -> Unit,
|
||||||
val setFilename: (String) -> Unit,
|
val setFilename: (String) -> Unit,
|
||||||
val uiStateFlow: StateFlow<PdfGenerationUiState>,// TODO is it ok to have that here?
|
val share: () -> Unit,
|
||||||
val sharePdf: () -> Unit,
|
val save: () -> Unit,
|
||||||
val savePdf: () -> Unit,
|
val open: (SavedItem) -> Unit,
|
||||||
val openPdf: () -> Unit,
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -26,12 +26,22 @@ private val Context.dataStore by preferencesDataStore(name = "fairscan_settings"
|
|||||||
class SettingsRepository(private val context: Context) {
|
class SettingsRepository(private val context: Context) {
|
||||||
|
|
||||||
private val EXPORT_DIR_URI = stringPreferencesKey("export_dir_uri")
|
private val EXPORT_DIR_URI = stringPreferencesKey("export_dir_uri")
|
||||||
|
private val EXPORT_FORMAT = stringPreferencesKey("export_format")
|
||||||
|
|
||||||
val exportDirUri: Flow<String?> =
|
val exportDirUri: Flow<String?> =
|
||||||
context.dataStore.data.map { prefs ->
|
context.dataStore.data.map { prefs ->
|
||||||
prefs[EXPORT_DIR_URI]
|
prefs[EXPORT_DIR_URI]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val exportFormat: Flow<ExportFormat> =
|
||||||
|
context.dataStore.data.map { prefs ->
|
||||||
|
when (prefs[EXPORT_FORMAT]) {
|
||||||
|
"JPEG" -> ExportFormat.JPEG
|
||||||
|
"PDF", null -> ExportFormat.PDF
|
||||||
|
else -> ExportFormat.PDF
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun setExportDirUri(uri: String?) {
|
suspend fun setExportDirUri(uri: String?) {
|
||||||
context.dataStore.edit { prefs ->
|
context.dataStore.edit { prefs ->
|
||||||
if (uri == null) {
|
if (uri == null) {
|
||||||
@@ -41,4 +51,15 @@ class SettingsRepository(private val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun setExportFormat(format: ExportFormat) {
|
||||||
|
context.dataStore.edit { prefs ->
|
||||||
|
prefs[EXPORT_FORMAT] = format.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ExportFormat(val mimeType: String) {
|
||||||
|
PDF("application/pdf"),
|
||||||
|
JPEG("image/jpeg"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ 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.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Folder
|
import androidx.compose.material.icons.filled.Folder
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
@@ -34,6 +36,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedButton
|
import androidx.compose.material3.OutlinedButton
|
||||||
|
import androidx.compose.material3.RadioButton
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
@@ -56,6 +59,7 @@ fun SettingsScreen(
|
|||||||
uiState: SettingsUiState,
|
uiState: SettingsUiState,
|
||||||
onChooseDirectoryClick: () -> Unit,
|
onChooseDirectoryClick: () -> Unit,
|
||||||
onResetExportDirClick: () -> Unit,
|
onResetExportDirClick: () -> Unit,
|
||||||
|
onExportFormatChanged: (ExportFormat) -> Unit,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
) {
|
) {
|
||||||
BackHandler { onBack() }
|
BackHandler { onBack() }
|
||||||
@@ -67,10 +71,13 @@ fun SettingsScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
SettingsContent(uiState, onChooseDirectoryClick, onResetExportDirClick, modifier = Modifier.padding(paddingValues))
|
SettingsContent(
|
||||||
|
uiState,
|
||||||
|
onChooseDirectoryClick,
|
||||||
|
onResetExportDirClick,
|
||||||
|
onExportFormatChanged,
|
||||||
|
modifier = Modifier.padding(paddingValues))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -78,6 +85,7 @@ private fun SettingsContent(
|
|||||||
uiState: SettingsUiState,
|
uiState: SettingsUiState,
|
||||||
onChooseDirectoryClick: () -> Unit,
|
onChooseDirectoryClick: () -> Unit,
|
||||||
onResetExportDirClick: () -> Unit,
|
onResetExportDirClick: () -> Unit,
|
||||||
|
onExportFormatChanged: (ExportFormat) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -89,6 +97,7 @@ private fun SettingsContent(
|
|||||||
modifier
|
modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(20.dp)
|
.padding(20.dp)
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
) {
|
) {
|
||||||
DirectorySettingItem(
|
DirectorySettingItem(
|
||||||
label = stringResource(R.string.export_directory),
|
label = stringResource(R.string.export_directory),
|
||||||
@@ -106,6 +115,26 @@ private fun SettingsContent(
|
|||||||
Text(stringResource(R.string.reset_to_default))
|
Text(stringResource(R.string.reset_to_default))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Spacer(Modifier.height(32.dp))
|
||||||
|
|
||||||
|
Text("Export format", style = MaterialTheme.typography.titleLarge)
|
||||||
|
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
RadioButton(
|
||||||
|
selected = uiState.exportFormat == ExportFormat.PDF,
|
||||||
|
onClick = { onExportFormatChanged(ExportFormat.PDF) },
|
||||||
|
)
|
||||||
|
Text("PDF")
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
|
RadioButton(
|
||||||
|
selected = uiState.exportFormat == ExportFormat.JPEG,
|
||||||
|
onClick = { onExportFormatChanged(ExportFormat.JPEG) },
|
||||||
|
)
|
||||||
|
Text("JPEG")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,6 +202,12 @@ fun SettingsScreenPreviewWithDir() {
|
|||||||
@Composable
|
@Composable
|
||||||
fun SettingsScreenPreview(uiState: SettingsUiState) {
|
fun SettingsScreenPreview(uiState: SettingsUiState) {
|
||||||
FairScanTheme {
|
FairScanTheme {
|
||||||
SettingsScreen(uiState, onChooseDirectoryClick = {}, onResetExportDirClick = {}, onBack= {})
|
SettingsScreen(
|
||||||
|
uiState,
|
||||||
|
onChooseDirectoryClick = {},
|
||||||
|
onResetExportDirClick = {},
|
||||||
|
onExportFormatChanged = {},
|
||||||
|
onBack = {}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,17 +21,23 @@ import kotlinx.coroutines.launch
|
|||||||
import org.fairscan.app.AppContainer
|
import org.fairscan.app.AppContainer
|
||||||
|
|
||||||
data class SettingsUiState(
|
data class SettingsUiState(
|
||||||
val exportDirUri: String? = null
|
val exportDirUri: String? = null,
|
||||||
|
val exportFormat: ExportFormat = ExportFormat.PDF,
|
||||||
)
|
)
|
||||||
|
|
||||||
class SettingsViewModel(container: AppContainer) : ViewModel() {
|
class SettingsViewModel(container: AppContainer) : ViewModel() {
|
||||||
|
|
||||||
private val repo = container.settingsRepository
|
private val repo = container.settingsRepository
|
||||||
|
|
||||||
val uiState: StateFlow<SettingsUiState> =
|
val uiState = combine(
|
||||||
repo.exportDirUri
|
repo.exportDirUri,
|
||||||
.map { uri -> SettingsUiState(exportDirUri = uri) }
|
repo.exportFormat,
|
||||||
.stateIn(
|
) { dir, format ->
|
||||||
|
SettingsUiState(
|
||||||
|
exportDirUri = dir,
|
||||||
|
exportFormat = format,
|
||||||
|
)
|
||||||
|
}.stateIn(
|
||||||
viewModelScope,
|
viewModelScope,
|
||||||
SharingStarted.WhileSubscribed(5000),
|
SharingStarted.WhileSubscribed(5000),
|
||||||
SettingsUiState()
|
SettingsUiState()
|
||||||
@@ -42,4 +48,10 @@ class SettingsViewModel(container: AppContainer) : ViewModel() {
|
|||||||
repo.setExportDirUri(uri)
|
repo.setExportDirUri(uri)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setExportFormat(format: ExportFormat) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
repo.setExportFormat(format)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<string name="contact">Kontakt</string>
|
<string name="contact">Kontakt</string>
|
||||||
<string name="copied_logs">Protokoly zkopírovány do schránky</string>
|
<string name="copied_logs">Protokoly zkopírovány do schránky</string>
|
||||||
<string name="copy_logs">Kopírovat protokoly</string>
|
<string name="copy_logs">Kopírovat protokoly</string>
|
||||||
<string name="creating_pdf">Vytváření PDF…</string>
|
<string name="creating_export">Příprava exportu…</string>
|
||||||
<string name="delete_page">Smazat stránku</string>
|
<string name="delete_page">Smazat stránku</string>
|
||||||
<string name="delete_page_warning">Chcete smazat tuto stránku?</string>
|
<string name="delete_page_warning">Chcete smazat tuto stránku?</string>
|
||||||
<string name="developer">Vývojář</string>
|
<string name="developer">Vývojář</string>
|
||||||
@@ -19,12 +19,14 @@
|
|||||||
<string name="download_dirname">stažených</string>
|
<string name="download_dirname">stažených</string>
|
||||||
<string name="end_scan">Ukončit skenování</string>
|
<string name="end_scan">Ukončit skenování</string>
|
||||||
<string name="error">Chyba: %1$s</string>
|
<string name="error">Chyba: %1$s</string>
|
||||||
|
<string name="error_no_app">Nebyla nalezena žádná aplikace pro otevření tohoto souboru</string>
|
||||||
<string name="error_no_document">Nebyl rozpoznán žádná dokument</string>
|
<string name="error_no_document">Nebyl rozpoznán žádná dokument</string>
|
||||||
<string name="error_no_pdf_app">Nebyla nazelena žádná aplikace pro otevření PDF</string>
|
<string name="error_save">Soubor se nepodařilo uložit</string>
|
||||||
<string name="error_save">Chyba při ukládání PDF</string>
|
<string name="export">Exportovat</string>
|
||||||
|
<string name="export_as">Exportovat jako %1$s</string>
|
||||||
<string name="export_directory">Složka pro export</string>
|
<string name="export_directory">Složka pro export</string>
|
||||||
<string name="export_pdf">Exportovat PDF</string>
|
|
||||||
<string name="file_size">Velikost souboru: %1$s</string>
|
<string name="file_size">Velikost souboru: %1$s</string>
|
||||||
|
<string name="file_size_total">Celková velikost: %1$s</string>
|
||||||
<string name="filename">Název souboru</string>
|
<string name="filename">Název souboru</string>
|
||||||
<string name="grant_permission">Povolit přístup</string>
|
<string name="grant_permission">Povolit přístup</string>
|
||||||
<string name="last_saved_pdf_files">Poslední PDF uložené v tomto zařízení:</string>
|
<string name="last_saved_pdf_files">Poslední PDF uložené v tomto zařízení:</string>
|
||||||
@@ -36,8 +38,7 @@
|
|||||||
<string name="menu">Menu</string>
|
<string name="menu">Menu</string>
|
||||||
<string name="new_document_warning">Toto skenování bude ztraceno. Chcete pokračovat?</string>
|
<string name="new_document_warning">Toto skenování bude ztraceno. Chcete pokračovat?</string>
|
||||||
<string name="open">Otevřít</string>
|
<string name="open">Otevřít</string>
|
||||||
<string name="open_pdf">Otevřít PDF</string>
|
<string name="open_file">Otevřít soubor</string>
|
||||||
<string name="pdf_saved_to">PDF bylo uloženo do %1s</string>
|
|
||||||
<string name="reset_to_default">Obnovit výchozí</string>
|
<string name="reset_to_default">Obnovit výchozí</string>
|
||||||
<string name="resume">Obnovit</string>
|
<string name="resume">Obnovit</string>
|
||||||
<string name="save">Uložit</string>
|
<string name="save">Uložit</string>
|
||||||
@@ -45,8 +46,8 @@
|
|||||||
<string name="scan_in_progress">Probíhá skenování</string>
|
<string name="scan_in_progress">Probíhá skenování</string>
|
||||||
<string name="settings">Nastavení</string>
|
<string name="settings">Nastavení</string>
|
||||||
<string name="share">Sdílet</string>
|
<string name="share">Sdílet</string>
|
||||||
<string name="share_pdf">Sdílet PDF</string>
|
<string name="share_document">Sdílet dokument</string>
|
||||||
<string name="storage_permission_denied">Nelze uložit PDF: přístup zakázán</string>
|
<string name="storage_permission_denied">Nelze uložit soubor: oprávnění bylo odmítnuto</string>
|
||||||
<string name="turn_off_torch">Vypnout svítilnu</string>
|
<string name="turn_off_torch">Vypnout svítilnu</string>
|
||||||
<string name="turn_on_torch">Zapnout svítilnu</string>
|
<string name="turn_on_torch">Zapnout svítilnu</string>
|
||||||
<string name="unknown_size">Neznámá velikost</string>
|
<string name="unknown_size">Neznámá velikost</string>
|
||||||
@@ -54,6 +55,12 @@
|
|||||||
<string name="view_full_list">Zobrazit úplný seznam</string>
|
<string name="view_full_list">Zobrazit úplný seznam</string>
|
||||||
<string name="view_the_full_license">Zobrazit úplnou licenci</string>
|
<string name="view_the_full_license">Zobrazit úplnou licenci</string>
|
||||||
<string name="yes">Ano</string>
|
<string name="yes">Ano</string>
|
||||||
|
<plurals name="files_saved_to">
|
||||||
|
<item quantity="one">%2$s uložen do %3$s</item>
|
||||||
|
<item quantity="few">%1$d soubory uloženy do %3$s</item>
|
||||||
|
<item quantity="many">%1$d souborů uloženo do %3$s</item>
|
||||||
|
<item quantity="other">%1$d souborů uloženo do %3$s</item>
|
||||||
|
</plurals>
|
||||||
<plurals name="page_count">
|
<plurals name="page_count">
|
||||||
<item quantity="one">%d stránka</item> <!-- 1 -->
|
<item quantity="one">%d stránka</item> <!-- 1 -->
|
||||||
<item quantity="few">%d stránky</item> <!-- 2–4 -->
|
<item quantity="few">%d stránky</item> <!-- 2–4 -->
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<string name="contact">Kontakt</string>
|
<string name="contact">Kontakt</string>
|
||||||
<string name="copied_logs">Logs in die Zwischenablage kopiert</string>
|
<string name="copied_logs">Logs in die Zwischenablage kopiert</string>
|
||||||
<string name="copy_logs">Logs kopieren</string>
|
<string name="copy_logs">Logs kopieren</string>
|
||||||
<string name="creating_pdf">PDF wird erstellt…</string>
|
<string name="creating_export">Export wird vorbereitet…</string>
|
||||||
<string name="delete_page">Seite löschen</string>
|
<string name="delete_page">Seite löschen</string>
|
||||||
<string name="delete_page_warning">Möchten Sie diese Seite löschen?</string>
|
<string name="delete_page_warning">Möchten Sie diese Seite löschen?</string>
|
||||||
<string name="developer">Entwickler</string>
|
<string name="developer">Entwickler</string>
|
||||||
@@ -19,12 +19,14 @@
|
|||||||
<string name="download_dirname">Downloads</string>
|
<string name="download_dirname">Downloads</string>
|
||||||
<string name="end_scan">Scan beenden</string>
|
<string name="end_scan">Scan beenden</string>
|
||||||
<string name="error">Fehler: %1$s</string>
|
<string name="error">Fehler: %1$s</string>
|
||||||
|
<string name="error_no_app">Keine App zum Öffnen dieser Datei gefunden</string>
|
||||||
<string name="error_no_document">Kein Dokument erkannt</string>
|
<string name="error_no_document">Kein Dokument erkannt</string>
|
||||||
<string name="error_no_pdf_app">Keine App zum Öffnen von PDF gefunden</string>
|
<string name="error_save">Datei konnte nicht gespeichert werden</string>
|
||||||
<string name="error_save">PDF konnte nicht gespeichert werden</string>
|
<string name="export">Exportieren</string>
|
||||||
|
<string name="export_as">Als %1$s exportieren</string>
|
||||||
<string name="export_directory">Exportordner</string>
|
<string name="export_directory">Exportordner</string>
|
||||||
<string name="export_pdf">PDF exportieren</string>
|
|
||||||
<string name="file_size">Dateigröße: %1$s</string>
|
<string name="file_size">Dateigröße: %1$s</string>
|
||||||
|
<string name="file_size_total">Gesamtgröße: %1$s</string>
|
||||||
<string name="filename">Dateiname</string>
|
<string name="filename">Dateiname</string>
|
||||||
<string name="grant_permission">Berechtigung erteilen</string>
|
<string name="grant_permission">Berechtigung erteilen</string>
|
||||||
<string name="last_saved_pdf_files">Zuletzt auf diesem Gerät gespeicherte PDFs:</string>
|
<string name="last_saved_pdf_files">Zuletzt auf diesem Gerät gespeicherte PDFs:</string>
|
||||||
@@ -36,8 +38,7 @@
|
|||||||
<string name="menu">Menü</string>
|
<string name="menu">Menü</string>
|
||||||
<string name="new_document_warning">Das aktuelle Dokument geht verloren. Möchten Sie fortfahren?</string>
|
<string name="new_document_warning">Das aktuelle Dokument geht verloren. Möchten Sie fortfahren?</string>
|
||||||
<string name="open">Öffnen</string>
|
<string name="open">Öffnen</string>
|
||||||
<string name="open_pdf">PDF öffnen</string>
|
<string name="open_file">Datei öffnen</string>
|
||||||
<string name="pdf_saved_to">PDF gespeichert in %1s</string>
|
|
||||||
<string name="reset_to_default">Auf Standard zurücksetzen</string>
|
<string name="reset_to_default">Auf Standard zurücksetzen</string>
|
||||||
<string name="resume">Fortsetzen</string>
|
<string name="resume">Fortsetzen</string>
|
||||||
<string name="save">Speichern</string>
|
<string name="save">Speichern</string>
|
||||||
@@ -45,8 +46,8 @@
|
|||||||
<string name="scan_in_progress">Scan läuft</string>
|
<string name="scan_in_progress">Scan läuft</string>
|
||||||
<string name="settings">Einstellungen</string>
|
<string name="settings">Einstellungen</string>
|
||||||
<string name="share">Teilen</string>
|
<string name="share">Teilen</string>
|
||||||
<string name="share_pdf">PDF teilen</string>
|
<string name="share_document">Dokument teilen</string>
|
||||||
<string name="storage_permission_denied">PDF-Datei kann nicht gespeichert werden: Berechtigung verweigert</string>
|
<string name="storage_permission_denied">Datei kann nicht gespeichert werden: Berechtigung verweigert</string>
|
||||||
<string name="turn_off_torch">Taschenlampe ausschalten</string>
|
<string name="turn_off_torch">Taschenlampe ausschalten</string>
|
||||||
<string name="turn_on_torch">Taschenlampe einschalten</string>
|
<string name="turn_on_torch">Taschenlampe einschalten</string>
|
||||||
<string name="unknown_size">Unbekannte Größe</string>
|
<string name="unknown_size">Unbekannte Größe</string>
|
||||||
@@ -54,6 +55,10 @@
|
|||||||
<string name="view_full_list">Vollständige Liste anzeigen</string>
|
<string name="view_full_list">Vollständige Liste anzeigen</string>
|
||||||
<string name="view_the_full_license">Vollständige Lizenz anzeigen</string>
|
<string name="view_the_full_license">Vollständige Lizenz anzeigen</string>
|
||||||
<string name="yes">Ja</string>
|
<string name="yes">Ja</string>
|
||||||
|
<plurals name="files_saved_to">
|
||||||
|
<item quantity="one">%2$s gespeichert in %3$s</item>
|
||||||
|
<item quantity="other">%1$d Dateien gespeichert in %3$s</item>
|
||||||
|
</plurals>
|
||||||
<plurals name="page_count">
|
<plurals name="page_count">
|
||||||
<item quantity="one">%d Seite</item>
|
<item quantity="one">%d Seite</item>
|
||||||
<item quantity="other">%d Seiten</item>
|
<item quantity="other">%d Seiten</item>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<string name="contact">Contacto</string>
|
<string name="contact">Contacto</string>
|
||||||
<string name="copied_logs">Registros copiados al portapapeles</string>
|
<string name="copied_logs">Registros copiados al portapapeles</string>
|
||||||
<string name="copy_logs">Copiar registros</string>
|
<string name="copy_logs">Copiar registros</string>
|
||||||
<string name="creating_pdf">Creando PDF…</string>
|
<string name="creating_export">Preparando la exportación…</string>
|
||||||
<string name="delete_page">Eliminar página</string>
|
<string name="delete_page">Eliminar página</string>
|
||||||
<string name="delete_page_warning">¿Quieres eliminar esta página?</string>
|
<string name="delete_page_warning">¿Quieres eliminar esta página?</string>
|
||||||
<string name="developer">Desarrollador</string>
|
<string name="developer">Desarrollador</string>
|
||||||
@@ -19,12 +19,14 @@
|
|||||||
<string name="download_dirname">Descargas</string>
|
<string name="download_dirname">Descargas</string>
|
||||||
<string name="end_scan">Finalizar escaneo</string>
|
<string name="end_scan">Finalizar escaneo</string>
|
||||||
<string name="error">Error: %1$s</string>
|
<string name="error">Error: %1$s</string>
|
||||||
|
<string name="error_no_app">No se encontró ninguna aplicación para abrir este archivo</string>
|
||||||
<string name="error_no_document">No se detectó ningún documento</string>
|
<string name="error_no_document">No se detectó ningún documento</string>
|
||||||
<string name="error_no_pdf_app">No se encontró ninguna aplicación para abrir PDF</string>
|
<string name="error_save">No se pudo guardar el archivo</string>
|
||||||
<string name="error_save">Error al guardar el PDF</string>
|
<string name="export">Exportar</string>
|
||||||
|
<string name="export_as">Exportar como %1$s</string>
|
||||||
<string name="export_directory">Carpeta de exportación</string>
|
<string name="export_directory">Carpeta de exportación</string>
|
||||||
<string name="export_pdf">Exportar PDF</string>
|
|
||||||
<string name="file_size">Tamaño del archivo: %1$s</string>
|
<string name="file_size">Tamaño del archivo: %1$s</string>
|
||||||
|
<string name="file_size_total">Tamaño total: %1$s</string>
|
||||||
<string name="filename">Nombre del archivo</string>
|
<string name="filename">Nombre del archivo</string>
|
||||||
<string name="grant_permission">Conceder permiso</string>
|
<string name="grant_permission">Conceder permiso</string>
|
||||||
<string name="last_saved_pdf_files">PDF recientes guardados en este dispositivo:</string>
|
<string name="last_saved_pdf_files">PDF recientes guardados en este dispositivo:</string>
|
||||||
@@ -36,8 +38,7 @@
|
|||||||
<string name="menu">Menú</string>
|
<string name="menu">Menú</string>
|
||||||
<string name="new_document_warning">El escaneo actual se perderá. ¿Deseas continuar?</string>
|
<string name="new_document_warning">El escaneo actual se perderá. ¿Deseas continuar?</string>
|
||||||
<string name="open">Abrir</string>
|
<string name="open">Abrir</string>
|
||||||
<string name="open_pdf">Abrir PDF</string>
|
<string name="open_file">Abrir archivo</string>
|
||||||
<string name="pdf_saved_to">PDF guardado en %1s</string>
|
|
||||||
<string name="reset_to_default">Restablecer valores predeterminados</string>
|
<string name="reset_to_default">Restablecer valores predeterminados</string>
|
||||||
<string name="resume">Reanudar</string>
|
<string name="resume">Reanudar</string>
|
||||||
<string name="save">Guardar</string>
|
<string name="save">Guardar</string>
|
||||||
@@ -45,8 +46,8 @@
|
|||||||
<string name="scan_in_progress">Escaneo en curso</string>
|
<string name="scan_in_progress">Escaneo en curso</string>
|
||||||
<string name="settings">Ajustes</string>
|
<string name="settings">Ajustes</string>
|
||||||
<string name="share">Compartir</string>
|
<string name="share">Compartir</string>
|
||||||
<string name="share_pdf">Compartir PDF</string>
|
<string name="share_document">Compartir documento</string>
|
||||||
<string name="storage_permission_denied">No se puede guardar el archivo PDF: permiso denegado</string>
|
<string name="storage_permission_denied">No se puede guardar el archivo: permiso denegado</string>
|
||||||
<string name="turn_off_torch">Apagar linterna</string>
|
<string name="turn_off_torch">Apagar linterna</string>
|
||||||
<string name="turn_on_torch">Encender linterna</string>
|
<string name="turn_on_torch">Encender linterna</string>
|
||||||
<string name="unknown_size">Tamaño desconocido</string>
|
<string name="unknown_size">Tamaño desconocido</string>
|
||||||
@@ -54,6 +55,10 @@
|
|||||||
<string name="view_full_list">Ver lista completa</string>
|
<string name="view_full_list">Ver lista completa</string>
|
||||||
<string name="view_the_full_license">Ver la licencia completa</string>
|
<string name="view_the_full_license">Ver la licencia completa</string>
|
||||||
<string name="yes">Sí</string>
|
<string name="yes">Sí</string>
|
||||||
|
<plurals name="files_saved_to" tools:ignore="MissingQuantity">
|
||||||
|
<item quantity="one">%2$s guardado en %3$s</item>
|
||||||
|
<item quantity="other">%1$d archivos guardados en %3$s</item>
|
||||||
|
</plurals>
|
||||||
<plurals name="page_count" tools:ignore="MissingQuantity">
|
<plurals name="page_count" tools:ignore="MissingQuantity">
|
||||||
<item quantity="one">%d página</item>
|
<item quantity="one">%d página</item>
|
||||||
<item quantity="other">%d páginas</item>
|
<item quantity="other">%d páginas</item>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<string name="contact">Contact</string>
|
<string name="contact">Contact</string>
|
||||||
<string name="copied_logs">Logs copiés dans le presse-papiers</string>
|
<string name="copied_logs">Logs copiés dans le presse-papiers</string>
|
||||||
<string name="copy_logs">Copier les logs</string>
|
<string name="copy_logs">Copier les logs</string>
|
||||||
<string name="creating_pdf">Création du PDF…</string>
|
<string name="creating_export">Préparation de l’export…</string>
|
||||||
<string name="delete_page">Supprimer la page</string>
|
<string name="delete_page">Supprimer la page</string>
|
||||||
<string name="delete_page_warning">Voulez-vous supprimer cette page ?</string>
|
<string name="delete_page_warning">Voulez-vous supprimer cette page ?</string>
|
||||||
<string name="developer">Développeur</string>
|
<string name="developer">Développeur</string>
|
||||||
@@ -19,12 +19,14 @@
|
|||||||
<string name="download_dirname">Téléchargements</string>
|
<string name="download_dirname">Téléchargements</string>
|
||||||
<string name="end_scan">Terminer le scan</string>
|
<string name="end_scan">Terminer le scan</string>
|
||||||
<string name="error">Erreur : %1$s</string>
|
<string name="error">Erreur : %1$s</string>
|
||||||
|
<string name="error_no_app">Aucune application trouvée pour ouvrir ce fichier</string>
|
||||||
<string name="error_no_document">Aucun document détecté</string>
|
<string name="error_no_document">Aucun document détecté</string>
|
||||||
<string name="error_no_pdf_app">Aucune application trouvée pour ouvrir un PDF</string>
|
<string name="error_save">Échec de l\'enregistrement du fichier</string>
|
||||||
<string name="error_save">Échec de l\'enregistrement du PDF</string>
|
<string name="export">Exporter</string>
|
||||||
|
<string name="export_as">Exporter en %1$s</string>
|
||||||
<string name="export_directory">Dossier d’export</string>
|
<string name="export_directory">Dossier d’export</string>
|
||||||
<string name="export_pdf">Exporter en PDF</string>
|
|
||||||
<string name="file_size">Taille du fichier : %1$s</string>
|
<string name="file_size">Taille du fichier : %1$s</string>
|
||||||
|
<string name="file_size_total">Taille totale : %1$s</string>
|
||||||
<string name="filename">Nom de fichier</string>
|
<string name="filename">Nom de fichier</string>
|
||||||
<string name="grant_permission">Autoriser</string>
|
<string name="grant_permission">Autoriser</string>
|
||||||
<string name="last_saved_pdf_files">Derniers PDF enregistrés sur l’appareil :</string>
|
<string name="last_saved_pdf_files">Derniers PDF enregistrés sur l’appareil :</string>
|
||||||
@@ -36,8 +38,7 @@
|
|||||||
<string name="menu">Menu</string>
|
<string name="menu">Menu</string>
|
||||||
<string name="new_document_warning">Le scan en cours sera perdu. Voulez-vous continuer ?</string>
|
<string name="new_document_warning">Le scan en cours sera perdu. Voulez-vous continuer ?</string>
|
||||||
<string name="open">Ouvrir</string>
|
<string name="open">Ouvrir</string>
|
||||||
<string name="open_pdf">Ouvrir le PDF</string>
|
<string name="open_file">Ouvrir le fichier</string>
|
||||||
<string name="pdf_saved_to">PDF enregistré dans %1s</string>
|
|
||||||
<string name="reset_to_default">Réinitialiser par défaut</string>
|
<string name="reset_to_default">Réinitialiser par défaut</string>
|
||||||
<string name="resume">Reprendre</string>
|
<string name="resume">Reprendre</string>
|
||||||
<string name="save">Enregistrer</string>
|
<string name="save">Enregistrer</string>
|
||||||
@@ -45,8 +46,8 @@
|
|||||||
<string name="scan_in_progress">Scan en cours</string>
|
<string name="scan_in_progress">Scan en cours</string>
|
||||||
<string name="settings">Paramètres</string>
|
<string name="settings">Paramètres</string>
|
||||||
<string name="share">Partager</string>
|
<string name="share">Partager</string>
|
||||||
<string name="share_pdf">Partager le PDF</string>
|
<string name="share_document">Partager le document</string>
|
||||||
<string name="storage_permission_denied">Impossible d’enregistrer le fichier PDF : permission refusée</string>
|
<string name="storage_permission_denied">Impossible d’enregistrer le fichier : permission refusée</string>
|
||||||
<string name="turn_off_torch">Éteindre la torche</string>
|
<string name="turn_off_torch">Éteindre la torche</string>
|
||||||
<string name="turn_on_torch">Allumer la torche</string>
|
<string name="turn_on_torch">Allumer la torche</string>
|
||||||
<string name="unknown_size">Taille inconnue</string>
|
<string name="unknown_size">Taille inconnue</string>
|
||||||
@@ -54,6 +55,10 @@
|
|||||||
<string name="view_full_list">Voir la liste complète</string>
|
<string name="view_full_list">Voir la liste complète</string>
|
||||||
<string name="view_the_full_license">Voir la licence complète</string>
|
<string name="view_the_full_license">Voir la licence complète</string>
|
||||||
<string name="yes">Oui</string>
|
<string name="yes">Oui</string>
|
||||||
|
<plurals name="files_saved_to" tools:ignore="MissingQuantity">
|
||||||
|
<item quantity="one">%2$s enregistré dans %3$s</item>
|
||||||
|
<item quantity="other">%1$d fichiers enregistrés dans %3$s</item>
|
||||||
|
</plurals>
|
||||||
<plurals name="page_count" tools:ignore="MissingQuantity">
|
<plurals name="page_count" tools:ignore="MissingQuantity">
|
||||||
<item quantity="one">%d page</item>
|
<item quantity="one">%d page</item>
|
||||||
<item quantity="other">%d pages</item>
|
<item quantity="other">%d pages</item>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<resources>
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="about">Informazioni</string>
|
<string name="about">Informazioni</string>
|
||||||
<string name="add_page">Aggiungi pagina</string>
|
<string name="add_page">Aggiungi pagina</string>
|
||||||
<string name="app_tagline">Un\'app semplice e rispettosa per scansionare i tuoi documenti.</string>
|
<string name="app_tagline">Un\'app semplice e rispettosa per scansionare i tuoi documenti.</string>
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
<string name="contact">Contatti</string>
|
<string name="contact">Contatti</string>
|
||||||
<string name="copied_logs">Log copiati negli appunti</string>
|
<string name="copied_logs">Log copiati negli appunti</string>
|
||||||
<string name="copy_logs">Copia log</string>
|
<string name="copy_logs">Copia log</string>
|
||||||
<string name="creating_pdf">Creazione PDF…</string>
|
<string name="creating_export">Preparazione dell’esportazione…</string>
|
||||||
<string name="delete_page">Elimina pagina</string>
|
<string name="delete_page">Elimina pagina</string>
|
||||||
<string name="delete_page_warning">Vuoi eliminare questa pagina?</string>
|
<string name="delete_page_warning">Vuoi eliminare questa pagina?</string>
|
||||||
<string name="developer">Sviluppatore</string>
|
<string name="developer">Sviluppatore</string>
|
||||||
@@ -19,12 +19,14 @@
|
|||||||
<string name="download_dirname">Download</string>
|
<string name="download_dirname">Download</string>
|
||||||
<string name="end_scan">Termina scansione</string>
|
<string name="end_scan">Termina scansione</string>
|
||||||
<string name="error">Errore: %1$s</string>
|
<string name="error">Errore: %1$s</string>
|
||||||
|
<string name="error_no_app">Nessuna app trovata per aprire questo file</string>
|
||||||
<string name="error_no_document">Nessun documento rilevato</string>
|
<string name="error_no_document">Nessun documento rilevato</string>
|
||||||
<string name="error_no_pdf_app">Nessuna app trovata per aprire PDF</string>
|
<string name="error_save">Impossibile salvare il file</string>
|
||||||
<string name="error_save">Salvataggio PDF fallito</string>
|
<string name="export">Esporta</string>
|
||||||
|
<string name="export_as">Esporta come %1$s</string>
|
||||||
<string name="export_directory">Cartella di esportazione</string>
|
<string name="export_directory">Cartella di esportazione</string>
|
||||||
<string name="export_pdf">Esporta PDF</string>
|
<string name="file_size">Dimensione del file: %1$s</string>
|
||||||
<string name="file_size">Dimensione file: %1$s</string>
|
<string name="file_size_total">Dimensione totale: %1$s</string>
|
||||||
<string name="filename">Nome file</string>
|
<string name="filename">Nome file</string>
|
||||||
<string name="grant_permission">Concendi autorizzazione</string>
|
<string name="grant_permission">Concendi autorizzazione</string>
|
||||||
<string name="last_saved_pdf_files">PDF recenti salvati su questo dispositivo:</string>
|
<string name="last_saved_pdf_files">PDF recenti salvati su questo dispositivo:</string>
|
||||||
@@ -36,8 +38,7 @@
|
|||||||
<string name="menu">Menu</string>
|
<string name="menu">Menu</string>
|
||||||
<string name="new_document_warning">La scansiona attuale verrà persa. Vuoi continuare?</string>
|
<string name="new_document_warning">La scansiona attuale verrà persa. Vuoi continuare?</string>
|
||||||
<string name="open">Apri</string>
|
<string name="open">Apri</string>
|
||||||
<string name="open_pdf">Apri PDF</string>
|
<string name="open_file">Apri file</string>
|
||||||
<string name="pdf_saved_to">PDF salvato in %1s</string>
|
|
||||||
<string name="reset_to_default">Ripristina impostazioni predefinite</string>
|
<string name="reset_to_default">Ripristina impostazioni predefinite</string>
|
||||||
<string name="resume">Riprendi</string>
|
<string name="resume">Riprendi</string>
|
||||||
<string name="save">Salva</string>
|
<string name="save">Salva</string>
|
||||||
@@ -45,8 +46,8 @@
|
|||||||
<string name="scan_in_progress">Scansione in corso</string>
|
<string name="scan_in_progress">Scansione in corso</string>
|
||||||
<string name="settings">Impostazioni</string>
|
<string name="settings">Impostazioni</string>
|
||||||
<string name="share">Condividi</string>
|
<string name="share">Condividi</string>
|
||||||
<string name="share_pdf">Condividi PDF</string>
|
<string name="share_document">Condividi documento</string>
|
||||||
<string name="storage_permission_denied">Impossibile salvare il file PDF: autorizzazione negata</string>
|
<string name="storage_permission_denied">Impossibile salvare il file: permesso negato</string>
|
||||||
<string name="turn_off_torch">Spegni la torcia</string>
|
<string name="turn_off_torch">Spegni la torcia</string>
|
||||||
<string name="turn_on_torch">Accendi la torcia</string>
|
<string name="turn_on_torch">Accendi la torcia</string>
|
||||||
<string name="unknown_size">Dimensione sconosciuta</string>
|
<string name="unknown_size">Dimensione sconosciuta</string>
|
||||||
@@ -54,7 +55,11 @@
|
|||||||
<string name="view_full_list">Vedi l\'elenco completo</string>
|
<string name="view_full_list">Vedi l\'elenco completo</string>
|
||||||
<string name="view_the_full_license">Vedi la licenza completa</string>
|
<string name="view_the_full_license">Vedi la licenza completa</string>
|
||||||
<string name="yes">Sì</string>
|
<string name="yes">Sì</string>
|
||||||
<plurals name="page_count">
|
<plurals name="files_saved_to" tools:ignore="MissingQuantity">
|
||||||
|
<item quantity="one">%2$s salvato in %3$s</item>
|
||||||
|
<item quantity="other">%1$d file salvati in %3$s</item>
|
||||||
|
</plurals>
|
||||||
|
<plurals name="page_count" tools:ignore="MissingQuantity">
|
||||||
<item quantity="one">%d pagina</item>
|
<item quantity="one">%d pagina</item>
|
||||||
<item quantity="other">%d pagine</item>
|
<item quantity="other">%d pagine</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<string name="contact">Contato</string>
|
<string name="contact">Contato</string>
|
||||||
<string name="copied_logs">Registros copiados para a área de transferência</string>
|
<string name="copied_logs">Registros copiados para a área de transferência</string>
|
||||||
<string name="copy_logs">Copiar registros</string>
|
<string name="copy_logs">Copiar registros</string>
|
||||||
<string name="creating_pdf">Criando PDF…</string>
|
<string name="creating_export">Preparando exportação…</string>
|
||||||
<string name="delete_page">Excluir página</string>
|
<string name="delete_page">Excluir página</string>
|
||||||
<string name="delete_page_warning">Deseja excluir esta página?</string>
|
<string name="delete_page_warning">Deseja excluir esta página?</string>
|
||||||
<string name="developer">Desenvolvedor</string>
|
<string name="developer">Desenvolvedor</string>
|
||||||
@@ -19,12 +19,14 @@
|
|||||||
<string name="download_dirname">Downloads</string>
|
<string name="download_dirname">Downloads</string>
|
||||||
<string name="end_scan">Finalizar digitalização</string>
|
<string name="end_scan">Finalizar digitalização</string>
|
||||||
<string name="error">Erro: %1$s</string>
|
<string name="error">Erro: %1$s</string>
|
||||||
|
<string name="error_no_app">Nenhum app encontrado para abrir este arquivo</string>
|
||||||
<string name="error_no_document">Nenhum documento detectado</string>
|
<string name="error_no_document">Nenhum documento detectado</string>
|
||||||
<string name="error_no_pdf_app">Nenhum aplicativo encontrado para abrir PDF</string>
|
<string name="error_save">Falha ao salvar o arquivo</string>
|
||||||
<string name="error_save">Falha ao salvar PDF</string>
|
<string name="export">Exportar</string>
|
||||||
|
<string name="export_as">Exportar como %1$s</string>
|
||||||
<string name="export_directory">Diretório de exportação</string>
|
<string name="export_directory">Diretório de exportação</string>
|
||||||
<string name="export_pdf">Exportar PDF</string>
|
|
||||||
<string name="file_size">Tamanho do arquivo: %1$s</string>
|
<string name="file_size">Tamanho do arquivo: %1$s</string>
|
||||||
|
<string name="file_size_total">Tamanho total: %1$s</string>
|
||||||
<string name="filename">Nome do arquivo</string>
|
<string name="filename">Nome do arquivo</string>
|
||||||
<string name="grant_permission">Conceder permissão</string>
|
<string name="grant_permission">Conceder permissão</string>
|
||||||
<string name="last_saved_pdf_files">PDFs recentes salvos neste dispositivo:</string>
|
<string name="last_saved_pdf_files">PDFs recentes salvos neste dispositivo:</string>
|
||||||
@@ -36,8 +38,7 @@
|
|||||||
<string name="menu">Menu</string>
|
<string name="menu">Menu</string>
|
||||||
<string name="new_document_warning">A digitalização atual será perdida. Deseja continuar?</string>
|
<string name="new_document_warning">A digitalização atual será perdida. Deseja continuar?</string>
|
||||||
<string name="open">Abrir</string>
|
<string name="open">Abrir</string>
|
||||||
<string name="open_pdf">Abrir PDF</string>
|
<string name="open_file">Abrir arquivo</string>
|
||||||
<string name="pdf_saved_to">PDF salvo em %1s</string>
|
|
||||||
<string name="reset_to_default">Restaurar padrão</string>
|
<string name="reset_to_default">Restaurar padrão</string>
|
||||||
<string name="resume">Retomar</string>
|
<string name="resume">Retomar</string>
|
||||||
<string name="save">Salvar</string>
|
<string name="save">Salvar</string>
|
||||||
@@ -45,8 +46,8 @@
|
|||||||
<string name="scan_in_progress">Digitalização em andamento</string>
|
<string name="scan_in_progress">Digitalização em andamento</string>
|
||||||
<string name="settings">Configurações</string>
|
<string name="settings">Configurações</string>
|
||||||
<string name="share">Compartilhar</string>
|
<string name="share">Compartilhar</string>
|
||||||
<string name="share_pdf">Compartilhar PDF</string>
|
<string name="share_document">Compartilhar documento</string>
|
||||||
<string name="storage_permission_denied">Não foi possível salvar o arquivo PDF: permissão negada</string>
|
<string name="storage_permission_denied">Não foi possível salvar o arquivo: permissão negada</string>
|
||||||
<string name="turn_off_torch">Desligar lanterna</string>
|
<string name="turn_off_torch">Desligar lanterna</string>
|
||||||
<string name="turn_on_torch">Ligar lanterna</string>
|
<string name="turn_on_torch">Ligar lanterna</string>
|
||||||
<string name="unknown_size">Tamanho desconhecido</string>
|
<string name="unknown_size">Tamanho desconhecido</string>
|
||||||
@@ -54,6 +55,10 @@
|
|||||||
<string name="view_full_list">Ver lista completa</string>
|
<string name="view_full_list">Ver lista completa</string>
|
||||||
<string name="view_the_full_license">Ver licença completa</string>
|
<string name="view_the_full_license">Ver licença completa</string>
|
||||||
<string name="yes">Sim</string>
|
<string name="yes">Sim</string>
|
||||||
|
<plurals name="files_saved_to" tools:ignore="MissingQuantity">
|
||||||
|
<item quantity="one">%2$s salvo em %3$s</item>
|
||||||
|
<item quantity="other">%1$d arquivos salvos em %3$s</item>
|
||||||
|
</plurals>
|
||||||
<plurals name="page_count" tools:ignore="MissingQuantity">
|
<plurals name="page_count" tools:ignore="MissingQuantity">
|
||||||
<item quantity="one">%d página</item>
|
<item quantity="one">%d página</item>
|
||||||
<item quantity="other">%d páginas</item>
|
<item quantity="other">%d páginas</item>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<string name="contact">Контакты</string>
|
<string name="contact">Контакты</string>
|
||||||
<string name="copied_logs">Журналы скопированы в буфер обмена</string>
|
<string name="copied_logs">Журналы скопированы в буфер обмена</string>
|
||||||
<string name="copy_logs">Копировать журналы</string>
|
<string name="copy_logs">Копировать журналы</string>
|
||||||
<string name="creating_pdf">Создание PDF…</string>
|
<string name="creating_export">Подготовка экспорта…</string>
|
||||||
<string name="delete_page">Удалить страницу</string>
|
<string name="delete_page">Удалить страницу</string>
|
||||||
<string name="delete_page_warning">Вы желаете удалить эту страницу?</string>
|
<string name="delete_page_warning">Вы желаете удалить эту страницу?</string>
|
||||||
<string name="developer">Разработчик</string>
|
<string name="developer">Разработчик</string>
|
||||||
@@ -19,12 +19,14 @@
|
|||||||
<string name="download_dirname">Download</string>
|
<string name="download_dirname">Download</string>
|
||||||
<string name="end_scan">Закончить</string>
|
<string name="end_scan">Закончить</string>
|
||||||
<string name="error">Ошибка: %1$s</string>
|
<string name="error">Ошибка: %1$s</string>
|
||||||
|
<string name="error_no_app">Не найдено приложение для открытия этого файла</string>
|
||||||
<string name="error_no_document">Документ не обнаружен</string>
|
<string name="error_no_document">Документ не обнаружен</string>
|
||||||
<string name="error_no_pdf_app">Приложения для работы с PDF не обнаружено</string>
|
<string name="error_save">Не удалось сохранить файл</string>
|
||||||
<string name="error_save">Сбой при сохранении PDF</string>
|
<string name="export">Экспорт</string>
|
||||||
|
<string name="export_as">Экспортировать как %1$s</string>
|
||||||
<string name="export_directory">Папка экспорта</string>
|
<string name="export_directory">Папка экспорта</string>
|
||||||
<string name="export_pdf">Экспорт PDF</string>
|
|
||||||
<string name="file_size">Размер файла: %1$s</string>
|
<string name="file_size">Размер файла: %1$s</string>
|
||||||
|
<string name="file_size_total">Общий размер: %1$s</string>
|
||||||
<string name="filename">Имя файла</string>
|
<string name="filename">Имя файла</string>
|
||||||
<string name="grant_permission">Предоставить разрешение</string>
|
<string name="grant_permission">Предоставить разрешение</string>
|
||||||
<string name="last_saved_pdf_files">Последние PDF, сохранённые на этом устройстве:</string>
|
<string name="last_saved_pdf_files">Последние PDF, сохранённые на этом устройстве:</string>
|
||||||
@@ -36,8 +38,7 @@
|
|||||||
<string name="menu">Меню</string>
|
<string name="menu">Меню</string>
|
||||||
<string name="new_document_warning">Результаты текущего сканирования будут потеряны. Желаете продолжить?</string>
|
<string name="new_document_warning">Результаты текущего сканирования будут потеряны. Желаете продолжить?</string>
|
||||||
<string name="open">Открыть</string>
|
<string name="open">Открыть</string>
|
||||||
<string name="open_pdf">Открыть PDF</string>
|
<string name="open_file">Открыть файл</string>
|
||||||
<string name="pdf_saved_to">PDF сохранен в %1s</string>
|
|
||||||
<string name="reset_to_default">Сбросить по умолчанию</string>
|
<string name="reset_to_default">Сбросить по умолчанию</string>
|
||||||
<string name="resume">Продолжить</string>
|
<string name="resume">Продолжить</string>
|
||||||
<string name="save">Сохранить</string>
|
<string name="save">Сохранить</string>
|
||||||
@@ -45,8 +46,8 @@
|
|||||||
<string name="scan_in_progress">Сканирование выполняется</string>
|
<string name="scan_in_progress">Сканирование выполняется</string>
|
||||||
<string name="settings">Настройки</string>
|
<string name="settings">Настройки</string>
|
||||||
<string name="share">Поделиться</string>
|
<string name="share">Поделиться</string>
|
||||||
<string name="share_pdf">Поделиться PDF</string>
|
<string name="share_document">Поделиться документом</string>
|
||||||
<string name="storage_permission_denied">Не удается сохранить файл PDF: в разрешении отказано</string>
|
<string name="storage_permission_denied">Невозможно сохранить файл: доступ запрещён</string>
|
||||||
<string name="turn_off_torch">Выключить фонарик</string>
|
<string name="turn_off_torch">Выключить фонарик</string>
|
||||||
<string name="turn_on_torch">Включить фонарик</string>
|
<string name="turn_on_torch">Включить фонарик</string>
|
||||||
<string name="unknown_size">Неизвестный размер</string>
|
<string name="unknown_size">Неизвестный размер</string>
|
||||||
@@ -54,6 +55,12 @@
|
|||||||
<string name="view_full_list">Просмотреть полный список</string>
|
<string name="view_full_list">Просмотреть полный список</string>
|
||||||
<string name="view_the_full_license">Просмотреть полную лицензию</string>
|
<string name="view_the_full_license">Просмотреть полную лицензию</string>
|
||||||
<string name="yes">Да</string>
|
<string name="yes">Да</string>
|
||||||
|
<plurals name="files_saved_to">
|
||||||
|
<item quantity="one">%2$s сохранён в %3$s</item>
|
||||||
|
<item quantity="few">%1$d файла сохранены в %3$s</item>
|
||||||
|
<item quantity="many">%1$d файлов сохранено в %3$s</item>
|
||||||
|
<item quantity="other">%1$d файла сохранено в %3$s</item>
|
||||||
|
</plurals>
|
||||||
<plurals name="page_count">
|
<plurals name="page_count">
|
||||||
<item quantity="one">%d страница</item>
|
<item quantity="one">%d страница</item>
|
||||||
<item quantity="few">%d страницы</item>
|
<item quantity="few">%d страницы</item>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<string name="contact">联系人</string>
|
<string name="contact">联系人</string>
|
||||||
<string name="copied_logs">日志已复制到剪贴板</string>
|
<string name="copied_logs">日志已复制到剪贴板</string>
|
||||||
<string name="copy_logs">复制日志</string>
|
<string name="copy_logs">复制日志</string>
|
||||||
<string name="creating_pdf">正在创建 PDF…</string>
|
<string name="creating_export">正在准备导出…</string>
|
||||||
<string name="delete_page">删除页面</string>
|
<string name="delete_page">删除页面</string>
|
||||||
<string name="delete_page_warning">是否要删除此页面?</string>
|
<string name="delete_page_warning">是否要删除此页面?</string>
|
||||||
<string name="developer">开发者</string>
|
<string name="developer">开发者</string>
|
||||||
@@ -19,12 +19,14 @@
|
|||||||
<string name="download_dirname">下载</string>
|
<string name="download_dirname">下载</string>
|
||||||
<string name="end_scan">结束扫描</string>
|
<string name="end_scan">结束扫描</string>
|
||||||
<string name="error">错误: %1$s</string>
|
<string name="error">错误: %1$s</string>
|
||||||
|
<string name="error_no_app">未找到可打开此文件的应用</string>
|
||||||
<string name="error_no_document">未检测到任何文档</string>
|
<string name="error_no_document">未检测到任何文档</string>
|
||||||
<string name="error_no_pdf_app">未找到可打开PDF的应用</string>
|
<string name="error_save">无法保存文件</string>
|
||||||
<string name="error_save">保存PDF失败</string>
|
<string name="export">导出</string>
|
||||||
|
<string name="export_as">导出为 %1$s</string>
|
||||||
<string name="export_directory">导出目录</string>
|
<string name="export_directory">导出目录</string>
|
||||||
<string name="export_pdf">导出PDF</string>
|
<string name="file_size">文件大小:%1$s</string>
|
||||||
<string name="file_size">文件大小: %1$s</string>
|
<string name="file_size_total">总大小:%1$s</string>
|
||||||
<string name="filename">文件名字</string>
|
<string name="filename">文件名字</string>
|
||||||
<string name="grant_permission">授予权限</string>
|
<string name="grant_permission">授予权限</string>
|
||||||
<string name="last_saved_pdf_files">最近保存在此设备上的 PDF:</string>
|
<string name="last_saved_pdf_files">最近保存在此设备上的 PDF:</string>
|
||||||
@@ -36,8 +38,7 @@
|
|||||||
<string name="menu">菜单</string>
|
<string name="menu">菜单</string>
|
||||||
<string name="new_document_warning">当前扫描将丢失。是否继续?</string>
|
<string name="new_document_warning">当前扫描将丢失。是否继续?</string>
|
||||||
<string name="open">打开</string>
|
<string name="open">打开</string>
|
||||||
<string name="open_pdf">打开 PDF</string>
|
<string name="open_file">打开文件</string>
|
||||||
<string name="pdf_saved_to">PDF 已保存到 %1$s</string>
|
|
||||||
<string name="reset_to_default">恢复默认设置</string>
|
<string name="reset_to_default">恢复默认设置</string>
|
||||||
<string name="resume">恢复</string>
|
<string name="resume">恢复</string>
|
||||||
<string name="save">保存</string>
|
<string name="save">保存</string>
|
||||||
@@ -45,8 +46,8 @@
|
|||||||
<string name="scan_in_progress">正在进行扫描</string>
|
<string name="scan_in_progress">正在进行扫描</string>
|
||||||
<string name="settings">设置</string>
|
<string name="settings">设置</string>
|
||||||
<string name="share">共享</string>
|
<string name="share">共享</string>
|
||||||
<string name="share_pdf">共享 PDF</string>
|
<string name="share_document">分享文档</string>
|
||||||
<string name="storage_permission_denied">无法保存PDF文件:权限被拒绝</string>
|
<string name="storage_permission_denied">无法保存文件:权限被拒绝</string>
|
||||||
<string name="turn_off_torch">关闭手电筒</string>
|
<string name="turn_off_torch">关闭手电筒</string>
|
||||||
<string name="turn_on_torch">打开手电筒</string>
|
<string name="turn_on_torch">打开手电筒</string>
|
||||||
<string name="unknown_size">未知大小</string>
|
<string name="unknown_size">未知大小</string>
|
||||||
@@ -54,8 +55,10 @@
|
|||||||
<string name="view_full_list">查看完整列表</string>
|
<string name="view_full_list">查看完整列表</string>
|
||||||
<string name="view_the_full_license">查看完整许可证</string>
|
<string name="view_the_full_license">查看完整许可证</string>
|
||||||
<string name="yes">是</string>
|
<string name="yes">是</string>
|
||||||
|
<plurals name="files_saved_to">
|
||||||
|
<item quantity="other">%1$d 个文件已保存到 %3$s</item>
|
||||||
|
</plurals>
|
||||||
<plurals name="page_count">
|
<plurals name="page_count">
|
||||||
<item quantity="one">%d 页</item>
|
|
||||||
<item quantity="other">%d 页</item>
|
<item quantity="other">%d 页</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<string name="contact">Contact</string>
|
<string name="contact">Contact</string>
|
||||||
<string name="copied_logs">Logs copied to clipboard</string>
|
<string name="copied_logs">Logs copied to clipboard</string>
|
||||||
<string name="copy_logs">Copy logs</string>
|
<string name="copy_logs">Copy logs</string>
|
||||||
<string name="creating_pdf">Creating PDF…</string>
|
<string name="creating_export">Preparing export…</string>
|
||||||
<string name="delete_page">Delete page</string>
|
<string name="delete_page">Delete page</string>
|
||||||
<string name="delete_page_warning">Do you want to delete this page?</string>
|
<string name="delete_page_warning">Do you want to delete this page?</string>
|
||||||
<string name="developer">Developer</string>
|
<string name="developer">Developer</string>
|
||||||
@@ -20,12 +20,14 @@
|
|||||||
<string name="download_dirname">Downloads</string>
|
<string name="download_dirname">Downloads</string>
|
||||||
<string name="end_scan">End scan</string>
|
<string name="end_scan">End scan</string>
|
||||||
<string name="error">Error: %1$s</string>
|
<string name="error">Error: %1$s</string>
|
||||||
|
<string name="error_no_app">No app found to open this file</string>
|
||||||
<string name="error_no_document">No document detected</string>
|
<string name="error_no_document">No document detected</string>
|
||||||
<string name="error_no_pdf_app">No app found to open PDF</string>
|
<string name="error_save">Failed to save file</string>
|
||||||
<string name="error_save">Failed to save PDF</string>
|
<string name="export">Export</string>
|
||||||
|
<string name="export_as">Export as %1$s</string>
|
||||||
<string name="export_directory">Export directory</string>
|
<string name="export_directory">Export directory</string>
|
||||||
<string name="export_pdf">Export PDF</string>
|
|
||||||
<string name="file_size">File size: %1$s</string>
|
<string name="file_size">File size: %1$s</string>
|
||||||
|
<string name="file_size_total">Total size: %1$s</string>
|
||||||
<string name="filename">Filename</string>
|
<string name="filename">Filename</string>
|
||||||
<string name="grant_permission">Grant permission</string>
|
<string name="grant_permission">Grant permission</string>
|
||||||
<string name="last_saved_pdf_files">Recent PDFs saved on this device:</string>
|
<string name="last_saved_pdf_files">Recent PDFs saved on this device:</string>
|
||||||
@@ -37,8 +39,7 @@
|
|||||||
<string name="menu">Menu</string>
|
<string name="menu">Menu</string>
|
||||||
<string name="new_document_warning">The current scan will be lost. Do you want to continue?</string>
|
<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">Open</string>
|
||||||
<string name="open_pdf">Open PDF</string>
|
<string name="open_file">Open file</string>
|
||||||
<string name="pdf_saved_to">PDF saved in %1s</string>
|
|
||||||
<string name="reset_to_default">Reset to default</string>
|
<string name="reset_to_default">Reset to default</string>
|
||||||
<string name="resume">Resume</string>
|
<string name="resume">Resume</string>
|
||||||
<string name="save">Save</string>
|
<string name="save">Save</string>
|
||||||
@@ -46,8 +47,8 @@
|
|||||||
<string name="scan_in_progress">Scan in progress</string>
|
<string name="scan_in_progress">Scan in progress</string>
|
||||||
<string name="settings">Settings</string>
|
<string name="settings">Settings</string>
|
||||||
<string name="share">Share</string>
|
<string name="share">Share</string>
|
||||||
<string name="share_pdf">Share PDF</string>
|
<string name="share_document">Share document</string>
|
||||||
<string name="storage_permission_denied">Cannot save PDF file: permission was denied</string>
|
<string name="storage_permission_denied">Cannot save file: permission was denied</string>
|
||||||
<string name="turn_off_torch">Turn off torch</string>
|
<string name="turn_off_torch">Turn off torch</string>
|
||||||
<string name="turn_on_torch">Turn on torch</string>
|
<string name="turn_on_torch">Turn on torch</string>
|
||||||
<string name="unknown_size">Unknown size</string>
|
<string name="unknown_size">Unknown size</string>
|
||||||
@@ -55,6 +56,10 @@
|
|||||||
<string name="view_full_list">View full list</string>
|
<string name="view_full_list">View full list</string>
|
||||||
<string name="view_the_full_license">View the full license</string>
|
<string name="view_the_full_license">View the full license</string>
|
||||||
<string name="yes">Yes</string>
|
<string name="yes">Yes</string>
|
||||||
|
<plurals name="files_saved_to">
|
||||||
|
<item quantity="one">%2$s saved to %3$s</item>
|
||||||
|
<item quantity="other">%1$d files saved to %3$s</item>
|
||||||
|
</plurals>
|
||||||
<plurals name="page_count">
|
<plurals name="page_count">
|
||||||
<item quantity="one">%d page</item>
|
<item quantity="one">%d page</item>
|
||||||
<item quantity="other">%d pages</item>
|
<item quantity="other">%d pages</item>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import java.io.File
|
|||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import kotlin.io.path.createTempDirectory
|
import kotlin.io.path.createTempDirectory
|
||||||
|
|
||||||
class PdfFileManagerTest {
|
class FileManagerTest {
|
||||||
|
|
||||||
val pdfDir: File = createTempDirectory().toFile()
|
val pdfDir: File = createTempDirectory().toFile()
|
||||||
val externalDir: File = createTempDirectory().toFile()
|
val externalDir: File = createTempDirectory().toFile()
|
||||||
@@ -33,7 +33,7 @@ class PdfFileManagerTest {
|
|||||||
val f = File(externalDir, "f.pdf")
|
val f = File(externalDir, "f.pdf")
|
||||||
assertThat(f).doesNotExist()
|
assertThat(f).doesNotExist()
|
||||||
|
|
||||||
val manager = PdfFileManager(pdfDir, externalDir, dummyPdfWriter)
|
val manager = FileManager(pdfDir, externalDir, dummyPdfWriter)
|
||||||
assertThat(manager.copyToExternalDir(original))
|
assertThat(manager.copyToExternalDir(original))
|
||||||
.isEqualTo(f)
|
.isEqualTo(f)
|
||||||
.hasContent("original content")
|
.hasContent("original content")
|
||||||
@@ -48,7 +48,7 @@ class PdfFileManagerTest {
|
|||||||
@Test
|
@Test
|
||||||
fun cleanUpOldFiles() {
|
fun cleanUpOldFiles() {
|
||||||
val subDir = File(pdfDir,"subDir")
|
val subDir = File(pdfDir,"subDir")
|
||||||
val manager = PdfFileManager(subDir, externalDir, dummyPdfWriter)
|
val manager = FileManager(subDir, externalDir, dummyPdfWriter)
|
||||||
manager.cleanUpOldFiles(10)
|
manager.cleanUpOldFiles(10)
|
||||||
assertThat(subDir).doesNotExist()
|
assertThat(subDir).doesNotExist()
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ class PdfFileManagerTest {
|
|||||||
return list.size
|
return list.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val manager = PdfFileManager(pdfDir, externalDir, fakePdfWriter)
|
val manager = FileManager(pdfDir, externalDir, fakePdfWriter)
|
||||||
val jpegs = listOf(byteArrayOf(0x01, 0x02), byteArrayOf(0x11)).asSequence()
|
val jpegs = listOf(byteArrayOf(0x01, 0x02), byteArrayOf(0x11)).asSequence()
|
||||||
val pdf = manager.generatePdf(jpegs)
|
val pdf = manager.generatePdf(jpegs)
|
||||||
assertThat(pdf.pageCount).isEqualTo(2)
|
assertThat(pdf.pageCount).isEqualTo(2)
|
||||||
@@ -87,9 +87,9 @@ class PdfFileManagerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun addExtensionIfMissing() {
|
fun addExtensionIfMissing() {
|
||||||
assertThat(PdfFileManager.addExtensionIfMissing("f1.pdf")).isEqualTo("f1.pdf")
|
assertThat(FileManager.addPdfExtensionIfMissing("f1.pdf")).isEqualTo("f1.pdf")
|
||||||
assertThat(PdfFileManager.addExtensionIfMissing("f2.PDF")).isEqualTo("f2.PDF")
|
assertThat(FileManager.addPdfExtensionIfMissing("f2.PDF")).isEqualTo("f2.PDF")
|
||||||
assertThat(PdfFileManager.addExtensionIfMissing("f3")).isEqualTo("f3.pdf")
|
assertThat(FileManager.addPdfExtensionIfMissing("f3")).isEqualTo("f3.pdf")
|
||||||
assertThat(PdfFileManager.addExtensionIfMissing("f4.txt")).isEqualTo("f4.txt.pdf")
|
assertThat(FileManager.addPdfExtensionIfMissing("f4.txt")).isEqualTo("f4.txt.pdf")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user