Privacy-focused Android app for scanning, viewing, creating, editing, and protecting PDFs locally on the device.
- Scan documents to PDF with Google's ML Kit Document Scanner
- Import existing PDFs into the app archive
- Start scans from app shortcuts, a Quick Settings tile, or a home-screen widget
- Accept PDFs and images from other Android apps via Share or Open with
- View PDFs directly in the app with PdfRenderer, page scrolling, inline pinch-to-zoom, double-tap zoom toggle, cross-page scroll while zoomed, print, share, export, and external-open fallback
- Search stored OCR text inside the PDF viewer, jump between matching pages, and use locally detected IBAN, amount, and date actions
- Detect tables on a scanned page with on-device OCR, review and correct cells in an editable grid, and export them as CSV or TSV files for spreadsheet apps
- Fill out PDF form fields (text, checkboxes, radio buttons, dropdowns, list boxes) directly on the device, with script-specific fallback fonts (Latin/Cyrillic, Devanagari, Arabic, CJK) for non-Latin form text
- Calculate a document's SHA-256 file hash locally for integrity checks, without opening it in the viewer
- Transfer files to and from a PC over the local Wi-Fi network through a PIN-protected local web server; no cloud or internet connection involved
- Extract OCR text and create searchable PDFs with automatic/manual language selection; all OCR recognizers are delivered as Google Play Services modules, with Latin requested for background preload during app installation and an on-demand fallback when a model is missing
- Review OCR text per page with recognized language, quality badges, copy/share actions, and TXT export to Downloads
- Assign optional on-device automatic document tags from OCR text and filter the archive by invoice, contract, insurance, certificate, bank, or delivery tags
- Create PDFs from gallery images with A4 layouts:
- 1 image per page
- 2 images per page
- 4 images per page
- Append scanned pages, gallery images, or another PDF to an existing document
- Search by filename and stored OCR text
- Organize documents with folders and favorites
- Create and restore encrypted
.m24backuparchives through Android's system file picker - Scan PDFs for QR codes and inspect URLs, Wi-Fi credentials, and raw payloads locally
- Extract business-card contact data with OCR and export vCard 3.0 files
- Merge, split, reorder, rotate, extract, duplicate, and delete pages
- Recover deleted documents from an in-app trash for 30 days before permanent purge
- Add annotations with marks, rectangles, ovals, text notes, and zoom-aware editing
- Add page numbers, text watermarks, and signatures
- Compress PDFs, protect them with passwords, unlock them, remove text layers, convert to grayscale, and restrict usage
- Export OCR text as an editable Word document (.docx) to Downloads
- Translate PDF text on-device into 10 languages (EN, DE, ES, FR, PT, RU, AR, HI, ZH, JA) using ML Kit Translate with per-page results and copy/share actions; language models (~15–30 MB each) are downloaded on first use and stay on-device
- Securely redact content and optionally rebuild searchability with OCR
- Optionally protect the app UI with Android biometrics or device credentials
- No cloud upload
- No account required
- Files stay in app-internal storage unless you explicitly export them
- Encrypted backups are created only by explicit user action and are protected with the password chosen for that backup
- Backup passwords cannot be recovered; restored documents are stored again in the normal local app archive
- OCR text, OCR quality metadata, and optional automatic document tags are stored locally and can be exported only by explicit user action
- Incoming shared or opened files are copied into the archive only after user confirmation
- App Lock is a local UI gate; it does not encrypt PDFs or the database
- No own backend or document upload; Google Play Services / ML Kit SDKs may declare network permissions for model, compatibility, and diagnostics traffic
- The local Wi-Fi PC-Sync server only listens on the local network, requires a random 4-digit PIN (rate-limited against guessing), and runs only while explicitly started by the user; it never contacts the internet
- Backup/export of internal app data is disabled
- Backup files use the
.m24backupextension and the MIME typeapplication/vnd.info.meuse24.pdf-scanner.backup. - The backup payload is encrypted with Tink StreamingAead. The streaming keyset is wrapped with a key derived from the user password via Argon2id.
- The cleartext header contains only format and KDF metadata plus the wrapped keyset. Document names, OCR text, tags, folder names, thumbnails, and PDFs are inside the encrypted payload.
- Per-file checksums detect copy, ZIP, and implementation errors. Cryptographic integrity comes from authenticated encryption, not from the checksums alone.
- Restore is merge-only: documents are added with unique file names; existing documents are not deleted. Re-importing the same backup may create duplicate documents.
- If OCR text was excluded during export, restored documents cannot rebuild the local full-text search index from that backup.
- The exported backup protects the file outside the app sandbox. After restore, files and metadata are stored in the app's normal local archive again.
The app uses standard cryptography for user-initiated file and backup protection, including password-protected PDFs and encrypted .m24backup files. It does not provide a general-purpose cryptographic service; document this as standard file/backup protection in the Play Console encryption declaration.
- Android 10+ (API 29)
- Google Play Services for ML Kit scanning and OCR
- Android SDK Platform 36.1 for building
./gradlew --no-configuration-cache compileDebugKotlin
./gradlew --no-configuration-cache testDebugUnitTest
./gradlew --no-configuration-cache lint
./gradlew --no-configuration-cache assembleDebug
./gradlew --no-configuration-cache assembleRelease
./gradlew --no-configuration-cache bundleReleaseUseful Android verification:
./gradlew --no-configuration-cache connectedDebugAndroidTest
./gradlew --no-configuration-cache --% connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=info.meuse24.pdf_scanner.ImportAndPdfEditorInstrumentedTestIf Hilt-generated classes go missing after an incremental install, force a fresh install without the Gradle build cache:
./gradlew installDebug --no-build-cache --rerun-tasksui/Jetpack Compose screens and view modelsui/entry/,ui/tile/, andui/widget/external app-entry bridgesdomain/usecase/business logicdomain/model/app/domain models including settings, theme, OCR status/result metadata, and PDF metadatadomain/repository/,domain/gateway/, anddomain/pdf/clean domain contractsdomain/common/pure Kotlin helpers shared by workflows and use casesdomain/workflow/orchestration and error mappingdata/Room entities, DAO, database, repositoryutil/Android/PdfBox/ML Kit implementations behind domain ports
Main editor flows:
annotate/full-screen PDF annotation editorredact/secure redaction editorimagestopdf/gallery images to PDF flowviewer/in-app PDF reader backed by Android PdfRenderertableexport/on-device table detection review and CSV/TSV exportformfill/AcroForm field editorsync/local Wi-Fi PC-Sync screenbusinesscard/,folders/,lock/feature screensshared/viewport math and text-snap helpers reused by editors
Recent structure work:
- Clean Architecture package boundaries:
domainno longer imports Android, UI, data, or util implementation types - Extracted domain gateways for storage, downloads, resource mapping, file access, OCR text extraction, searchable PDF generation, QR scanning, and review-prompt policy
- Moved persistent settings, theme mode, OCR quality/status/result models, and pure page-range/filename helpers into
domain - Added app shortcuts, a Quick Settings tile, a home-screen scan widget, and Android Share/Open-with import via a shared
AppEntryActionbridge - Added folders, favorites, app-lock settings, business-card vCard export, and Android Print integration
- Added optional OCR-based tags, tag filter chips, retroactive tagging from Settings, and OCR text TXT export
- Added an in-app PdfRenderer viewer with lazy page rendering, local bitmap cache, inline pinch-to-zoom with cross-page scroll, double-tap zoom toggle, debounced zoom re-render, zoom overlay, and viewer action bar
PdfEditorsplit into focused ops files for annotations, overlays, redaction, images, and shared core helpersHomeScreensplit into archive content, dialogs, sheets, and small screen models- String resources split by feature with
strings_annotate.xml,strings_images_to_pdf.xml,strings_shortcuts.xml,strings_folders.xml,strings_lock.xml, andstrings_businesscard.xmlin every locale - Legacy
HighlightScreenremoved; active editing now lives inannotate/andredact/ - Added on-device PDF text translation via ML Kit Translate with
TextTranslatordomain gateway,TranslateTextUseCase, andTranslationReviewScreen; models downloaded on demand, no language data bundled in the APK - Added AcroForm form filling (
PdfFormOpsport,FormFillWorkflow,formfill/UI) for text/checkbox/radio/combo/listbox fields, with multi-script Unicode fallback fonts chosen per field - Added local full-text search across OCR page text in the viewer with jump-to-match navigation, plus local IBAN/amount/date detection with copy and calendar-event actions
- Added local SHA-256 file hash calculation (
CalculateSha256UseCase) computed directly from file bytes, independent of the viewer - Added a local Wi-Fi PC-Sync feature: a PIN-protected Ktor (CIO) web server running as a foreground service (
LocalSyncService) lets a PC on the same network browse, download, and upload documents; no cloud or internet involved - Added on-device table extraction and CSV/TSV export (
domain/common/TableReconstructor.kt,ui/tableexport/): renders each page at 220 DPI, runs ML Kit OCR, reconstructs rows/columns purely from OCR geometry (no persisted bounding boxes), and offers an editable review grid before exporting; a JSON draft cache incacheDirsurvives process death during review
- JVM tests cover AutoTag scoring, the AutoTag settings toggle, retroactive tagging, OCR text TXT export, use cases, workflows, view models, and
PdfEditorhelpers - JVM tests also cover external app-entry decoding and navigation guards for Share/Open-with flows
- JVM tests cover the encrypted backup codec, ZIP payload/staging validation, merge restore, SAF orchestration use cases, and Backup UI state machine
- Business-card parsing/vCard generation and Room migrations are covered by unit or instrumentation tests where practical
- Viewer JVM tests cover
PdfViewerViewModelrender-window behavior and the bitmap cache - JVM tests cover
TranslateTextUseCase(page-text selection, blank-page filtering, fallback toextractedText, progress forwarding) andTranslationReviewViewModel(load record, translate success/error, progress cleared, duplicate-call guard) - JVM tests cover the table reconstruction algorithm (
TableReconstructorTest, synthetic layouts: grids, wrapped rows, deskew, competing regions), CSV/TSV encoding, delimiter defaults, export filenames, the draft store/codec, andTableExportViewModel(draft restore incl. stale-source detection, debounced draft saves, concurrency guards against double-tap re-extract and dialect races during export) - JVM tests cover local Wi-Fi PC-Sync HTTP routing, HTML rendering, session/PIN handling, and rate limiting
- Instrumentation tests cover Android-specific paths such as
PdfRenderer, URI import, MediaStore export, annotation rendering, redaction, image-to-PDF generation, and end-to-end table extraction against a real PDF via real ML Kit OCR
- Kotlin
- Jetpack Compose
- Hilt
- Room
- ML Kit Document Scanner
- ML Kit Text Recognition
- ML Kit Translate
- PdfBox-Android
- Ktor Server (CIO) for the local Wi-Fi PC-Sync web server
Detailed engineering notes live in CLAUDE.md.