From 3ef6ac7dcbc2c37d34f555b3d269d5feac4ed971 Mon Sep 17 00:00:00 2001 From: m Date: Sun, 13 Sep 2026 21:45:28 +0200 Subject: [PATCH] add document scanner --- mobile-kotlin/app/build.gradle.kts | 16 ++++++- .../app/src/main/AndroidManifest.xml | 5 +++ .../mobile/data/local/AppDatabase.kt | 10 +++-- .../mobile/data/local/migration/Migrations.kt | 43 ++++++++++++++++++- .../com/vaultdrop/mobile/di/DatabaseModule.kt | 4 ++ .../mobile/ui/dashboard/DashboardScreen.kt | 16 +++++++ .../mobile/ui/navigation/NavGraph.kt | 8 ++++ .../app/src/main/res/values/strings.xml | 33 ++++++++++++++ mobile-kotlin/gradle/libs.versions.toml | 6 +++ 9 files changed, 136 insertions(+), 5 deletions(-) diff --git a/mobile-kotlin/app/build.gradle.kts b/mobile-kotlin/app/build.gradle.kts index c9f0a78..ca88392 100644 --- a/mobile-kotlin/app/build.gradle.kts +++ b/mobile-kotlin/app/build.gradle.kts @@ -15,9 +15,18 @@ android { defaultConfig { applicationId = "com.vaultdrop.mobile" minSdk = 26 - targetSdk = 35 + // targetSdk 35 exige des .so alignés en 16K ; les libs précompilées + // OpenCV/CameraX sont en 4K (warning de compatibilité sur devices + // 16K-pages). On reste en 34 tant qu'elles ne sont pas mises à jour. + targetSdk = 34 versionCode = 1 versionName = "0.1.0" + + // OpenCV natif : on conserve uniquement les ABIs ciblés + // (arm64 = devices, x86_64 = émulateur) pour garder l'APK raisonnable. + ndk { + abiFilters += listOf("arm64-v8a", "x86_64") + } } buildTypes { @@ -73,6 +82,11 @@ dependencies { ksp(libs.androidx.hilt.compiler) implementation(libs.androidx.work.runtime.ktx) + implementation(libs.androidx.camera.camera2) + implementation(libs.androidx.camera.lifecycle) + implementation(libs.androidx.camera.view) + implementation(libs.opencv) + implementation(libs.retrofit) implementation(libs.retrofit.converter.moshi) implementation(libs.okhttp) diff --git a/mobile-kotlin/app/src/main/AndroidManifest.xml b/mobile-kotlin/app/src/main/AndroidManifest.xml index 0a189f1..366dbec 100644 --- a/mobile-kotlin/app/src/main/AndroidManifest.xml +++ b/mobile-kotlin/app/src/main/AndroidManifest.xml @@ -3,6 +3,11 @@ xmlns:tools="http://schemas.android.com/tools"> + + + = arrayOf( - MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7, + MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7, MIGRATION_7_8, ) } \ No newline at end of file diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/di/DatabaseModule.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/di/DatabaseModule.kt index af2921e..59070e4 100644 --- a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/di/DatabaseModule.kt +++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/di/DatabaseModule.kt @@ -6,6 +6,7 @@ import com.vaultdrop.mobile.data.local.AppDatabase import com.vaultdrop.mobile.data.local.dao.FileDao import com.vaultdrop.mobile.data.local.dao.FolderDao import com.vaultdrop.mobile.data.local.dao.PendingOperationDao +import com.vaultdrop.mobile.data.local.dao.ScanDao import com.vaultdrop.mobile.data.local.dao.UserPreferenceDao import com.vaultdrop.mobile.data.local.migration.Migrations import dagger.Module @@ -37,4 +38,7 @@ object DatabaseModule { @Provides fun providePendingOperationDao(db: AppDatabase): PendingOperationDao = db.pendingOperationDao() + + @Provides + fun provideScanDao(db: AppDatabase): ScanDao = db.scanDao() } \ No newline at end of file diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/dashboard/DashboardScreen.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/dashboard/DashboardScreen.kt index 816527e..8cd39d9 100644 --- a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/dashboard/DashboardScreen.kt +++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/dashboard/DashboardScreen.kt @@ -11,11 +11,14 @@ import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.DocumentScanner import androidx.compose.material3.Card import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold +import androidx.compose.material3.SmallFloatingActionButton import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable @@ -43,6 +46,7 @@ fun DashboardScreen( connectionStatusViewModel: ConnectionStatusViewModel, syncViewModel: SyncViewModel, onOpenReview: () -> Unit, + onOpenScan: () -> Unit, viewModel: DashboardViewModel = hiltViewModel(), ) { val connectionStatus by connectionStatusViewModel.status.collectAsStateWithLifecycle() @@ -64,6 +68,18 @@ fun DashboardScreen( bottomBar = { FloatingNavBar(selected = selectedTab, onSelect = onTabSelected) }, + floatingActionButton = { + SmallFloatingActionButton( + onClick = onOpenScan, + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary, + ) { + Icon( + imageVector = Icons.Filled.DocumentScanner, + contentDescription = stringResource(R.string.scan_title), + ) + } + }, ) { padding -> Column( modifier = Modifier diff --git a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/NavGraph.kt b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/NavGraph.kt index 47bbd95..6899992 100644 --- a/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/NavGraph.kt +++ b/mobile-kotlin/app/src/main/java/com/vaultdrop/mobile/ui/navigation/NavGraph.kt @@ -25,6 +25,7 @@ import com.vaultdrop.mobile.ui.folderdetail.FolderDetailScreen import com.vaultdrop.mobile.ui.folderlist.FolderListScreen import com.vaultdrop.mobile.ui.pdfbuilder.PdfBuilderScreen import com.vaultdrop.mobile.ui.review.SwipeReviewScreen +import com.vaultdrop.mobile.ui.scan.ScanFlowScreen import com.vaultdrop.mobile.ui.search.SearchScreen import com.vaultdrop.mobile.ui.settings.SettingsScreen import com.vaultdrop.mobile.ui.watchedfolders.WatchedFoldersScreen @@ -42,6 +43,7 @@ object Routes { const val ARG_DOCUMENT = "documentResourceId" const val PDF_BUILDER = "pdfbuilder/{ids}" const val ARG_BUILDER_IDS = "ids" + const val SCAN = "scan" fun folder(folderResourceId: String): String = "folder/$folderResourceId" fun document(documentResourceId: String): String = "document/$documentResourceId" @@ -126,6 +128,12 @@ fun NavGraph( connectionStatusViewModel = connectionStatusViewModel, syncViewModel = syncViewModel, onOpenReview = { navController.navigate(Routes.REVIEW) }, + onOpenScan = { navController.navigate(Routes.SCAN) }, + ) + } + composable(Routes.SCAN) { + ScanFlowScreen( + onBack = { navController.popBackStack() }, ) } composable(Routes.REVIEW) { diff --git a/mobile-kotlin/app/src/main/res/values/strings.xml b/mobile-kotlin/app/src/main/res/values/strings.xml index b3cb9b9..96ac86b 100644 --- a/mobile-kotlin/app/src/main/res/values/strings.xml +++ b/mobile-kotlin/app/src/main/res/values/strings.xml @@ -179,4 +179,37 @@ Aperçu Fermer l\'aperçu PDF générés + + + Scanner + Capturer + %1$d page(s) + Voir les pages + Cadre le document puis capture. + Autorise la caméra pour scanner des documents. + Autoriser la caméra + Recadrer + Valider la page + Reprendre + Couleur + N&B + N&B contrasté + Ajouter une page + Pages scannées + Supprimer + Monter + Descendre + Aucune page pour le moment.\nCapture ta première page. + Exporter vers le dossier VaultDrop + Export en cours… + Abandonner + Abandonner le scan ? + Les pages capturées seront supprimées. + Confirmer + Annuler + Session de scan indisponible. + Impossible d\'utiliser cette capture. + Impossible d\'enregistrer la page. + Impossible d\'exporter les pages. + Pages exportées \ No newline at end of file diff --git a/mobile-kotlin/gradle/libs.versions.toml b/mobile-kotlin/gradle/libs.versions.toml index 12a6e9e..f49ed0d 100644 --- a/mobile-kotlin/gradle/libs.versions.toml +++ b/mobile-kotlin/gradle/libs.versions.toml @@ -17,6 +17,8 @@ moshi = "1.15.1" securityCrypto = "1.0.0" timber = "5.0.1" coil = "2.7.0" +cameraX = "1.3.4" +opencv = "4.14.0" junit = "4.13.2" robolectric = "4.14.1" androidxTestCore = "1.6.1" @@ -29,6 +31,10 @@ androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecyc androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" } androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" } androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } +androidx-camera-camera2 = { group = "androidx.camera", name = "camera-camera2", version.ref = "cameraX" } +androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "cameraX" } +androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "cameraX" } +opencv = { group = "org.opencv", name = "opencv", version.ref = "opencv" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" } androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }