# PaperScan A local, privacy-conscious document scanner for Android. Scan paper documents with your camera, automatically detect the edges, correct the perspective, and bundle multiple pages into a single PDF. That PDF can be shared through Android or uploaded directly to your self-hosted **Paperless-ngx**. **Everything happens on the device.** Camera frames, edge detection (OpenCV) and PDF creation never leave your phone. The only network connection is the optional upload to the Paperless server you configure yourself. There is no AI or cloud processing. ## Features - 📷 Camera preview with multi-page capture (add pages one by one) - ✂️ Automatic edge detection + perspective correction (OpenCV), with manual corner adjustment - 🎨 Per-page filters: color, grayscale, black & white (document mode) - 🔄 Rotate, reorder and delete pages - 📄 Export to a multi-page PDF (A4 or original size) - ☁️ Direct upload to Paperless-ngx **or** share via the Android share sheet - 🔊 Capture feedback: shutter sound (only when the ringer isn't silenced) + a brief flash - 🔐 API token stored encrypted (Android Keystore / EncryptedSharedPreferences) ## Tech Kotlin · Jetpack Compose · CameraX · OpenCV (`org.opencv:opencv` via Maven) · `PdfDocument` · OkHttp · DataStore. minSdk 26 (Android 8), target/compile SDK 35. ## Building locally 1. Open the project in **Android Studio** (Ladybug or newer). This automatically generates the Gradle wrapper (`gradlew` + `gradle-wrapper.jar`). 2. Connect a device or start an emulator and press **Run**. Or from the command line (after `gradle wrapper` once): ```bash ./gradlew assembleDebug # debug APK, no signing required ./gradlew assembleRelease # release APK, requires signing (see below) ``` The release APK ends up in `app/build/outputs/apk/release/`. ## Creating a signing key Required for release builds (and therefore for updates that can overwrite each other). ```bash keytool -genkey -v -keystore release.jks -keyalg RSA -keysize 2048 \ -validity 10000 -alias paperscan ``` Keep `release.jks` **safe and out of git** (already in `.gitignore`). For local release builds, create a `keystore.properties` in the project root (see `keystore.properties.example`): ```properties storeFile=/path/to/release.jks storePassword=... keyAlias=paperscan keyPassword=... ``` ## Building via Gitea Actions The workflow `.gitea/workflows/build.yml` builds a signed release APK on every version tag (`git tag v0.1.0 && git push --tags`) or via a manual run, and publishes it as a **Release** with the APK attached as a downloadable asset. Add these secrets under **Settings → Actions → Secrets** in your Gitea repo: | Secret | Contents | | --- | --- | | `SIGNING_KEYSTORE_BASE64` | `base64 -w0 release.jks` (the keystore as base64) | | `SIGNING_STORE_PASSWORD` | keystore password | | `SIGNING_KEY_ALIAS` | e.g. `paperscan` | | `SIGNING_KEY_PASSWORD` | key password | The build uses env vars, so nothing sensitive ends up in the repo. > The build runs in a container (`mingc/android-build-box`), so the Actions runner must be > **x86_64** — the Android build tools (aapt2) are x86_64-Linux only and won't run natively > on an ARM64 runner. ## Setting up Paperless-ngx (in the app) 1. Create an API token in Paperless: **Settings → profile → API token** (or via `/api/token/`). 2. In PaperScan, open the gear icon → enter the **server URL** (e.g. `https://paperless.home.lan`) and the **token** → **Test connection** → **Save**. Prefer **https**; with `http://` the app warns that the token and documents travel over the network unencrypted. ## Project layout ``` app/src/main/java/eu/geyskens/pdfscan/ ├── PaperScanApp.kt # Application; loads OpenCV native libs ├── MainActivity.kt # Compose host + navigation ├── ScanViewModel.kt # scan session, rendering, export, upload ├── data/ # Page model, ScanFilter, SettingsRepository ├── scan/ # EdgeDetector, ImageProcessor, BitmapIo (OpenCV) ├── pdf/ # PdfBuilder (multi-page PDF) ├── paperless/ # PaperlessClient (OkHttp REST + task polling) └── ui/screens/ # Camera, Pages, Crop, Export, Settings ``` ## Roadmap Tracked as issues in the repository: - Live edge-detection overlay while aiming (like Microsoft Lens) - Internationalization: extract UI strings to resources + add translations - Batch import scans from the gallery > OCR is intentionally **not** done on-device: Paperless-ngx already runs OCR (via > OCRmyPDF/Tesseract) when it consumes the uploaded document.