New setting for default color mode ("default filter")

This commit is contained in:
Pierre-Yves Nicolas
2026-04-02 11:20:05 +02:00
parent 139922c19e
commit 4aae5accae
24 changed files with 153 additions and 25 deletions

View File

@@ -25,6 +25,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.runBlocking
import org.fairscan.app.ui.screens.camera.extractDocumentFromBitmap
import org.fairscan.app.ui.screens.settings.DefaultColorMode
import org.fairscan.imageprocessing.ImageSize
import org.fairscan.imageprocessing.detectDocumentQuad
import org.fairscan.imageprocessing.scaledTo
@@ -62,7 +63,9 @@ class DocumentDetectionTest {
if (quad != null) {
val resizedQuad =
quad.scaledTo(mask.width, mask.height, bitmap.width, bitmap.height)
outputJpeg = extractDocumentFromBitmap(bitmap, resizedQuad, 0, mask, scope).pageJpeg
val auto = DefaultColorMode.AUTO
val page = extractDocumentFromBitmap(bitmap, resizedQuad,0, mask, scope, auto)
outputJpeg = page.pageJpeg
val file = File(context.getExternalFilesDir(null), imageFileName)
file.writeBytes(outputJpeg.bytes)
Log.i("DocumentDetectionTest", "Image saved to ${file.absolutePath}")

View File

@@ -269,6 +269,7 @@ class MainActivity : ComponentActivity() {
}
SettingsScreen(
settingsUiState,
onDefaultColorModeChanged = { mode -> settingsViewModel.setDefaultColorMode(mode) },
onChooseDirectoryClick = {
try {
launcher.launch(null)

View File

@@ -195,6 +195,7 @@ class MainViewModel(val imageRepository: ImageRepository, launchMode: LaunchMode
capturedPage.pageJpeg,
sourceJpeg,
capturedPage.metadata,
capturedPage.colorMode,
)
imageRepository.pages()
}

View File

@@ -141,10 +141,10 @@ class ImageRepository(
}
}
suspend fun add(processed: Jpeg, source: Jpeg, metadata: PageMetadata) =
suspend fun add(processed: Jpeg, source: Jpeg, metadata: PageMetadata, colorMode: ColorMode) =
mutex.withLock {
val id = "${System.currentTimeMillis()}"
val key = PageViewKey(id, Rotation.R0, metadata.autoColorMode)
val key = PageViewKey(id, Rotation.R0, colorMode)
processedImageFile(key).writeBytes(processed.bytes)
sourceFile(id).writeBytes(source.bytes)
pages.addOrReplace(
@@ -154,7 +154,7 @@ class ImageRepository(
baseRotationDegrees = metadata.baseRotation.degrees,
manualRotationDegrees = Rotation.R0.degrees,
isColored = metadata.autoColorMode == ColorMode.COLOR,
colorMode = metadata.autoColorMode,
colorMode = colorMode,
)
)
saveMetadata()

View File

@@ -16,9 +16,11 @@
package org.fairscan.app.domain
import kotlinx.coroutines.Deferred
import org.fairscan.imageprocessing.ColorMode
data class CapturedPage(
val pageJpeg: Jpeg,
val sourceJpeg: Deferred<Jpeg>,
val metadata: PageMetadata,
val colorMode: ColorMode,
)

View File

@@ -540,7 +540,8 @@ fun CameraScreenPreviewWithProcessedImage() {
CapturedPage(
debugImage("gallica.bnf.fr-bpt6k5530456s-1.jpg"),
CompletableDeferred(Jpeg(ByteArray(0))),
PageMetadata(quad, R0, ColorMode.COLOR))))
PageMetadata(quad, R0, ColorMode.COLOR),
ColorMode.COLOR)))
}
@Preview(showBackground = true, widthDp = 640, heightDp = 320)

View File

@@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.fairscan.app.AppContainer
@@ -35,6 +36,7 @@ import org.fairscan.app.domain.ExportQuality
import org.fairscan.app.domain.Jpeg
import org.fairscan.app.domain.PageMetadata
import org.fairscan.app.domain.Rotation
import org.fairscan.app.ui.screens.settings.DefaultColorMode
import org.fairscan.imageprocessing.ImageSize
import org.fairscan.imageprocessing.Mask
import org.fairscan.imageprocessing.Quad
@@ -53,6 +55,7 @@ sealed interface CameraEvent {
class CameraViewModel(appContainer: AppContainer): ViewModel() {
private val imageSegmentationService = appContainer.imageSegmentationService
private val settingsRepository = appContainer.settingsRepository
private val logger = appContainer.logger
private val _events = MutableSharedFlow<CameraEvent>()
@@ -165,8 +168,9 @@ class CameraViewModel(appContainer: AppContainer): ViewModel() {
val quad = detectDocumentQuad(mask, originalSize, isLiveAnalysis = false)
if (quad != null) {
val resizedQuad = quad.scaledTo(mask.width, mask.height, source.width, source.height)
val defaultColorMode = settingsRepository.defaultColorMode.first()
result = extractDocumentFromBitmap(
source, resizedQuad, rotationDegrees, mask, viewModelScope)
source, resizedQuad, rotationDegrees, mask, viewModelScope, defaultColorMode)
}
}
return@withContext result
@@ -223,14 +227,20 @@ sealed class CaptureState {
}
fun extractDocumentFromBitmap(
source: Bitmap, quad: Quad, rotationDegrees: Int, mask: Mask, viewModelScope: CoroutineScope
source: Bitmap,
quad: Quad,
rotationDegrees: Int,
mask: Mask,
viewModelScope: CoroutineScope,
defaultColorMode: DefaultColorMode = DefaultColorMode.AUTO
): CapturedPage {
val rgba = Mat()
Utils.bitmapToMat(source, rgba)
val bgr = Mat()
Imgproc.cvtColor(rgba, bgr, Imgproc.COLOR_RGBA2BGR) // CV_8UC4 → CV_8UC3
rgba.release()
val colorMode = autoColorMode(bgr, mask, quad)
val autoColorMode = autoColorMode(bgr, mask, quad)
val colorMode = defaultColorMode.colorMode ?: autoColorMode
val maxPixels = ExportQuality.BALANCED.maxPixels
val page = extractDocument(bgr, quad, rotationDegrees, colorMode, maxPixels)
val pageJpeg = Jpeg.fromMat(page, ExportQuality.BALANCED.jpegQuality)
@@ -239,11 +249,11 @@ fun extractDocumentFromBitmap(
val normalizedQuad = quad.scaledTo(source.width, source.height, 1, 1)
val baseRotation = Rotation.fromDegrees(rotationDegrees)
val metadata = PageMetadata(normalizedQuad, baseRotation, colorMode)
val metadata = PageMetadata(normalizedQuad, baseRotation, autoColorMode)
val sourceJpegDeferred = viewModelScope.async(Dispatchers.IO) {
compressJpeg(source, 90)
}
return CapturedPage(pageJpeg, sourceJpegDeferred, metadata)
return CapturedPage(pageJpeg, sourceJpegDeferred, metadata, colorMode)
}
fun rotateBitmap(source: Bitmap, angle: Float): Bitmap {

View File

@@ -22,16 +22,29 @@ import androidx.datastore.preferences.preferencesDataStore
import androidx.documentfile.provider.DocumentFile
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import org.fairscan.app.R
import org.fairscan.app.domain.ExportQuality
import org.fairscan.imageprocessing.ColorMode
private val Context.dataStore by preferencesDataStore(name = "fairscan_settings")
class SettingsRepository(private val context: Context) {
private val DEFAULT_COLOR_MODE = stringPreferencesKey("default_color_mode")
private val EXPORT_DIR_URI = stringPreferencesKey("export_dir_uri")
private val EXPORT_FORMAT = stringPreferencesKey("export_format")
private val EXPORT_QUALITY = stringPreferencesKey("export_quality")
val defaultColorMode: Flow<DefaultColorMode> =
context.dataStore.data.map { prefs ->
when (prefs[DEFAULT_COLOR_MODE]) {
"AUTO" -> DefaultColorMode.AUTO
"COLOR" -> DefaultColorMode.COLOR
"GRAYSCALE" -> DefaultColorMode.GRAYSCALE
else -> DefaultColorMode.AUTO
}
}
val exportDirUri: Flow<String?> =
context.dataStore.data.map { prefs ->
prefs[EXPORT_DIR_URI]
@@ -60,6 +73,12 @@ class SettingsRepository(private val context: Context) {
}
}
suspend fun setDefaultColorMode(mode: DefaultColorMode) {
context.dataStore.edit { prefs ->
prefs[DEFAULT_COLOR_MODE] = mode.name
}
}
suspend fun setExportDirUri(uri: String?) {
context.dataStore.edit { prefs ->
if (uri == null) {
@@ -83,6 +102,12 @@ class SettingsRepository(private val context: Context) {
}
}
enum class DefaultColorMode(val colorMode: ColorMode?, val labelResource: Int) {
AUTO(null, R.string.color_mode_auto),
COLOR(ColorMode.COLOR, R.string.color_mode_color),
GRAYSCALE(ColorMode.GRAYSCALE, R.string.color_mode_grayscale),
}
enum class ExportFormat(val mimeType: String) {
PDF("application/pdf"),
JPEG("image/jpeg"),

View File

@@ -54,6 +54,7 @@ import org.fairscan.app.ui.theme.FairScanTheme
@Composable
fun SettingsScreen(
uiState: SettingsUiState,
onDefaultColorModeChanged: (DefaultColorMode) -> Unit,
onChooseDirectoryClick: () -> Unit,
onResetExportDirClick: () -> Unit,
onExportFormatChanged: (ExportFormat) -> Unit,
@@ -71,6 +72,7 @@ fun SettingsScreen(
) { paddingValues ->
SettingsContent(
uiState,
onDefaultColorModeChanged,
onChooseDirectoryClick,
onResetExportDirClick,
onExportFormatChanged,
@@ -82,6 +84,7 @@ fun SettingsScreen(
@Composable
private fun SettingsContent(
uiState: SettingsUiState,
onDefaultColorModeChanged: (DefaultColorMode) -> Unit,
onChooseDirectoryClick: () -> Unit,
onResetExportDirClick: () -> Unit,
onExportFormatChanged: (ExportFormat) -> Unit,
@@ -108,6 +111,26 @@ private fun SettingsContent(
.padding(20.dp)
.verticalScroll(rememberScrollState())
) {
Text(stringResource(R.string.settings_section_scan), style = MaterialTheme.typography.titleLarge)
Spacer(Modifier.height(32.dp))
Text(stringResource(R.string.color_mode_default), style = MaterialTheme.typography.titleMedium)
DefaultColorMode.entries.forEach { mode ->
Row(verticalAlignment = Alignment.CenterVertically) {
RadioButton(
selected = uiState.defaultColorMode == mode,
onClick = { onDefaultColorModeChanged(mode) },
)
Text(stringResource(mode.labelResource))
}
}
Spacer(Modifier.height(32.dp))
Text(stringResource(R.string.settings_section_export), style = MaterialTheme.typography.titleLarge)
Spacer(Modifier.height(32.dp))
DirectorySettingItem(
label = stringResource(R.string.export_directory),
folderLabel,
@@ -128,7 +151,7 @@ private fun SettingsContent(
Spacer(Modifier.height(32.dp))
Text(stringResource(R.string.export_quality), style = MaterialTheme.typography.titleLarge)
Text(stringResource(R.string.export_quality), style = MaterialTheme.typography.titleMedium)
ExportQuality.entries.reversed().forEach { quality ->
Row(verticalAlignment = Alignment.CenterVertically) {
@@ -142,7 +165,7 @@ private fun SettingsContent(
Spacer(Modifier.height(32.dp))
Text(stringResource(R.string.export_format), style = MaterialTheme.typography.titleLarge)
Text(stringResource(R.string.export_format), style = MaterialTheme.typography.titleMedium)
Row(verticalAlignment = Alignment.CenterVertically) {
RadioButton(
@@ -173,7 +196,7 @@ fun DirectorySettingItem(
Column {
Text(
text = label,
style = MaterialTheme.typography.titleLarge
style = MaterialTheme.typography.titleMedium
)
Spacer(Modifier.height(8.dp))
@@ -209,13 +232,13 @@ fun DirectorySettingItem(
@Preview
@Composable
fun SettingsScreenPreviewWithoutDir() {
SettingsScreenPreview(SettingsUiState(null))
SettingsScreenPreview(SettingsUiState())
}
@Preview
@Composable
fun SettingsScreenPreviewWithDir() {
SettingsScreenPreview(SettingsUiState("content://root/dir"))
SettingsScreenPreview(SettingsUiState(exportDirUri = "content://root/dir"))
}
@Composable
@@ -223,6 +246,7 @@ fun SettingsScreenPreview(uiState: SettingsUiState) {
FairScanTheme {
SettingsScreen(
uiState,
onDefaultColorModeChanged = {},
onChooseDirectoryClick = {},
onResetExportDirClick = {},
onExportFormatChanged = {},

View File

@@ -27,6 +27,7 @@ import org.fairscan.app.AppContainer
import org.fairscan.app.domain.ExportQuality
data class SettingsUiState(
val defaultColorMode: DefaultColorMode = DefaultColorMode.AUTO,
val exportDirUri: String? = null,
val exportDirName: String? = null,
val exportFormat: ExportFormat = ExportFormat.PDF,
@@ -41,12 +42,14 @@ class SettingsViewModel(container: AppContainer) : ViewModel() {
val dirName: StateFlow<String?> = _dirName
val uiState = combine(
repo.defaultColorMode,
repo.exportDirUri,
dirName,
repo.exportFormat,
repo.exportQuality,
) { uri, name, format, quality ->
) { colorMode, uri, name, format, quality ->
SettingsUiState(
defaultColorMode = colorMode,
exportDirUri = uri,
exportDirName = name,
exportFormat = format,
@@ -58,6 +61,12 @@ class SettingsViewModel(container: AppContainer) : ViewModel() {
SettingsUiState()
)
fun setDefaultColorMode(pref: DefaultColorMode) {
viewModelScope.launch {
repo.setDefaultColorMode(pref)
}
}
fun setExportDirUri(uri: String?) {
viewModelScope.launch {
repo.setExportDirUri(uri)

View File

@@ -9,7 +9,9 @@
<string name="change_directory">غيّر المجلد</string>
<string name="clear_text">امحُ النص</string>
<string name="color_mode">فلتر</string>
<string name="color_mode_auto">تلقائي</string>
<string name="color_mode_color">ألوان</string>
<string name="color_mode_default">الفلتر الافتراضي</string>
<string name="color_mode_grayscale">تدرج الرمادي</string>
<string name="contact">تواصل</string>
<string name="copied_logs">نُسخت السجلات إلى الحافظة</string>
@@ -58,6 +60,8 @@
<string name="scan_button">مسح جديد</string>
<string name="scan_in_progress">المسح قيد التقدم</string>
<string name="settings">الإعدادات</string>
<string name="settings_section_scan">المسح</string>
<string name="settings_section_export">التصدير</string>
<string name="share">شارك</string>
<string name="share_document">شارك المستند</string>
<string name="storage_permission_denied">تعذر حفظ الملف: رُفض الإذن</string>

View File

@@ -9,7 +9,9 @@
<string name="change_directory">Změnit složku</string>
<string name="clear_text">Smazat text</string>
<string name="color_mode">Filtr</string>
<string name="color_mode_auto">Automaticky</string>
<string name="color_mode_color">Barva</string>
<string name="color_mode_default">Výchozí filtr</string>
<string name="color_mode_grayscale">Odstíny šedi</string>
<string name="contact">Kontakt</string>
<string name="copied_logs">Protokoly zkopírovány do schránky</string>
@@ -58,6 +60,8 @@
<string name="scan_button">Nové skenování</string>
<string name="scan_in_progress">Probíhá skenování</string>
<string name="settings">Nastavení</string>
<string name="settings_section_scan">Skenování</string>
<string name="settings_section_export">Export</string>
<string name="share">Sdílet</string>
<string name="share_document">Sdílet dokument</string>
<string name="storage_permission_denied">Nelze uložit soubor: oprávnění bylo odmítnuto</string>

View File

@@ -9,7 +9,9 @@
<string name="change_directory">Ordner ändern</string>
<string name="clear_text">Text löschen</string>
<string name="color_mode">Filter</string>
<string name="color_mode_auto">Automatisch</string>
<string name="color_mode_color">Farbe</string>
<string name="color_mode_default">Standardfilter</string>
<string name="color_mode_grayscale">Graustufen</string>
<string name="contact">Kontakt</string>
<string name="copied_logs">Logs in die Zwischenablage kopiert</string>
@@ -58,6 +60,8 @@
<string name="scan_button">Neuer Scan</string>
<string name="scan_in_progress">Scan läuft</string>
<string name="settings">Einstellungen</string>
<string name="settings_section_scan">Scan</string>
<string name="settings_section_export">Export</string>
<string name="share">Teilen</string>
<string name="share_document">Dokument teilen</string>
<string name="storage_permission_denied">Datei kann nicht gespeichert werden: Berechtigung verweigert</string>

View File

@@ -9,7 +9,9 @@
<string name="change_directory">Cambiar carpeta</string>
<string name="clear_text">Borrar texto</string>
<string name="color_mode">Filtro</string>
<string name="color_mode_auto">Automático</string>
<string name="color_mode_color">Color</string>
<string name="color_mode_default">Filtro predeterminado</string>
<string name="color_mode_grayscale">Escala de grises</string>
<string name="contact">Contacto</string>
<string name="copied_logs">Registros copiados al portapapeles</string>
@@ -58,6 +60,8 @@
<string name="scan_button">Nuevo escaneo</string>
<string name="scan_in_progress">Escaneo en curso</string>
<string name="settings">Ajustes</string>
<string name="settings_section_scan">Escaneo</string>
<string name="settings_section_export">Exportación</string>
<string name="share">Compartir</string>
<string name="share_document">Compartir documento</string>
<string name="storage_permission_denied">No se puede guardar el archivo: permiso denegado</string>

View File

@@ -9,7 +9,9 @@
<string name="change_directory">Changer de dossier</string>
<string name="clear_text">Effacer le text</string>
<string name="color_mode">Filtre</string>
<string name="color_mode_auto">Automatique</string>
<string name="color_mode_color">Couleur</string>
<string name="color_mode_default">Filtre par défaut</string>
<string name="color_mode_grayscale">Niveaux de gris</string>
<string name="contact">Contact</string>
<string name="copied_logs">Logs copiés dans le presse-papiers</string>
@@ -58,6 +60,8 @@
<string name="scan_button">Nouveau scan</string>
<string name="scan_in_progress">Scan en cours</string>
<string name="settings">Paramètres</string>
<string name="settings_section_scan">Scan</string>
<string name="settings_section_export">Export</string>
<string name="share">Partager</string>
<string name="share_document">Partager le document</string>
<string name="storage_permission_denied">Impossible denregistrer le fichier : permission refusée</string>

View File

@@ -9,7 +9,9 @@
<string name="change_directory">Trocar cartafol</string>
<string name="clear_text">Borrar texto</string>
<string name="color_mode">Filtro</string>
<string name="color_mode_auto">Automático</string>
<string name="color_mode_color">Cor</string>
<string name="color_mode_default">Filtro predeterminado</string>
<string name="color_mode_grayscale">Escala de grises</string>
<string name="contact">Contacto</string>
<string name="copied_logs">Rexistros copiados ao portapapeis</string>
@@ -58,6 +60,8 @@
<string name="scan_button">Novo escaneo</string>
<string name="scan_in_progress">Escaneo en curso</string>
<string name="settings">Configuración</string>
<string name="settings_section_scan">Escaneo</string>
<string name="settings_section_export">Exportación</string>
<string name="share">Compartir</string>
<string name="share_document">Compartir documento</string>
<string name="storage_permission_denied">Non se pode gardar o ficheiro: permiso denegado</string>

View File

@@ -9,7 +9,9 @@
<string name="change_directory">Cambia cartella</string>
<string name="clear_text">Svuota testo</string>
<string name="color_mode">Filtro</string>
<string name="color_mode_auto">Automatico</string>
<string name="color_mode_color">Colore</string>
<string name="color_mode_default">Filtro predefinito</string>
<string name="color_mode_grayscale">Scala di grigi</string>
<string name="contact">Contatti</string>
<string name="copied_logs">Log copiati negli appunti</string>
@@ -58,6 +60,8 @@
<string name="scan_button">Nuova scansione</string>
<string name="scan_in_progress">Scansione in corso</string>
<string name="settings">Impostazioni</string>
<string name="settings_section_scan">Scansione</string>
<string name="settings_section_export">Esportazione</string>
<string name="share">Condividi</string>
<string name="share_document">Condividi documento</string>
<string name="storage_permission_denied">Impossibile salvare il file: permesso negato</string>

View File

@@ -9,7 +9,9 @@
<string name="change_directory">Alterar diretório</string>
<string name="clear_text">Limpar texto</string>
<string name="color_mode">Filtro</string>
<string name="color_mode_auto">Automático</string>
<string name="color_mode_color">Cor</string>
<string name="color_mode_default">Filtro padrão</string>
<string name="color_mode_grayscale">Escala de cinza</string>
<string name="contact">Contato</string>
<string name="copied_logs">Registros copiados para a área de transferência</string>
@@ -58,6 +60,8 @@
<string name="scan_button">Nova digitalização</string>
<string name="scan_in_progress">Digitalização em andamento</string>
<string name="settings">Configurações</string>
<string name="settings_section_scan">Digitalização</string>
<string name="settings_section_export">Exportação</string>
<string name="share">Compartilhar</string>
<string name="share_document">Compartilhar documento</string>
<string name="storage_permission_denied">Não foi possível salvar o arquivo: permissão negada</string>

View File

@@ -8,10 +8,12 @@
<string name="cancel">Отмена</string>
<string name="change_directory">Изменить папку</string>
<string name="clear_text">Стереть текст</string>
<string name="color_mode">Фильтр</string>
<string name="color_mode_auto">Автоматически</string>
<string name="color_mode_color">Цвет</string>
<string name="color_mode_default">Фильтр по умолчанию</string>
<string name="color_mode_grayscale">Оттенки серого</string>
<string name="contact">Контакты</string>
<string name="color_mode">Фильтр</string>
<string name="copied_logs">Журналы скопированы в буфер обмена</string>
<string name="copy_logs">Копировать журналы</string>
<string name="creating_export">Подготовка экспорта…</string>
@@ -58,6 +60,8 @@
<string name="scan_button">Начать</string>
<string name="scan_in_progress">Сканирование выполняется</string>
<string name="settings">Настройки</string>
<string name="settings_section_scan">Сканирование</string>
<string name="settings_section_export">Экспорт</string>
<string name="share">Поделиться</string>
<string name="share_document">Поделиться документом</string>
<string name="storage_permission_denied">Невозможно сохранить файл: доступ запрещён</string>

View File

@@ -9,7 +9,9 @@
<string name="change_directory">Dizini değiştir</string>
<string name="clear_text">Metni temizle</string>
<string name="color_mode">Filtre</string>
<string name="color_mode_auto">Otomatik</string>
<string name="color_mode_color">Renkli</string>
<string name="color_mode_default">Varsayılan filtre</string>
<string name="color_mode_grayscale">Gri tonlama</string>
<string name="contact">İletişim</string>
<string name="copied_logs">Günlükler panoya kopyalandı</string>
@@ -58,6 +60,8 @@
<string name="scan_button">Yeni Tarama</string>
<string name="scan_in_progress">Tarama devam ediyor</string>
<string name="settings">Ayarlar</string>
<string name="settings_section_scan">Tarama</string>
<string name="settings_section_export">Dışa aktarma</string>
<string name="share">Paylaş</string>
<string name="share_document">Belgeyi paylaş</string>
<string name="storage_permission_denied">Dosya kaydedilemiyor: izin reddedildi</string>

View File

@@ -9,7 +9,9 @@
<string name="change_directory">變更目錄</string>
<string name="clear_text">清除文字</string>
<string name="color_mode">濾鏡</string>
<string name="color_mode_auto">自動</string>
<string name="color_mode_color">彩色</string>
<string name="color_mode_default">預設濾鏡</string>
<string name="color_mode_grayscale">灰階</string>
<string name="contact">聯絡我們</string>
<string name="copied_logs">日誌已複製到剪貼簿</string>
@@ -58,6 +60,8 @@
<string name="scan_button">開始掃描</string>
<string name="scan_in_progress">掃描進行中</string>
<string name="settings">設定</string>
<string name="settings_section_scan">掃描</string>
<string name="settings_section_export">匯出</string>
<string name="share">分享</string>
<string name="share_document">分享文件</string>
<string name="storage_permission_denied">無法儲存檔案:權限遭拒</string>

View File

@@ -9,7 +9,9 @@
<string name="change_directory">更改目录</string>
<string name="clear_text">清除文字</string>
<string name="color_mode">滤镜</string>
<string name="color_mode_auto">自动</string>
<string name="color_mode_color">彩色</string>
<string name="color_mode_default">默认滤镜</string>
<string name="color_mode_grayscale">灰度</string>
<string name="contact">联系人</string>
<string name="copied_logs">日志已复制到剪贴板</string>
@@ -58,6 +60,8 @@
<string name="scan_button">新建扫描</string>
<string name="scan_in_progress">正在进行扫描</string>
<string name="settings">设置</string>
<string name="settings_section_scan">扫描</string>
<string name="settings_section_export">导出</string>
<string name="share">共享</string>
<string name="share_document">分享文档</string>
<string name="storage_permission_denied">无法保存文件:权限被拒绝</string>

View File

@@ -10,7 +10,9 @@
<string name="change_directory">Change folder</string>
<string name="clear_text">Clear text</string>
<string name="color_mode">Filter</string>
<string name="color_mode_auto">Automatic</string>
<string name="color_mode_color">Color</string>
<string name="color_mode_default">Default filter</string>
<string name="color_mode_grayscale">Grayscale</string>
<string name="contact">Contact</string>
<string name="copied_logs">Logs copied to clipboard</string>
@@ -62,6 +64,8 @@
<string name="scan_button">New Scan</string>
<string name="scan_in_progress">Scan in progress</string>
<string name="settings">Settings</string>
<string name="settings_section_scan">Scan</string>
<string name="settings_section_export">Export</string>
<string name="share">Share</string>
<string name="share_document">Share document</string>
<string name="storage_permission_denied">Cannot save file: permission was denied</string>

View File

@@ -75,7 +75,7 @@ class ImageRepositoryTest {
val repo = repo()
assertThat(repo.imageIds()).isEmpty()
val jpeg = jpeg(101, 102, 103)
repo.add(jpeg, jpeg(51), metadata1)
repo.add(jpeg, jpeg(51), metadata1, COLOR)
assertThat(repo.imageIds()).hasSize(1)
val id = repo.imageIds()[0]
val key = PageViewKey(id, R0, COLOR)
@@ -96,7 +96,7 @@ class ImageRepositoryTest {
fun delete_image() = runTest {
val repo = repo()
val jpeg = jpeg(101, 102, 103)
repo.add(jpeg, jpeg(51), metadata1)
repo.add(jpeg, jpeg(51), metadata1, COLOR)
assertThat(jpegFiles(processedDir())).hasSize(1)
assertThat(jpegFiles(sourceDir())).hasSize(1)
assertThat(repo.imageIds()).hasSize(1)
@@ -188,7 +188,7 @@ class ImageRepositoryTest {
fun `clear should delete pages`() = runTest {
val jpeg = jpeg(101, 102, 103)
val repo1 = repo()
repo1.add(jpeg, jpeg(51), metadata1)
repo1.add(jpeg, jpeg(51), metadata1, COLOR)
assertThat(repo1.imageIds()).isNotEmpty()
repo1.clear()
assertThat(repo1.imageIds()).isEmpty()
@@ -201,7 +201,7 @@ class ImageRepositoryTest {
@Test
fun rotate() = runTest {
val repo = repo()
repo.add(jpeg(101, 102, 103), jpeg(51), metadata1)
repo.add(jpeg(101, 102, 103), jpeg(51), metadata1, COLOR)
assertThat(repo.pages().last().metadata).isEqualTo(metadata1)
val id = repo.pages().last().id
repo.rotate(id, true)
@@ -230,9 +230,9 @@ class ImageRepositoryTest {
@Test
fun movePage() = runTest {
val repo = repo()
repo.add(jpeg(101), jpeg(51), metadata1)
repo.add(jpeg(101), jpeg(51), metadata1, COLOR)
Thread.sleep(1L) // to avoid file name clashes
repo.add(jpeg(110), jpeg(51), metadata1)
repo.add(jpeg(110), jpeg(51), metadata1, COLOR)
val id0 = repo.imageIds().first()
val id1 = repo.imageIds().last()
repo.movePage(id1, 0)
@@ -284,10 +284,10 @@ class ImageRepositoryTest {
fun last_added_source_file() = runTest {
val repo = repo()
assertThat(repo.lastAddedSourceFile()).isNull()
repo.add(jpeg(101), jpeg(51), metadata1)
repo.add(jpeg(101), jpeg(51), metadata1, COLOR)
assertThat(repo.lastAddedSourceFile()).hasBinaryContent(byteArrayOf(51))
Thread.sleep(1)
repo.add(jpeg(102), jpeg(52), metadata1)
repo.add(jpeg(102), jpeg(52), metadata1, COLOR)
assertThat(repo.lastAddedSourceFile()).hasBinaryContent(byteArrayOf(52))
val id = repo.imageIds().last()