Discover compatible API versions, client channels and mobile extensions before login. Follow server process completion, metadata updates, form rules and cancellation, with isolated native OpenAPI conformance coverage. Co-Authored-By: OpenAI GPT-6 (Codex) <noreply@openai.com>
QQQ Android
Native Kotlin/Compose clients for QQQ. The new scanner-host application runs server-defined QQQ apps and processes on Android handhelds, initially Zebra TC22.
Modules
qqq/android/core: metadata and process HTTP client (QQQRepository/NetworkQQQRepository).qqq/android/mobileapp: native app picker, authentication, process runner, and widget renderer.scanner-host: server configuration, device registration/approval gate, and DataWedge barcode capture.qqqsampleandroidmobileapp: historical example application.
The client supports metadata and process execution. It does not implement the record CRUD/fluent API previously described by this README.
The scanner host uses the versioned semantic UI API for app discovery, native process screens and actions, plus the registered mobile-scanners extension for device enrollment, branding and connectivity. It checks core and extension compatibility before login. See the client contract and supported components; full web-client feature coverage is not implied.
First scanner application
Use the companion qbit-mobile-scanners server/demo with QQQ 4.1.0-RC.20. The demo uses QQQ TABLE_BASED username/password authentication. Credentials are exchanged through the native QQQ session endpoint; the password is not stored.
- Start the companion demo and install the debug APK.
- Run
adb reverse tcp:8080 tcp:8080for a USB-connected TC22. - Open QQQ Mobile, use
http://127.0.0.1:8080/, and sign in. - An administrator approves the displayed device ID in QQQ.
- Tap Check approval, select the server-provided mobile app, and open Scan capture.
- Scan a barcode and wait for Saved scan before scanning again. Manual entry is available for development and non-Zebra devices.
The server defines the app, native process steps, and SCANNER widget; the APK does not hard-code a scan process name. Existing native widget types continue to work.
Zebra DataWedge profile
Create a profile associated with io.qrun.mobile.scanner, activity *:
- Enable Barcode input and single-barcode decoding.
- Disable Keystroke output.
- Enable Intent output with action
io.qrun.mobile.scanner.SCAN. - Set category
android.intent.category.DEFAULTand delivery to Broadcast. - Target package
io.qrun.mobile.scannerwhen package targeting is available.
The receiver is active only while a scanner widget is visible and its activity is resumed. It reads com.symbol.datawedge.data_string, label_type, and source. Multi-decode payloads are rejected. Symbology/source are client-reported metadata, not proof of scanner hardware identity. See Zebra Intent output.
Customer branding and diagnostics
The shared launcher name/icon remain QQQ Mobile. On connecting to a server, the host reads public GET /qqq/v1/qbit/mobile-scanners/branding (displayName, primaryColor, optional logoUrl). The name and Material theme apply to login, device approval, and native app screens. Names are limited to 80 printable characters and colors to #RRGGBB; malformed values fall back to QQQ defaults. Button foregrounds choose readable black/white contrast. Optional logos must use HTTPS, are fetched without session/device headers or cookies, and cannot prevent login if unavailable. Responses are size-limited, redirects are disabled, and branding is reset when changing servers. Reconnect to pick up server branding changes; no APK rebuild is needed.
The persistent footer shows Checking, Connected, or Disconnected with the last check time. It checks GET /qqq/v1/qbit/mobile-scanners/status every five seconds while foregrounded and on network changes; Connected means a valid { "status": "ok" } response, not sign-in or device approval. Tap the footer for Copy, Clear, and Close diagnostics. The in-memory log holds the latest 100 fixed event codes with timestamps, HTTP status, and latency for branding, connection checks, native QQQ calls (including login/process requests), and device enrollment/status requests. A transport failure has no HTTP status; an HTTP response retains its status, including 401/403/500 errors. The log contains no credentials, scan values, request paths, queries, raw response text, or exception messages. Changing servers clears the displayed diagnostics. Monitoring never queues or retries a scan.
Online-only behavior
There is no offline queue. One submitted scan is retained in memory until its acknowledgedEventId is returned. If a response is uncertain, Retry same request preserves its event ID and original timestamp; the server deduplicates it. Further scans are blocked until the pending request is resolved. Sign-out/reset clears the pending request; app termination loses in-memory state. Repeated physical scans deliberately receive distinct event IDs.
Enrollment credentials are encrypted with Android Keystore and stored under noBackupFilesDir, scoped to the configured server. The app disables backup. Device headers accompany native QQQ requests, and the server enforces approval on every scan. Release builds reject HTTP; only the development build permits it for local testing. The host disables HTTP redirects to avoid forwarding credentials to another origin.
Build
Requirements: Java 17 or compatible newer runtime, Android SDK platform35, build tools34/35, and the included Gradle8.7 wrapper. Configure ANDROID_HOME or ignored local.properties.
.\gradlew.bat :scanner-host:testDebugUnitTest :scanner-host:assembleDebug :scanner-host:lintDebug
APK: scanner-host/build/outputs/apk/debug/scanner-host-debug.apk.
The existing libraries also have unit and emulator tests. testDebugUnitTest runs JVM tests; connectedDebugAndroidTest needs a connected device/emulator. Physical TC22 trigger/intent behavior must still be verified on hardware.
See emulator testing for a local interactive demo, synthetic scanner broadcasts, and the disposable-emulator smoke check.
CI and delivery
The managed CircleCI Android profile owns shared-library verification and its existing main-only Maven publication. The GitHub Scanner Android workflow adds shared/scanner JVM tests, scanner lint, debug APK assembly, SHA256, and downloadable artifacts for pull requests and supported branches. The API35 emulator lane runs the shared native UI tests and real-server scanner E2E on pushes and pull requests. See emulator testing for the isolated test package and contract checks.
The GitHub workflow does not publish libraries or production releases. Signed APK distribution remains a separate release task. See Contributing, Security, and Changelog.
Extension points
LocalAuthenticatedContent allows a host to gate authenticated QQQ content. LocalCustomWidgetRenderer lets the host render additional metadata-defined widgets without replacing the standard renderer. NetworkQQQRepository accepts a server-scoped header provider; credentials never enter process values.
The first scanner host supports TABLE_BASED authentication only and shows an explicit unsupported-provider message for other authentication types. Existing shared-library Auth0 support remains intact; production identity-provider integration requires callback configuration and verification before enabling it in this host.
License
Root license: Apache-2.0. Historical source files still carry older license headers; the repository's pre-existing license-header inconsistency has not been resolved by this implementation.