LetterLens scans a paper letter, extracts text with ML Kit, classifies it, pulls deadlines, and returns what/when/next. Android UI is Jetpack Compose; the API is Ktor; and the core parsing is moving to Kotlin Multiplatform.
Demo: https://youtube.com/shorts/lvZbWHVL1h4
Repo: https://github.com/Qandil11/LetterLens
Architecture (minimal)
Android app: Compose + ML Kit OCR → sends extracted text
Ktor server: /explain returns { type, deadline, summary, actions, citations }
KMP (WIP): move parsing/classifier into :shared for reuse
Run it locally (quickstart)
// Start the server
./gradlew :server:run
// healthcheck
curl http://127.0.0.1:8080/health
Device networking options
Emulator: base URL = http://10.0.2.2:8080/
Physical device (USB):
adb reverse tcp:8080 tcp:8080
base URL = http://127.0.0.1:8080/
Physical device (Wi-Fi): use your Mac/PC LAN IP, e.g. http://192.168.x.y:8080/
<!-- app/src/debug/AndroidManifest.xml -->
<application android:usesCleartextTraffic="true" />
Tiny Ktor module (sketch)
fun Application.module() {
install(ContentNegotiation) { json() }
routing {
get("/health") { call.respond(mapOf("ok" to true)) }
post("/explain") {
val req = call.receive<ExplainReq>()
val type = classify(req.text, req.hint)
val deadline = extractDeadline(req.text, type)
val (summary, actions, citations) = explainForType(type, req.text)
call.respond(ExplainRes(type, deadline, summary, actions, citations))
}
}
}
Why KMP here?
The classification/date parsing is pure Kotlin → easy to share across Android/iOS/desktop.
Keeps the server thin; long-term we can ship fully on-device.
Roadmap
Swap rule-based classifier for a compact model
Expand letter libraries (NHS/Council/HMRC/DVLA/UKVI)
Accessibility polish and offline mode
Tags: #android #kotlin #kmp #jetpackcompose #ktor #mlkit #ocr #nlp #govtech
Top comments (0)