InTouch for Android
:sdk builds a native Android AAR. :app is a foreground capture and fleet viewer example for Android 8.0 and later. The SDK uses Android framework sensors, location, SQLite, and HTTP APIs. It needs Internet access and the host app requests location permission if it wants coordinates.
Run on an emulator
- Start the local service from the repository root and create its local credentials. Boot a Google APIs AVD. Set
ANDROID_HOMEto the installed SDK directory (compile platform 36), use Java 17 or 21, and putplatform-toolson PATH. - In this directory run
./gradlew :app:assembleDebug :app:assembleDebugAndroidTest :sdk:assembleRelease. - Run
adb install -r app/build/outputs/apk/debug/app-debug.apk, then launch Mappls InTouch. The emulator reaches the host service viahttp://10.0.2.2:8917. On a physical device, use an HTTPS service or debug USB forwarding withadb reverse tcp:8917 tcp:8917andhttp://127.0.0.1:8917. - On Phone telemetry, enter the local Android device bearer token and tap Start capture. Grant location access if desired. The app samples the available sensors while its activity is visible. Tap Send queued samples to request delivery on the next worker tick. Sensor availability varies by device. Use the emulator Extended Controls to simulate location and rotation.
- In Device viewer, enter a viewer token, refresh, choose a device and inspect recent readings. Select Start live updates to follow the authenticated change stream; the viewer coalesces notifications and fetches detail at most once every two seconds. Select an available clip to download and play it. To add video from the capture screen, use the camera intent or select a local MP4/WebM file. Capturing video always requires user action. Tokens entered in the sample stay in activity memory. Device and viewer tokens have different API permissions.
The debug variant permits cleartext only for the emulator host alias and loopback. Release requires HTTPS. The sample never embeds a credential in the APK or persists the entered bearer tokens in preferences.
Embed the library
Include the :sdk module in your Gradle build, or copy sdk/build/outputs/aar/sdk-release.aar to the host application's libs directory and declare implementation files('libs/sdk-release.aar'). Add both Android location permissions to your app manifest for the optional location adapter. Supply a device-scoped token from your provisioning flow.
InTouch.Listener events = new InTouch.Listener() {
public void status(String message) { /* Callback may be on an SDK worker. */ }
public void samplingChanged(int milliseconds) {
// Call phone.configure() on your active PhoneSensors adapter.
}
};
InTouch device = new InTouch(context, origin, deviceToken, events);
PhoneSensors phone = new PhoneSensors(context, device, events);
// On a background thread, a BLE or USB sensor adapter can enqueue a burst.
JSONArray burst = new JSONArray().put(InTouch.sample("temperature",
System.currentTimeMillis(), new JSONObject().put("celsius", 4.8)));
device.record(burst);
device.requestFlush("manual");
// On foreground session termination, phone.close() also closes device.
Call blocking viewer and media operations from a background thread. Create a Transport(origin, viewerToken) for devices(), samples(id), media(id), or downloadClip(id,clip,destination). For a direct, immediate upload use a device credential for uploadClip(clipId,mime,file). Reuse the clip ID when retrying the same bytes. The host app owns consent, credential rotation and storage, background upload scheduling and any background collection.
Sampling and delivery
| Stream | Native adapter and units |
|---|---|
| Location | GPS with fine permission, network with coarse permission. Latitude/longitude in degrees, horizontal accuracy in metres, optional speed in m/s and bearing in degrees. |
| Orientation | Android rotation vector converted to w,x,y,z quaternion. Sensor axes use the Android device frame. |
| Movement | Linear acceleration excluding gravity in m/s². An acceleration magnitude at or above motionThreshold adds an incident and requests an early send, with a 5 s cooldown. |
| Diagnostics | Battery percentage, charging state, model and SDK level recorded once per minute. |
| Video | Short camera intent or file picker, fixed-length streaming upload up to 16 MiB. Camera, encoder, permission UX and recording limits depend on the chosen camera app. |
Sensor events accumulate for up to 100 readings or approximately one second before SQLite commit. A sudden process kill can lose the uncommitted tail. Network retries preserve the committed ID and payload until a receipt arrives. The 8,192-row local queue caps accepted data and retains queued readings. At capacity the adapter keeps one in-memory burst and reports saturation; later sensor readings can be lost until space returns. Rows are bounded at 4 KiB. A credential change uses a separate queue to prevent cross-device replay. Recover/drain the original credential queue before rotation. Clear app storage to erase the local notebook and queued location samples.
Each HTTP batch contains at most 100 readings. A worker handles periodic, count, event, manual and remote flush triggers. It drains up to eight batches per turn. Failed connections back off with jitter to about one minute. Heartbeats deliver bounded sampling and flush intervals, the motion threshold, desired revision and flush commands. Sensor FIFO latency is requested at 1 s where supported. Timestamp conversion preserves sensor event time relative to the host wall clock. Storage, sampling and network traffic can grow quickly at short intervals; see the platform performance spec for profile targets.
The sample stops collection when backgrounded. Persistent unattended tracking requires a separately designed foreground service with user-visible notification and platform permissions. Clips use a separate, device-scoped persistent outbox. Pass an input stream to device.queueClip(mime, bytes) on a worker thread. It accepts up to four clips, syncs each temporary file before marking it ready, and retains the ID and bytes until the server acknowledges the upload. A failed clip retries after about one minute while a session is running. Resume the same session identity after an app restart to drain cached clips. WebRTC, MQTT, remote physical actuation and continuous background video are outside this Android release.
Reproduce the integration check
With the local backend up and both APK variants built, run:
python3 smoke.py --serial emulator-5554 --control
# Add --clip /path/to/a-small.mp4 to check authenticated upload, listing and byte-identical download.
The optional --control check enqueues a local operator flush after installation, waits during active capture, and verifies the device acknowledged the command before expiry. A control flush takes a snapshot of the highest committed queue sequence so that new incoming readings can continue while the snapshot drains.
The check reads the root-local .env, streams credentials into an app-private debug file over ADB, grants foreground location permissions, runs a hardware sensor/diagnostics adapter and submits a uniquely tagged diagnostic. It verifies the authenticated SSE cursor, fleet and sample list with the separate viewer credential, then drives the native fleet and device screens. It removes temporary credentials and fixture files on completion. Samples and optional test media remain in the local platform database. This test verifies emulator integration; phone battery draw, camera capture quality, absent-sensor behavior and sustained cellular operation require physical-device testing.