Scan, organize and read your documents. All of it on your device.
A document scanner, a searchable library and a PDF reader in one Android app.
No account, no cloud, no watermarks.
Features · Privacy · Under the hood · Build it · Contributing
|
|
|
|
|
|
Languages
English, Arabic, Chinese, Dutch, French, German, Hindi, Italian, Japanese, Korean, Portuguese, Russian and Spanish.
Docucraft is local-first: your documents never leave your device unless you share them.
- Scanning and text recognition run on the device, through Google's ML Kit. Nothing is uploaded to be processed.
- No account and no cloud. Documents are kept in the app's private storage, and the library and its search index in a local database.
- Other apps' PDFs are opened, not copied. Docucraft keeps a reference to them and nothing else, until you choose Save to Docucraft.
- Links are checked before they open. Only web, email and phone links are followed, and a web link is shown by the address a browser would really open, so a look-alike does not pass for the real one.
- What is collected: usage events and crash reports, through Firebase Analytics and Crashlytics. Never your documents, nor their text.
| Area | Technology |
|---|---|
| Language | Kotlin, with coroutines and Flow |
| UI | Jetpack Compose, Material 3 Expressive, Material 3 Adaptive, platform RemoteViews for the widgets |
| Navigation | Navigation 3: one back stack, typed keys, sheets and dialogs as destinations |
| Architecture | Clean architecture by feature, MVI ViewModels, ports and sealed results |
| Dependency injection | Koin |
| Storage | Room with FTS4 full-text search, DataStore |
| Background work | WorkManager |
| On-device ML | ML Kit Document Scanner and Text Recognition |
An in-house Compose engine over the platform's PdfRenderer |
|
| Theming | MaterialKolor, Haze |
| Files | FileKit |
The app is a shell over feature modules, which share the library's domain and UI and open each
other only by navigation keys, kept in :core:navigation. Domains are plain Kotlin modules, and
every SDK sits behind a contract, so the scanner and the text recognizer can each be replaced by
changing one line of dependency injection, and no ML Kit type ever reaches a feature.
graph TD
app[":app<br/><i>shell: activities, back stack, engine bindings</i>"]
features[":feature:*<br/><i>home, search, organization, bin, scan,<br/>document-actions, pdfviewer, settings, widgets</i>"]
library[":library:domain · :library:ui<br/><i>the catalogue's rules and shared UI</i>"]
libraryData[":library:data<br/><i>Room, files, search, indexing</i>"]
core[":core:domain · :designsystem · :navigation · :ui"]
coreData[":core:data<br/><i>preferences, analytics</i>"]
composepdf[":composepdf<br/><i>PDF engine</i>"]
apis[":scanner:api · :content:api<br/><i>the engines' contracts</i>"]
engines[":scanner:mlkit · :content:platform · :content:mlkit<br/><i>the engines, named only by the app</i>"]
app --> features
app --> libraryData
app --> coreData
app --> engines
engines --> apis
features --> apis
features --> library
features --> core
features --> composepdf
libraryData --> library
library --> core
How each part works, and why it was built that way, is written down in docs/:
| Document | Covers |
|---|---|
| Architecture | Modules, layers, ports, ViewModels, theme and blur |
| Navigation | One back stack, scenes, modal destinations |
| Scanning | The scanning contract, saving, search, reading the text of pages |
| Database | The catalogue's tables and rules, migrations, the bin |
| Organization | Folders, tags and favorites |
| PDF engine | Rendering, layout, gestures, extension points |
| PDF viewer | The viewer's screen, settings and the viewer for other apps' PDFs |
| Text and links | Page content, text selection, safe links |
| Testing | What is tested where, fixtures, checking by hand |
You need a recent Android Studio, JDK 17, and a device or emulator on Android 7.0 (API 24) or later with Google Play services, which provide the scanner and the recognition model.
git clone https://github.com/BobbyESP/Docucraft.git
cd DocucraftFirebase is part of the build and its configuration is not in the repository. Create a
Firebase project, register the Android apps
com.bobbyesp.docucraft and com.bobbyesp.docucraft.debug, and place its google-services.json in
app/.
Then open the project in Android Studio and run it, or from a terminal (on Windows,
.\gradlew.bat):
| Task | Command |
|---|---|
| Debug APK | ./gradlew :app:assembleDebug |
| Unit tests | ./gradlew testDebugUnitTest |
| Instrumented tests | ./gradlew connectedDebugAndroidTest |
| Format | ./gradlew spotlessApply |
Note
Instrumented tests run on every attached device. Set ANDROID_SERIAL to target one.
- Google Play services are required for scanning and for text recognition.
- Selecting a PDF's own text needs Android 15 or later. On earlier versions, text recognition covers it for documents in your library.
- Search finds whole words and their beginnings, and does not yet split Chinese, Japanese or Korean text into words.
- PDFs are opened from the device. One behind a web address is not downloaded yet.
Issues and pull requests are welcome. Before changing a subsystem, read its document in
docs/ and the rules in AGENTS.md.
- Commits follow
type(scope): summary, and the body says why. - Run
./gradlew spotlessApplybefore committing. - A new string needs its translation in every language of
app/src/main/res/values-*.
Docucraft is free software, distributed under the GNU General Public License v3.0. See LICENSE.
Built with ❤️ by Bobby
If Docucraft is useful to you, a star helps others find it. ⭐