Android App Overview
The Qlarr Android app is a native mobile application for offline-first survey data collection.
Repository
Role in the System
The Android app focuses exclusively on running surveys. It downloads survey designs and resources from the Backend, runs them locally using its own copy of the Survey Engine (KMP), and syncs responses back when connectivity is available.
Surveys are rendered through an embedded WebView that loads the Frontend React app from bundled assets. A JavaScript bridge connects the WebView to native Android capabilities (camera, GPS, audio recording, barcode scanning).
Technology Stack
- Kotlin with Jetpack Compose (Material 3)
- Survey Engine (KMP) — survey logic and navigation (via JitPack)
- Room — local SQLite database for surveys and responses
- Retrofit + OkHttp — backend API communication with JWT interceptor
- WorkManager — background response sync
- Koin — dependency injection
- Coil — image loading
- ExoPlayer (Media3) — video playback
Key Features
- Offline-First: Surveys are downloaded and cached locally. All responses are stored in Room and synced when connectivity is available.
- Background Sync: WorkManager queues response uploads with automatic retry and backoff.
- Media Capture: Camera photos (with compression), video recording, file selection, and barcode/QR scanning via native Android APIs.
- Sensor Integration: GPS location tracking and background audio recording during surveys.
- Guest Mode: Bundled example surveys that work without authentication for demo purposes.
- Survey Versioning: Detects when a newer survey version is available on the server.
Offline Data Collection
The Android app is purpose-built for collecting survey data in the field where internet connectivity is unreliable or unavailable. The entire offline workflow is designed so that surveyors never lose data:
-
Survey download — When connected, the app downloads survey designs, resources (images, media), and autocomplete data from the backend. Everything is cached locally in Room and on-device file storage.
-
Local execution — Surveys run entirely on-device using the embedded Survey Engine (KMP). No server communication is needed during data collection. The engine generates the
runtime.jsstate machine from the cached design and handles all navigation, validation, and conditional logic locally. -
Response storage — Each completed response is saved to the local Room database with all its values, timestamps, events (GPS coordinates, audio recordings), and file attachments (photos, videos, signatures). Responses are marked as
isSynced = falseuntil successfully uploaded. -
Background sync — When connectivity is restored, Android's
WorkManagerqueues a sync job that uploads responses in the background. The sync process:- Finds all unsynced, submitted responses across all active surveys
- Uploads file attachments first (photos, videos, audio recordings), checking each file against the server to avoid duplicate uploads
- Uploads the response data (values, timestamps, navigation state, events)
- Marks each response as synced and updates local response counts
- Retries automatically with linear backoff if the upload fails
-
Version tracking — The app tracks the published version of each survey. When a newer version is available on the server, it notifies the surveyor so they can update before collecting new responses.
For more details on configuring surveys for offline use, see Offline Mode.
Architecture
WebView Integration
The app uses a custom QlarrWebView that loads the React frontend from bundled assets. The WebView intercepts requests to serve survey resources locally:
- Survey runtime (
runtime.js) — generated by the KMP engine from the cached design - Survey resources (images, media) — served from local file storage
- Response attachments — served from local storage
A JavaScript interface (Android object) exposes native capabilities to the web frontend:
| Method | Purpose |
|---|---|
navigate(body) | Navigate survey questions |
start() | Start a new response |
autoSaveValues(values) | Auto-save progress and quit |
capturePhoto(key, maxSize) | Open camera for photo capture |
captureVideo(key, maxSize) | Open camera for video recording |
selectFile(key, accepted, maxSize) | Open file picker |
scanBarcode(key) | Open barcode/QR scanner |
searchAutoComplete(uuid, query) | Search autocomplete values |
Data Flow
- User logs in (email/password or Google) — tokens stored in
SessionManager - Survey list is fetched from the backend and cached locally
- Survey designs and resources are downloaded and stored in Room + local files
- User opens a survey —
QlarrWebViewrenders the React frontend - The KMP engine handles navigation, validation, and conditional logic
- Responses are saved to Room with
isSynced = false WorkManageruploads responses and file attachments in the background- Response counts are updated after successful sync
Local Database
The Room database stores two entities:
- SurveyDataEntity — survey metadata, cached design, publish version, feature flags (GPS, audio, timings), response counts
- Response — response values, navigation state, sync status, language, timestamps, events (location, audio recordings)
Requirements
- Android 7.0+ (API 24)
- Google Chrome (for WebView rendering)