Fix list of recent documents after introduction of the custom export dir
This commit is contained in:
committed by
pynicolas
parent
5d7011614b
commit
2d15cd129e
@@ -72,7 +72,7 @@ class AppContainer(context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val mainViewModelFactory = viewModelFactory { MainViewModel(it) }
|
val mainViewModelFactory = viewModelFactory { MainViewModel(it) }
|
||||||
val homeViewModelFactory = viewModelFactory { HomeViewModel(it) }
|
val homeViewModelFactory = viewModelFactory { HomeViewModel(it, context) }
|
||||||
val cameraViewModelFactory = viewModelFactory { CameraViewModel(it) }
|
val cameraViewModelFactory = viewModelFactory { CameraViewModel(it) }
|
||||||
val exportViewModelFactory = viewModelFactory { ExportViewModel(it) }
|
val exportViewModelFactory = viewModelFactory { ExportViewModel(it) }
|
||||||
val aboutViewModelFactory = viewModelFactory { AboutViewModel(it) }
|
val aboutViewModelFactory = viewModelFactory { AboutViewModel(it) }
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
navigation = navigation,
|
navigation = navigation,
|
||||||
onClearScan = { viewModel.startNewDocument() },
|
onClearScan = { viewModel.startNewDocument() },
|
||||||
recentDocuments = recentDocs,
|
recentDocuments = recentDocs,
|
||||||
onOpenPdf = { file -> openPdf(file.toUri()) }
|
onOpenPdf = { fileUri -> openPdf(fileUri) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is Screen.Main.Camera -> {
|
is Screen.Main.Camera -> {
|
||||||
|
|||||||
@@ -145,13 +145,17 @@ class ExportViewModel(container: AppContainer): ViewModel() {
|
|||||||
val exportDir = settingsRepository.exportDirUri.first()
|
val exportDir = settingsRepository.exportDirUri.first()
|
||||||
var fileInDownloads: File? = null
|
var fileInDownloads: File? = null
|
||||||
|
|
||||||
val savedUri: Uri =
|
var savedName: String
|
||||||
if (exportDir == null) {
|
val savedUri: Uri
|
||||||
fileInDownloads = pdfFileManager.copyToExternalDir(pdf.file)
|
if (exportDir == null) {
|
||||||
fileInDownloads.toUri()
|
fileInDownloads = pdfFileManager.copyToExternalDir(pdf.file)
|
||||||
} else {
|
savedUri = fileInDownloads.toUri()
|
||||||
copyViaSaf(context, pdf.file, exportDir.toUri())
|
savedName = fileInDownloads.name
|
||||||
}
|
} else {
|
||||||
|
val saved = copyViaSaf(context, pdf.file, exportDir.toUri())
|
||||||
|
savedUri = saved.uri
|
||||||
|
savedName = saved.name?:pdf.file.name
|
||||||
|
}
|
||||||
|
|
||||||
_pdfUiState.update {
|
_pdfUiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
@@ -162,11 +166,7 @@ class ExportViewModel(container: AppContainer): ViewModel() {
|
|||||||
fileInDownloads?.let { mediaScan(context, it) }
|
fileInDownloads?.let { mediaScan(context, it) }
|
||||||
|
|
||||||
// TODO remove that call: that should be handled through the ExportEvent
|
// TODO remove that call: that should be handled through the ExportEvent
|
||||||
homeViewModel.addRecentDocument(
|
homeViewModel.addRecentDocument(savedUri, savedName, pdf.pageCount)
|
||||||
// FIXME This is not a file path
|
|
||||||
savedUri.toString(),
|
|
||||||
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)
|
||||||
@@ -187,7 +187,7 @@ class ExportViewModel(container: AppContainer): ViewModel() {
|
|||||||
context: Context,
|
context: Context,
|
||||||
source: File,
|
source: File,
|
||||||
exportDirUri: Uri,
|
exportDirUri: Uri,
|
||||||
): Uri {
|
): DocumentFile {
|
||||||
val resolver = context.contentResolver
|
val resolver = context.contentResolver
|
||||||
|
|
||||||
val tree = DocumentFile.fromTreeUri(context, exportDirUri)
|
val tree = DocumentFile.fromTreeUri(context, exportDirUri)
|
||||||
@@ -203,7 +203,7 @@ class ExportViewModel(container: AppContainer): ViewModel() {
|
|||||||
}
|
}
|
||||||
} ?: throw IllegalStateException("Failed to open SAF output stream")
|
} ?: throw IllegalStateException("Failed to open SAF output stream")
|
||||||
|
|
||||||
return target.uri
|
return target
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cleanUpOldPdfs(thresholdInMillis: Int) {
|
fun cleanUpOldPdfs(thresholdInMillis: Int) {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.fairscan.app.ui.screens.home
|
package org.fairscan.app.ui.screens.home
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -54,6 +55,7 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
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 kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import org.fairscan.app.R
|
import org.fairscan.app.R
|
||||||
import org.fairscan.app.ui.Navigation
|
import org.fairscan.app.ui.Navigation
|
||||||
@@ -76,7 +78,7 @@ fun HomeScreen(
|
|||||||
navigation: Navigation,
|
navigation: Navigation,
|
||||||
onClearScan: () -> Unit,
|
onClearScan: () -> Unit,
|
||||||
recentDocuments: List<RecentDocumentUiState>,
|
recentDocuments: List<RecentDocumentUiState>,
|
||||||
onOpenPdf: (File) -> Unit,
|
onOpenPdf: (Uri) -> Unit,
|
||||||
) {
|
) {
|
||||||
Scaffold (
|
Scaffold (
|
||||||
topBar = {
|
topBar = {
|
||||||
@@ -227,7 +229,7 @@ fun OngoingScanBanner(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun RecentDocumentList(
|
private fun RecentDocumentList(
|
||||||
recentDocuments: List<RecentDocumentUiState>,
|
recentDocuments: List<RecentDocumentUiState>,
|
||||||
onOpenPdf: (File) -> Unit
|
onOpenPdf: (Uri) -> Unit
|
||||||
) {
|
) {
|
||||||
Spacer(Modifier.height(8.dp))
|
Spacer(Modifier.height(8.dp))
|
||||||
Text(
|
Text(
|
||||||
@@ -241,7 +243,7 @@ private fun RecentDocumentList(
|
|||||||
ListItem(
|
ListItem(
|
||||||
headlineContent = {
|
headlineContent = {
|
||||||
Text(
|
Text(
|
||||||
doc.file.name,
|
doc.fileName,
|
||||||
style = MaterialTheme.typography.bodyMedium
|
style = MaterialTheme.typography.bodyMedium
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -260,7 +262,7 @@ private fun RecentDocumentList(
|
|||||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
modifier = Modifier.clickable { onOpenPdf(doc.file) }
|
modifier = Modifier.clickable { onOpenPdf(doc.fileUri) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -309,8 +311,10 @@ fun HomeScreenPreviewWithLastSavedFiles() {
|
|||||||
navigation = dummyNavigation(),
|
navigation = dummyNavigation(),
|
||||||
onClearScan = {},
|
onClearScan = {},
|
||||||
recentDocuments = listOf(
|
recentDocuments = listOf(
|
||||||
RecentDocumentUiState(File("/path/my_file.pdf"), 1755971180000, 3),
|
RecentDocumentUiState(
|
||||||
RecentDocumentUiState(File("/path/scan2.pdf"), 1755000500000, 1)
|
File("/path/my_file.pdf").toUri(), "my_file.pdf", 1755971180000, 3),
|
||||||
|
RecentDocumentUiState(
|
||||||
|
"content:///path/scan2.pdf".toUri(), "scan2.pdf",1755000500000, 1)
|
||||||
),
|
),
|
||||||
onOpenPdf = {},
|
onOpenPdf = {},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,10 +14,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.fairscan.app.ui.screens.home
|
package org.fairscan.app.ui.screens.home
|
||||||
|
|
||||||
import java.io.File
|
import android.net.Uri
|
||||||
|
|
||||||
data class RecentDocumentUiState(
|
data class RecentDocumentUiState(
|
||||||
val file: File,
|
val fileUri: Uri,
|
||||||
|
val fileName: String,
|
||||||
val saveTimestamp: Long,
|
val saveTimestamp: Long,
|
||||||
val pageCount: Int,
|
val pageCount: Int,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,6 +14,10 @@
|
|||||||
*/
|
*/
|
||||||
package org.fairscan.app.ui.screens.home
|
package org.fairscan.app.ui.screens.home
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.core.net.toUri
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
@@ -25,30 +29,45 @@ import org.fairscan.app.AppContainer
|
|||||||
import org.fairscan.app.RecentDocument
|
import org.fairscan.app.RecentDocument
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class HomeViewModel(appContainer: AppContainer): ViewModel() {
|
class HomeViewModel(appContainer: AppContainer, appContext: Context): ViewModel() {
|
||||||
|
|
||||||
private val recentDocumentsDataStore = appContainer.recentDocumentsDataStore
|
private val recentDocumentsDataStore = appContainer.recentDocumentsDataStore
|
||||||
|
|
||||||
val recentDocuments: StateFlow<List<RecentDocumentUiState>> =
|
val recentDocuments: StateFlow<List<RecentDocumentUiState>> =
|
||||||
recentDocumentsDataStore.data.map {
|
recentDocumentsDataStore.data.map {
|
||||||
it.documentsList.map {
|
it.documentsList.mapNotNull { doc ->
|
||||||
doc ->
|
var fileName = doc.fileName
|
||||||
RecentDocumentUiState(
|
var uri: Uri? = null
|
||||||
file = File(doc.filePath),
|
if (doc.fileUri.isNullOrEmpty()) {
|
||||||
saveTimestamp = doc.createdAt,
|
if (!doc.filePath.isNullOrEmpty()) {
|
||||||
pageCount = doc.pageCount,
|
val file = File(doc.filePath)
|
||||||
)
|
uri = file.toUri()
|
||||||
}.filter { doc -> doc.file.exists() }
|
fileName = file.name
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uri = doc.fileUri.toUri()
|
||||||
|
}
|
||||||
|
if (uri != null) {
|
||||||
|
RecentDocumentUiState(
|
||||||
|
fileUri = uri,
|
||||||
|
fileName = fileName,
|
||||||
|
saveTimestamp = doc.createdAt,
|
||||||
|
pageCount = doc.pageCount,
|
||||||
|
)
|
||||||
|
} else null
|
||||||
|
}.filter { item -> uriExists(appContext, item.fileUri) }
|
||||||
}.stateIn(
|
}.stateIn(
|
||||||
scope = viewModelScope,
|
scope = viewModelScope,
|
||||||
started = SharingStarted.WhileSubscribed(5_000),
|
started = SharingStarted.WhileSubscribed(5_000),
|
||||||
initialValue = emptyList(),
|
initialValue = emptyList(),
|
||||||
)
|
)
|
||||||
fun addRecentDocument(filePath: String, pageCount: Int) {
|
|
||||||
|
fun addRecentDocument(fileUri: Uri, fileName: String, pageCount: Int) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
recentDocumentsDataStore.updateData { current ->
|
recentDocumentsDataStore.updateData { current ->
|
||||||
val newDoc = RecentDocument.newBuilder()
|
val newDoc = RecentDocument.newBuilder()
|
||||||
.setFilePath(filePath)
|
.setFileUri(fileUri.toString())
|
||||||
|
.setFileName(fileName)
|
||||||
.setPageCount(pageCount)
|
.setPageCount(pageCount)
|
||||||
.setCreatedAt(System.currentTimeMillis())
|
.setCreatedAt(System.currentTimeMillis())
|
||||||
.build()
|
.build()
|
||||||
@@ -64,4 +83,16 @@ class HomeViewModel(appContainer: AppContainer): ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun uriExists(context: Context, uri: Uri): Boolean {
|
||||||
|
return if (uri.scheme == "file") {
|
||||||
|
File(uri.path.orEmpty()).exists()
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
DocumentFile.fromSingleUri(context, uri)?.exists() == true
|
||||||
|
} catch (_: Exception) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ message RecentDocument {
|
|||||||
string file_path = 1;
|
string file_path = 1;
|
||||||
int64 created_at = 2; // timestamp in ms
|
int64 created_at = 2; // timestamp in ms
|
||||||
int32 page_count = 3;
|
int32 page_count = 3;
|
||||||
|
string file_uri = 4;
|
||||||
|
string file_name = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RecentDocuments {
|
message RecentDocuments {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<string name="file_size">Velikost souboru: %1$s</string>
|
<string name="file_size">Velikost souboru: %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é do stažených:</string>
|
<string name="last_saved_pdf_files">Poslední PDF uložené v tomto zařízení:</string>
|
||||||
<string name="libraries">Kníhovny</string>
|
<string name="libraries">Kníhovny</string>
|
||||||
<string name="libraries_intro">Tato aplikace využívá několik open-source knihoven včetně:</string>
|
<string name="libraries_intro">Tato aplikace využívá několik open-source knihoven včetně:</string>
|
||||||
<string name="libraries_open_source">Open-source knihovny</string>
|
<string name="libraries_open_source">Open-source knihovny</string>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<string name="file_size">Dateigröße: %1$s</string>
|
<string name="file_size">Dateigröß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 gespeicherte PDFs in Downloads:</string>
|
<string name="last_saved_pdf_files">Zuletzt auf diesem Gerät gespeicherte PDFs:</string>
|
||||||
<string name="libraries">Bibliotheken</string>
|
<string name="libraries">Bibliotheken</string>
|
||||||
<string name="libraries_intro">Diese Anwendung verwendet mehrere Open-Source-Bibliotheken, darunter:</string>
|
<string name="libraries_intro">Diese Anwendung verwendet mehrere Open-Source-Bibliotheken, darunter:</string>
|
||||||
<string name="libraries_open_source">Open-Source-Bibliotheken</string>
|
<string name="libraries_open_source">Open-Source-Bibliotheken</string>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<string name="file_size">Tamaño del archivo: %1$s</string>
|
<string name="file_size">Tamaño del archivo: %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 Descargas:</string>
|
<string name="last_saved_pdf_files">PDF recientes guardados en este dispositivo:</string>
|
||||||
<string name="libraries">Bibliotecas</string>
|
<string name="libraries">Bibliotecas</string>
|
||||||
<string name="libraries_intro">Esta aplicación utiliza varias bibliotecas de código abierto, incluidas:</string>
|
<string name="libraries_intro">Esta aplicación utiliza varias bibliotecas de código abierto, incluidas:</string>
|
||||||
<string name="libraries_open_source">Bibliotecas de código abierto</string>
|
<string name="libraries_open_source">Bibliotecas de código abierto</string>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<string name="file_size">Taille du fichier : %1$s</string>
|
<string name="file_size">Taille du fichier : %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 dans Téléchargements :</string>
|
<string name="last_saved_pdf_files">Derniers PDF enregistrés sur l’appareil :</string>
|
||||||
<string name="libraries">Bibliothèques</string>
|
<string name="libraries">Bibliothèques</string>
|
||||||
<string name="libraries_intro">Cette application utilise plusieurs bibliothèques open source, notamment :</string>
|
<string name="libraries_intro">Cette application utilise plusieurs bibliothèques open source, notamment :</string>
|
||||||
<string name="libraries_open_source">Bibliothèques open source</string>
|
<string name="libraries_open_source">Bibliothèques open source</string>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<string name="file_size">Dimensione file: %1$s</string>
|
<string name="file_size">Dimensione file: %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 in Download:</string>
|
<string name="last_saved_pdf_files">PDF recenti salvati su questo dispositivo:</string>
|
||||||
<string name="libraries">Librerie</string>
|
<string name="libraries">Librerie</string>
|
||||||
<string name="libraries_intro">Questa app usa diverse librerie open source, incluse:</string>
|
<string name="libraries_intro">Questa app usa diverse librerie open source, incluse:</string>
|
||||||
<string name="libraries_open_source">Librerie open source</string>
|
<string name="libraries_open_source">Librerie open source</string>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<string name="file_size">Tamanho do arquivo: %1$s</string>
|
<string name="file_size">Tamanho do arquivo: %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 em Downloads:</string>
|
<string name="last_saved_pdf_files">PDFs recentes salvos neste dispositivo:</string>
|
||||||
<string name="libraries">Bibliotecas</string>
|
<string name="libraries">Bibliotecas</string>
|
||||||
<string name="libraries_intro">Este aplicativo usa várias bibliotecas de código aberto, incluindo:</string>
|
<string name="libraries_intro">Este aplicativo usa várias bibliotecas de código aberto, incluindo:</string>
|
||||||
<string name="libraries_open_source">Bibliotecas de código aberto</string>
|
<string name="libraries_open_source">Bibliotecas de código aberto</string>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<string name="file_size">Размер файла: %1$s</string>
|
<string name="file_size">Размер файла: %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 сохранены в Download:</string>
|
<string name="last_saved_pdf_files">Последние PDF, сохранённые на этом устройстве:</string>
|
||||||
<string name="libraries">Библиотеки</string>
|
<string name="libraries">Библиотеки</string>
|
||||||
<string name="libraries_intro">Это приложение использует ряд библиотек с открытым исходным кодом, включая:</string>
|
<string name="libraries_intro">Это приложение использует ряд библиотек с открытым исходным кодом, включая:</string>
|
||||||
<string name="libraries_open_source">Библиотеки с открытым исходным кодом</string>
|
<string name="libraries_open_source">Библиотеки с открытым исходным кодом</string>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
<string name="file_size">文件大小: %1$s</string>
|
<string name="file_size">文件大小: %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>
|
||||||
<string name="libraries">库</string>
|
<string name="libraries">库</string>
|
||||||
<string name="libraries_intro">本应用使用的开源库:</string>
|
<string name="libraries_intro">本应用使用的开源库:</string>
|
||||||
<string name="libraries_open_source">开源库</string>
|
<string name="libraries_open_source">开源库</string>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
<string name="file_size">File size: %1$s</string>
|
<string name="file_size">File 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 in Downloads:</string>
|
<string name="last_saved_pdf_files">Recent PDFs saved on this device:</string>
|
||||||
<string name="libraries">Libraries</string>
|
<string name="libraries">Libraries</string>
|
||||||
<string name="libraries_intro">This application uses several open-source libraries, including:</string>
|
<string name="libraries_intro">This application uses several open-source libraries, including:</string>
|
||||||
<string name="libraries_open_source">Open-source libraries</string>
|
<string name="libraries_open_source">Open-source libraries</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user