From fa9f1de637a8cbe9fd76c0903dc3f88051fef6e1 Mon Sep 17 00:00:00 2001 From: lkasso Date: Sun, 16 Aug 2026 19:28:48 -0700 Subject: [PATCH 1/9] README: full reference manual for the SDK and app Table of contents, requirements and supported boards, quick start, architecture and layer-by-layer breakdown, the sensor protocol interfaces, one documented subsection per board module with code samples verified against the sources, the logging guide (download, MMS flush, CSV, calibration, board state, anonymous signals, polled readables, processor handles), firmware updates and MetaBoot, Room persistence, data modes, BLE packet format, testing, demo mode, design notes and known gaps. HARDWARE.md test count refreshed. Co-Authored-By: Claude Fable 5 --- HARDWARE.md | 2 +- README.md | 2071 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 1778 insertions(+), 295 deletions(-) diff --git a/HARDWARE.md b/HARDWARE.md index f797667..7369542 100644 --- a/HARDWARE.md +++ b/HARDWARE.md @@ -1,6 +1,6 @@ # Hardware verification guide -Everything in this repo is verified by 1,173 JVM tests that run without +Everything in this repo is verified by 1,274 JVM tests that run without hardware. Three things remain that can only be proven against a real board and phone. This guide is the exact procedure. diff --git a/README.md b/README.md index 9fa6d9e..9dfa0a2 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,89 @@ # MetaWear Android SDK (Kotlin) -A coroutine/Flow-native Kotlin SDK for MbientLab MetaWear sensors, built for -Android. Structured **KMP-ready, Android-only**: the pure protocol/parsing -layer has zero Android dependencies and is unit-tested on a plain JVM, while -the transport and app layers are Android-specific. +A coroutine/Flow-native Kotlin implementation of the [MetaWear protocol](https://github.com/mbientlab/MetaWear-API) for Android. -## Module layout +This repository contains both the reusable SDK modules and the MetaWear Android app: + +- Use `:metawear-protocol` + `:metawear-core` when you need the scanner, device state machine, BLE transport, protocol router, and every sensor/module API. +- Add `:metawear-persistence` when you want Room-backed session storage and CSV export. +- Add `:metawear-firmware` only when your app needs over-the-air (Nordic DFU) firmware updates. +- Open `app/` for the full MetaWear app — see [The MetaWear App](#the-metawear-app). + +The SDK is structured **KMP-ready, Android-only**: the pure protocol/parsing layer has zero Android dependencies and is unit-tested on a plain JVM, while the transport, persistence, firmware, and app layers are Android-specific. + +## Table of Contents + +| Start here | Use it for | +|------------|------------| +| [Requirements](#requirements) | Toolchain and Android versions | +| [Supported boards](#supported-boards) | Which MetaMotion boards the SDK targets and how the model is detected | +| [What ships in this repository](#what-ships-in-this-repository) | The five Gradle modules and what each one is for | +| [The MetaWear App](#the-metawear-app) | The full Jetpack Compose app — what it does, running it (incl. demo mode), and how it's built | +| [Development workflow](#development-workflow) | Repository layout, common Gradle commands, toolchain notes | +| [Quick Start](#quick-start) | Adding the SDK, permissions, scanning, connecting, streaming, and sending simple commands | +| [Architecture](#architecture) | Understanding the scanner/device/protocol/transport layering | +| [Layer-by-layer breakdown](#layer-by-layer-breakdown) | Scanner, device state machine and key signatures, transport split, protocol router | +| [Sensor protocols](#sensor-protocols) | The capability interfaces every sensor implements, plus generic read/poll | +| [Supported sensors and modules](#supported-sensors-and-modules) | Finding the Kotlin type and configuration shape for each MetaWear module | +| [Logging](#logging) | On-device flash logging, typed downloads, polled loggers, anonymous logger recovery, and CSV export | +| [Firmware updates](#firmware-updates) | Over-the-air DFU — catalog checks, flashing, bootloader chaining | +| [Persistence (Room)](#persistence-room) | Saving downloaded sessions and reconstructing typed samples | +| [Data modes](#data-modes) | Streaming vs. logging, and the flash entry layout | +| [BLE packet format](#ble-packet-format) | Wire format, characteristics, module IDs | +| [Testing](#testing) | Running JVM unit tests, the mock transport, and the instrumented hardware suites | +| [Demo mode](#demo-mode) | The hardware-free MetaMotion S emulator | +| [Design notes](#design-notes) | Kotlin/coroutines gotchas the codebase relies on | +| [What's not yet implemented](#whats-not-yet-implemented) | Known gaps | + +## Requirements + +| Requirement | Version | +|------------------------|-------------------------------------------------------------| +| Android (runtime) | 8.0+ (`minSdk 26`); Android 12+ (API 31) recommended for the new `BLUETOOTH_SCAN` / `BLUETOOTH_CONNECT` permissions | +| Android (compile) | `compileSdk 36`, `targetSdk 36` | +| JDK (build) | 21 (Android Studio's bundled JBR works) | +| Gradle / AGP | Gradle 9.6, Android Gradle Plugin 9.2.1 | +| Kotlin | 2.1.20 (pure-JVM module), AGP 9 built-in Kotlin (Android modules) | +| IDE | Android Studio | +| Hardware for the app / instrumented tests | An Android phone with Bluetooth LE and a MetaMotion board | + +Key library versions: kotlinx-coroutines 1.10.1, kotlinx-datetime 0.6.2, Room 2.8.4 (KSP 2.3.9), Nordic Kotlin BLE Library 1.3.1, Nordic Android DFU Library 2.9.0, Compose BOM 2025.06.01. + +--- + +## Supported boards + +This SDK targets MbientLab MetaMotion boards. Anything else (legacy MetaWear R / RG / RPro / C / CPro, MetaMotion C, MetaEnvironment, MetaTracker, MetaHealth) decodes as `BoardModel.Unknown` — connect / read may still work but module-level behaviour is not validated. + +| Board | Model number (`0x2A24`) | Hardware revisions (`0x2A27`) | Verification status | +|-------------------|:-----------------------:|:---------------------------------------|:-------------------------------------------------| +| MetaMotion R / RL | `5` | `r0.1`, `r0.2`, `r0.3`, `r0.4`, `r0.5` | BMI160 wire-vector tests (JVM) | +| MetaMotion S | `8` | `r0.1` | BMI270 — verified on hardware (Pixel 7 + MetaMotion S) | + +`BoardModel` decodes the Model Number characteristic into a typed value; `DeviceInformation.model` and `DeviceInformation.isHardwareRevisionSupported` cross-check the revision against the table above: + +```kotlin +val info = device.deviceInfo ?: return +println(info.model.displayName) // "MetaMotion R / RL" or "MetaMotion S" +println(info.model.supportedHardwareRevisions) // ["r0.1", ..., "r0.5"] or ["r0.1"] +println(info.model.hasMMS) // true on MetaMotion S (larger flash, flush-page before download) +if (!info.isHardwareRevisionSupported) { + // Either an unsupported board model or a revision not on file. +} +``` + +The validator is forgiving about formatting — `"r0.4"`, `"R0.4"`, and `"0.4"` all match. + +--- + +## What ships in this repository ``` metawear-android/ ├── metawear-protocol/ ← pure Kotlin/JVM. Packet builder, parser, value types, │ sensor interfaces + configs, BleTransport seam + mock, │ protocol router, MetaWearDevice, MetaWearScanner. -│ Fast JVM unit tests (Steps 1–3 of the build plan). +│ Fast JVM unit tests. ├── metawear-core/ ← Android library. Nordic-backed BleTransport │ (NordicBleTransport), unfiltered scan source │ (AndroidBleScanSource), AndroidMetaWear entry point, @@ -24,289 +95,1657 @@ metawear-android/ │ release-catalog client, bootloader interlock, │ Flow-based DFU progress, MetaWearDevice extensions. │ JVM unit tests (catalog/version/interlock logic). -└── app/ ← Jetpack Compose demo app: scan, live streaming with +└── app/ ← Jetpack Compose MetaWear app: scan, live streaming with ring-buffer decimation, on-device logging + download, - session history with CSV export, LED/haptic controls, - device settings, firmware updates, and a - hardware-free demo mode (protocol-level MetaMotion S - emulator). -``` - -`:metawear-protocol` is the foundation everything else builds on, written -test-first to lock wire-format correctness before any BLE code. The full -vertical slice — scan → connect → `startStream(accelerometer)` → -`Flow>` — runs end-to-end against `MockBleTransport` -on the JVM, and against real hardware via `:metawear-core`'s smoke suite. - -The repo carries **1273 JVM tests** across the four testable modules -(1041 protocol + 46 persistence + 66 firmware + 120 app), plus instrumented -suites that need a device. - -## What's in `:metawear-protocol` - -The pure-JVM protocol layer (`com.mbientlab.metawear`): - -- `protocol.Module`, `protocol.Packet`, `protocol.PacketParser` — module - opcodes, command packet builder, and notification parser. All multi-byte - payloads are little-endian. -- `protocol.{Sensor, Streamable, Loggable, Readable, Command, CommandSequence, - Pollable}` — the capability interfaces every sensor module implements. -- `model.*` — pure value types: `CartesianFloat`, `Quaternion`, `EulerAngles`, - `Timestamped`, `Frequency`, `ModuleInfo`, `BoardModel`, `BoardState`, - `AnonymousSignal`, `DataTable` (CSV export), and the `MetaWearException` - hierarchy. -- `sensor.*` — one namespace per board module: accelerometers (BMI160/BMI270 - plus the Bosch interrupt surface and BMI270 step/activity features), - gyroscopes, magnetometer, sensor fusion + calibration, barometer/altimeter, - ambient light, thermometer, humidity, LED, haptic, switch, iBeacon, debug, - settings, timers, events, macros, GPIO, serial passthrough, data processors, - and misc readables. -- `transport.{BleTransport, ScanResult, WriteType}` + `MockBleTransport` — the - platform-agnostic BLE seam and its scriptable JVM mock. -- `protocol.ProtocolRouter` — routes BLE notifications between the transport - and the sensor modules. -- `MetaWearDevice`, `DeviceState`, `MetaWearScanner` — connection state - machine, streaming, send/read/poll surface, and scanner. - -The transport *interface* lives here (it is pure JVM: `java.util.UUID`, -`ByteArray`, `Flow`) so the protocol router and device layer stay -JVM-testable against `MockBleTransport`. Only the Nordic-backed implementation -is Android-specific and belongs in `:metawear-core`. - -Peripherals are identified by their Android MAC address, so the transport seam -uses an opaque `String` identifier. - -All 22 board modules are covered, plus the full device-side logging -surface: `startLogging`/`stopLogging` (including polled readables via the -timer→event→logger chain and processor handles), `downloadLogs` with chunk -reassembly and watchdog, `clearLog`/`flushLogPage`, logger/processor query and -recovery, anonymous-signal reconstruction, `factoryReset`, board-state -capture/restore, and `DataTable` CSV export. Data-processor streaming uses a -per-id demux: one shared `(0x09, 0x03)` subscription fans packets out to -per-processor-id flows, so multiple processors stream simultaneously; the -flows complete cleanly on an intentional disconnect and fail with the -underlying error on an unexpected one. - -**1041 JVM tests** cover this module, including reference byte vectors from -the MetaWear C++ SDK's Python test suite. - -## What's in `:metawear-core` - -The Android-only transport layer (`com.mbientlab.metawear.core`), built on the -[Nordic Kotlin BLE Library](https://github.com/NordicSemiconductor/Kotlin-BLE-Library): - -- `NordicBleTransport` — per-peripheral connect/write/read/notify/RSSI. -- `AndroidBleScanSource` — the unfiltered BLE scan source. -- `AndroidMetaWear` — default `MetaWearScanner()` wiring. -- `androidTest/HardwareSupport`, `HardwareSmokeTest` + 10 per-module suites - (gyro BMI270, magnetometer, sensor fusion, switch, haptic, GPIO, settings, - logging round-trip, environment, one-shot reads); suites self-skip without - a board. - -Transport notes: -- `connect()` retries Android's transient status-133 (`GATT_ERROR`) failures, - discovers all services (so Device Information Service reads resolve by - characteristic UUID alone), requests MTU 247 (packed 3-sample streaming does - not fit the 23-byte default), and enables notifications on `326A9006-…`. -- GATT operations are serialized by the Nordic client's internal per-connection - mutex — Android allows only one outstanding GATT op. -- Scans run **without** a service-UUID filter: MetaWear boards don't reliably - advertise the custom service UUID, so `MetaWearScanner` matches the - "MetaWear" name prefix instead. -- The library declares Bluetooth permissions but never requests them; apps - must be granted `BLUETOOTH_SCAN` + `BLUETOOTH_CONNECT` (API 31+) or the - legacy `BLUETOOTH`/`ACCESS_FINE_LOCATION` pair (API ≤ 30) first. - -## What's in `:metawear-persistence` - -Room-backed storage for downloaded log sessions -(`com.mbientlab.metawear.persistence`): - -- `SessionRecord`, `SampleRecord` — Room `@Entity` types with a cascade - foreign key (deleting a session deletes its samples). -- `PersistenceStore` — suspend methods over a Room DAO. -- `PersistenceDatabase` — create one per app. -- `SessionSnapshot` — immutable session summary. -- `Persistable` codec objects (`CartesianFloatPersistable`, …) — one singleton - codec per supported sample type. -- `PersistenceException` — the store's error taxonomy. - -All six supported sample types persist through one flat `(f0…f3, accuracy)` -record layout: `Float`, `Boolean`, `CartesianFloat`, `CorrectedCartesianFloat`, -`Quaternion`, `EulerAngles`. `Instant`s persist as epoch milliseconds; -sessions are identified by store-assigned UUID strings and devices by their -Android MAC identifier. `PersistenceStore.exportTable` rebuilds a `DataTable` -for CSV export straight from the database. - -Room's annotation processing runs through KSP — the standalone-versioned -KSP ≥ 2.3 line, which works with AGP 9's built-in Kotlin. **42 JVM unit -tests** cover the store logic (run against an in-memory fake of the DAO -interface), and an instrumented suite re-checks what the fake can only -mirror — the generated SQL's sort orders and counts, the foreign-key cascade, -and the epoch-millis converter — against a real Room database on-device. - -## What's in `:metawear-firmware` - -Nordic-DFU firmware updates (`com.mbientlab.metawear.firmware`) on top of the -[Nordic Android DFU Library](https://github.com/NordicSemiconductor/Android-DFU-Library): - -- `FirmwareServer`, `FirmwareFetcher` (+ `HttpUrlConnectionFetcher`) — HTTP - client for MbientLab's firmware release server. -- `FirmwareCatalog` — parser for the release-catalog JSON (hand-rolled, like - the BoardState codec). -- `FirmwareBuild` — value type describing one firmware artifact. -- `FirmwareException` — sealed error taxonomy with stable messages. -- `BootloaderInterlock` — pure flash-plan decision table. -- `MetaWearVersion.kt` — dotted-numeric version compare. -- `DFUProgress`, `DfuSession` — a cold Flow over the Nordic DFU library's - broadcasts. -- `MetaBootProbe` — one-shot GATT bootloader-version read. -- `FirmwareUpdate.kt` — `checkForFirmwareUpdate` / `updateFirmware` / - `updateFirmwareToLatest` extensions on `MetaWearDevice`. - -Usage: connect, then collect `device.updateFirmwareToLatest(context)` — a cold -`Flow` that walks catalog fetch → download → `[0xFE, 0x02]` -bootloader handoff (`sendExpectingDisconnect`) → Nordic DFU, emitting progress -the whole way; cancelling the collector aborts the transfer. An outdated -bootloader automatically becomes a multi-stage flash (bootloader chain first), -reported through `currentPart`/`totalParts`. When the flow completes, call -`connect()` again — the board rebooted and local device state is stale. - -Android specifics: -- The DFU transfer runs inside an Android service. The library declares - `MetaWearDfuService` (a `DfuBaseService` subclass) in its manifest, and - manifest merging carries the registration into your app — nothing to add - unless you subclass it for a foreground notification target, in which case - also create Nordic's notification channel via - `DfuServiceInitiator.createDfuNotificationChannel(context)` first. -- Firmware updates need the same `BLUETOOTH_CONNECT` runtime permission as - the rest of the SDK. -- The catalog JSON is parsed by a minimal hand-rolled parser (android.org.json - is stubbed out on the JVM unit-test classpath), keeping all **66 firmware - tests** — version compare, catalog selection, server/mock-fetcher, - bootloader interlock — plain JVM unit tests. The `DfuSession` / - `MetaBootProbe` wrappers are hardware-only and carry no unit-test coverage. - -## What's in `:app` - -A Jetpack Compose demo app, lean but feature-complete: - -- **Scan** — runtime BLE permission flow (`BLUETOOTH_SCAN`/`CONNECT` on 31+, - fine location on 26–30), nearby devices from the SDK scanner's StateFlows - (name, RSSI), remembered devices persisted by MAC. -- **Device hub** — connection state badge, model/firmware/battery summary, - identify (LED flash), reconnect/disconnect, feature navigation. -- **Live stream** — multi-sensor picker with a "Motion & Fusion" section - (accelerometer, gyroscope, magnetometer, and all seven sensor-fusion - outputs, with ODR/range chips and a 100 Hz BLE bandwidth advisor) and an - "Environmental" section: polled readables (temperature, humidity, polled - pressure — 1 s–5 m interval picker) plus streamed barometer pressure, - altitude, and ambient light with nominal-rate chips. Streamed sensors get a - dependency-free Canvas line chart, live readout, and true effective-Hz; - polled sensors get a latest-value tile (fed by `device.poll` one-shot - reads) plus the same chart; the quaternion output additionally renders a - live 3D orientation cube (a dependency-free Canvas wireframe with - orthographic projection and depth cueing) that is **tared**: it shows - rotation since a reference pose (auto-set from the first valid sample, - re-zeroed by the Zero button) through the IMU's 90° mounting correction — - the raw quaternion's absolute frame isn't stable session-to-session. While - a fusion output streams, a calibration badge polls the accuracy state every - 2 s and coaches per sensor; the bar is MEDIUM, since HIGH is a live score - the magnetometer legitimately loses indoors. - The charting hot path keeps sensor-rate work off the UI: samples ingest - into plain ring buffers on a background coroutine (full-resolution capture - + 1-in-N decimated display ring) and a ~33 ms ticker snapshots into Compose - state — nothing touches UI state at sensor rate. Stop archives each channel - to session history; buffers export as CSV (quaternion buffers gain derived - `heading,pitch,roll` columns matching the firmware's Euler convention). -- **Logging** — start/stop multi-sensor flash logging, elapsed clock, then a - single raw download drain with progress, per-sensor typed decode, and - persistence via `PersistenceStore`. Polled environmental sensors log - through the SDK's timer → event → logger chain (`PolledLogger`); streamed - barometer/ambient-light sessions decode to `Float`/lux samples — all export - through the same CSV path. Pending session records (including the - board-allocated polled-logger handles) are persisted, so they survive - process death: on the next download the SDK's `recoverLoggers` rebuilds the - chunk registry from the board's trigger table before decoding. -- **Group logging** — record the same sensors across a fleet of boards under - one shared group id: boards are armed sequentially (connect → clear → start - → verify entries actually land → disconnect), blink a gentle red recording - heartbeat that re-arms itself via on-board disconnect events (event ids - persisted for cleanup across app restarts), then stop + download the whole - batch with per-board progress. A board carrying a **foreign log** (someone - else's session) is detected on connect via a pure decision table — surfaced - for download/discard when decodable, cleared silently when it's undecodable - garbage, left alone when it's this app's own pending session. Foreign - downloads rebuild decoders from the board's own logger configuration - (anonymous signals) and label recovered sessions by signal identity. -- **Sessions** — history grouped by board identity (serial-keyed; titles - prefer the freshest stamped name, then the MAC; shared names get - disambiguated), swipe-to-delete with optimistic store delete, per-sample- - type chart styles recovered from the stored kind + capture-time label, - a 3D quaternion **replay** with play/pause/scrub timeline and 1×/2×/4× - speeds, and `epoch,elapsed_ms,…` CSV export (quaternion sessions include - derived Euler columns; filenames carry the capture-time board name and a - session-id discriminator) shared through the system sheet. -- **Controls** — LED color/pattern presets with play/stop, haptic motor - strength/pulse-width sliders, buzzer pulse. -- **Settings** — validated advertising rename, advertising interval/timeout, - TX power, a **Maintenance** section (LED reset, macro clear, event + timer - clear, restart-without-erase — each scoped so users don't reach for factory - reset), and a confirm-dialog factory reset. -- **Firmware** — catalog update check plus a Nordic-DFU update flow with - state/progress UI. -- **Demo mode** — `DemoBleTransport`, a protocol-level MetaMotion S emulator: - module discovery, device-info / battery / MAC / temperature / humidity / - pressure / illuminance / log reads, synthetic waveforms on every sensor - including packed registers, fusion outputs, altitude, and ambient light, - and full logging round trips — streamed, the polled timer/event chain, and - recovery across a simulated process restart. The identity fleet (up to 16 - distinguishable boards) backs a three-board simulated fleet in the app, so - group logging works end-to-end with no hardware. The scan screen offers - demo mode via a toggle (and suggests it when Bluetooth is off). The demo - pipeline — including full group-capture walks against the real persistence - store — is exercised end-to-end by JVM unit tests through the real - `MetaWearDevice`. - -The 3D orientation view is deliberately a Canvas wireframe cube: the -photoreal case model, button/LED window materials, and scene lighting of a -full 3D asset pipeline have no dependency-free equivalent, and only the -quaternion frame math is load-bearing. - -Install on a device or emulator: + group (fleet) logging, session history with CSV export, + LED/haptic controls, device settings, firmware updates, + and a hardware-free demo mode (protocol-level + MetaMotion S emulator). +``` + +The modules are intentionally split so an app can take just protocol + core without pulling in Room or the DFU library. `:metawear-protocol` is the foundation everything else builds on, written test-first to lock wire-format correctness before any BLE code. The full vertical slice — scan → connect → `startStream(accelerometer)` → `Flow>` — runs end-to-end against `MockBleTransport` on the JVM, and against real hardware via `:metawear-core`'s instrumented suites. + +The repo carries **1274 JVM tests** across the four testable modules (1042 protocol + 46 persistence + 66 firmware + 120 app), plus **43 BLE + 13 persistence instrumented tests** that need a phone (both hardware-verified on a Pixel 7 + MetaMotion S). + +--- + +## The MetaWear App + +`app/` is the MetaWear app — a Jetpack Compose app built on the four SDK modules (`:metawear-protocol` + `:metawear-core` for BLE + protocol, `:metawear-persistence` for storage and CSV, `:metawear-firmware` for DFU). It doubles as the reference consumer of the SDK: the public APIs an app needs are exercised here end to end. + +### What you can do + +| Area | What it does | +|------|--------------| +| **Scan & connect** | Runtime BLE permission flow, nearby MetaMotion boards with live name + RSSI from the scanner's `StateFlow`s, remembered devices persisted by MAC, reconnect | +| **Device hub** | Connection state badge, model / firmware / battery summary, identify (LED flash), reconnect / disconnect, feature navigation | +| **Live stream** | Multi-sensor picker — a "Motion & Fusion" section (accelerometer, gyroscope, magnetometer, and all seven sensor-fusion outputs, with ODR/range chips and a 100 Hz BLE bandwidth advisor) and an "Environmental" section (polled temperature / humidity / pressure with a 1 s–5 min interval picker, plus streamed barometer pressure, altitude, and ambient light). Canvas line charts, live readouts, true effective-Hz, a tared 3D orientation cube on the quaternion output, and a fusion calibration badge | +| **Logging & download** | Start / stop multi-sensor flash logging with an elapsed clock — the board keeps recording while disconnected — then reconnect and download with progress, per-sensor typed decode, and persistence. Pending sessions survive process death (`recoverLoggers` rebuilds the chunk registry) | +| **Group logging** | Record the same sensors across a fleet of boards under one shared group id, with a red recording heartbeat that re-arms itself via on-board disconnect events, then stop + download the whole batch with per-board progress. Foreign logs (another app's session) are detected on connect and offered for download via anonymous signals | +| **Session history** | Browse saved sessions grouped by board, re-plot them, replay quaternion sessions in 3D with a scrub timeline (1×/2×/4×), swipe-to-delete, and export any session to CSV through the system share sheet | +| **Controls** | LED color / pattern presets with play / stop, haptic motor strength and pulse-width sliders, buzzer pulse | +| **Settings** | Validated advertising rename, advertising interval / timeout, TX power, a Maintenance section (reset LED, clear macros, clear events + timers, restart-without-erase), and a confirm-dialog factory reset | +| **Firmware** | Catalog update check plus a Nordic-DFU update flow with state / progress UI | +| **Demo mode** | A toggle on the scan screen runs the entire app against a protocol-level MetaMotion S emulator — no hardware needed | + +### Running it + +1. Open the repo in Android Studio (JDK 21). The launch configuration is the **app** module. +2. **With hardware** — run on a physical Android phone (Android 8.0+, Android 12+ recommended) and connect a MetaMotion board over Bluetooth. Grant the Bluetooth permission prompts on first scan. +3. **Without hardware** — toggle **Demo mode** on the scan screen (the app also suggests it when Bluetooth is off). A "Simulated MetaWear" board appears and every screen works: synthetic live streams, a recordable / downloadable log session, battery / RSSI, and a three-board simulated fleet for group logging. See [Demo mode](#demo-mode). + +Or install from the command line: ```bash JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home" \ ./gradlew :app:installDebug ``` -## Build & test +### How it's built + +- **Jetpack Compose + Material 3**, single-activity (`MainActivity`) with Navigation Compose string routes (`"scan"`, `"group"`, `"device"`, `"info"`, `"stream"`, `"logging"`, `"sessions"`, `"controls"`, `"settings"`, `"firmware"`). No DI framework — `AppContainer` (owned by `MetaWearApplication`) is the process-wide root state: the shared `MetaWearScanner`, `PersistenceStore`, remembered-device store, log-session registry, demo-mode flag, and the `GroupCaptureCoordinator`. Each screen is driven by a focused ViewModel in `vm/`. +- **High-rate streaming pipeline** — samples ingest into plain ring buffers on a background coroutine (a 600-sample full-resolution capture ring plus a 180-sample 1-in-N decimated display ring, `Channel.kt` / `RingBuffer.kt`), and a ~33 ms ticker snapshots into Compose state (`StreamSessionViewModel`), so nothing touches UI state at sensor rate and charts stay smooth at 200 Hz. +- **3D orientation** — the quaternion output renders a dependency-free Canvas wireframe cube (`QuaternionCube`, `TaredQuaternionCube`) that is **tared**: it shows rotation since a reference pose (auto-set from the first valid sample, re-zeroed by the Zero button) through the IMU's 90° mounting correction, because the raw quaternion's absolute frame isn't stable session-to-session. While a fusion output streams, a calibration badge polls `SensorFusionCalibrationState` every 2 s; the bar is MEDIUM, since HIGH is a live score the magnetometer legitimately loses indoors. +- **Logging** — polled environmental sensors log through the SDK's timer → event → logger chain (`PolledLogger`); pending session records including the board-allocated polled-logger handles are persisted, so a fresh process can `recoverLoggers` and finish the download. Group capture arms boards sequentially (connect → clear → start → verify entries land → disconnect) and detects foreign logs with a pure decision table (`ForeignLog.kt`). +- **CSV export** — live buffers export as `time,…` (quaternion buffers gain derived `heading,pitch,roll` matching the firmware's Euler convention); persisted sessions export as `epoch,elapsed_ms,…` via `PersistenceStore.exportTable`. Filenames carry the capture-time board name and a session-id discriminator; sharing goes through a `FileProvider`. + +### Where the code lives + +| Path (`app/src/main/kotlin/com/mbientlab/metawear/app/`) | Contents | +|------|----------| +| `MetaWearApplication.kt`, `MainActivity.kt`, `AppContainer.kt` | Entry point, single activity, root app state | +| `core/` | Pure-Kotlin logic: `RingBuffer`, `EffectiveHz`, `BandwidthAdvisor`, `CalibrationReadiness`, `QuaternionCube`, `QuaternionFrame`, `ReplayTimeline`, `SensorSelection`, `SessionHistoryGrouping` | +| `data/` | Device-facing adapters: `ConfiguredSensor` (+ `openStream` / `startLoggingOn` / `decodeAndSave`), `LogDownloader`, `LogSessionRegistry`, `ForeignLog.kt`, `PolledPressure`, `RecordingHeartbeat`, `RememberedDeviceStore` | +| `demo/` | `DemoBleTransport` — the hardware-free MetaMotion S emulator | +| `export/` | `LiveBufferCsvExporter`, `ExportFilename`, `CsvShare` | +| `vm/` | ViewModels (`ScannerViewModel`, `DeviceViewModel`, `StreamSessionViewModel`, `LogSessionViewModel`, `DownloadViewModel`, `SessionHistoryViewModel`, `ControlsViewModel`, `SettingsViewModel`, `FirmwareUpdateViewModel`), `Channel`, `GroupCaptureCoordinator` | +| `ui/` | `AppNavHost`, one folder per screen (`scan`, `device`, `stream`, `logging`, `sessions`, `controls`, `settings`, `firmware`), shared `components/` (`LineChart`, `TaredQuaternionCube`, `FusionCalibrationBadge`, glass cards / badges), `theme/` | + +`app/src/test` holds 120 JVM tests, including end-to-end demo-pipeline walks through the real `MetaWearDevice` against `DemoBleTransport` and full group-capture runs against the real persistence store. + +--- + +## Development workflow + +### Repository layout + +| Path | Purpose | +|------|---------| +| `metawear-protocol/src/main/kotlin/com/mbientlab/metawear/` | `MetaWearDevice`, `DeviceLogging.kt`, `DeviceAnonymousSignals.kt`, `MetaWearScanner`; `protocol/` (module opcodes, packet builder/parser, capability interfaces, `ProtocolRouter`, `PolledLogger`); `model/` (value types, `BoardModel`, `BoardState`, `AnonymousSignal`, `DataTable`, exceptions); `sensor/` (one file per board module); `transport/` (`BleTransport`, `MockBleTransport`, `Uuids`) | +| `metawear-protocol/src/test` | JVM unit tests for parsing, commands, modules, device state, logging, and mock-transport behaviour | +| `metawear-core/src/main` | `NordicBleTransport`, `AndroidBleScanSource`, `AndroidMetaWear` | +| `metawear-core/src/androidTest` | Instrumented hardware suites (`HardwareSmokeTest` + 10 per-module suites, `HardwareSupport`) | +| `metawear-persistence/src/main` | Room entities, DAO, `PersistenceDatabase`, `PersistenceStore`, `Persistable` codecs | +| `metawear-persistence/src/test`, `src/androidTest` | JVM store tests (fake DAO) and instrumented real-database tests | +| `metawear-firmware/src/main` | `FirmwareServer`, `FirmwareCatalog`, `FirmwareBuild`, `BootloaderInterlock`, `DfuSession`, `MetaBootProbe`, `FirmwareUpdate.kt`, `MetaWearDfuService` | +| `metawear-firmware/src/test` | Catalog, version, server, interlock tests | +| `app/` | The Compose app (see above) | +| `HARDWARE.md` | Step-by-step hardware verification guide (phone setup, running the instrumented suites, manual app pass) | +| `.github/workflows/ci.yml` | CI: JDK 21, all JVM test tasks, `assembleDebug` for every module | + +### Common commands ```bash -./gradlew :metawear-protocol:test # parsing/command/transport tests (JVM) +./gradlew :metawear-protocol:test # parsing/command/transport/device tests (JVM) ./gradlew :metawear-core:assembleDebug # Android transport library (AAR) ./gradlew :metawear-persistence:testDebugUnitTest # persistence tests (JVM) -./gradlew :metawear-persistence:connectedAndroidTest # Room round-trips (needs a device) +./gradlew :metawear-persistence:connectedAndroidTest # Room round-trips (needs a phone) ./gradlew :metawear-firmware:testDebugUnitTest # firmware tests (JVM) ./gradlew :app:testDebugUnitTest # app logic + demo-emulator tests (JVM) ./gradlew :app:assembleDebug # Compose app APK +./gradlew :metawear-core:connectedAndroidTest # hardware suites (needs a phone + board) + +# Run one suite while developing a module. +./gradlew :metawear-protocol:test --tests '*LedTest*' +``` + +Opens directly in Android Studio; the Kotlin toolchain targets JDK 21. Set `JAVA_HOME` to a JDK 21 (Android Studio's bundled JBR: `/Applications/Android Studio.app/Contents/jbr/Contents/Home`). + +### Toolchain notes + +- `:metawear-core`, `:metawear-persistence`, `:metawear-firmware`, and `:app` use **AGP 9's built-in Kotlin** (no `org.jetbrains.kotlin.android` plugin — AGP 8.x does not run on this repo's Gradle 9.6). `:metawear-protocol` is a plain `kotlin("jvm")` module on Kotlin 2.1.20. +- `:app` adds the Compose compiler Gradle plugin (`org.jetbrains.kotlin.plugin.compose`) pinned to **2.2.10 — AGP 9.2.1's embedded Kotlin compiler version**, not the 2.1.20 used by the pure-JVM module's explicit Kotlin plugin. +- Room's annotation processing runs through **KSP** — the standalone-versioned KSP ≥ 2.3 line (2.3.9), which works with AGP 9's built-in Kotlin. +- Unit tests are JUnit 5 (Jupiter) with `kotlinx-coroutines-test` and Turbine; instrumented tests are JUnit 4 on `AndroidJUnitRunner`. + +### Documentation standards + +Public SDK types carry KDoc (`/** … */`) because they surface in Android Studio quick documentation and generated symbol docs. Implementation comments explain protocol quirks, firmware ordering constraints, or concurrency reasoning; avoid comments that merely restate a line of Kotlin. Markdown docs prefer small, runnable snippets and call out whether hardware is required. + +--- + +## Quick Start + +### Add the SDK to your build + +The SDK is **not yet published to Maven Central**. Until it is, consume it from source in one of two ways. + +**Option A — include the modules in your build.** Clone (or add as a git submodule) next to your project and include the modules you need in `settings.gradle.kts`: + +```kotlin +// settings.gradle.kts +include(":metawear-protocol", ":metawear-core", ":metawear-persistence", ":metawear-firmware") +project(":metawear-protocol").projectDir = file("../MetaWear-API-Kotlin/metawear-protocol") +project(":metawear-core").projectDir = file("../MetaWear-API-Kotlin/metawear-core") +project(":metawear-persistence").projectDir = file("../MetaWear-API-Kotlin/metawear-persistence") +project(":metawear-firmware").projectDir = file("../MetaWear-API-Kotlin/metawear-firmware") +``` + +```kotlin +// app/build.gradle.kts +dependencies { + implementation(project(":metawear-protocol")) + implementation(project(":metawear-core")) + implementation(project(":metawear-persistence")) // optional + implementation(project(":metawear-firmware")) // optional +} +``` + +**Option B — composite build.** Keep the SDK as an included build and substitute the planned coordinates: + +```kotlin +// settings.gradle.kts +includeBuild("../MetaWear-API-Kotlin") { + dependencySubstitution { + substitute(module("com.mbientlab:metawear-protocol")).using(project(":metawear-protocol")) + substitute(module("com.mbientlab:metawear-core")).using(project(":metawear-core")) + substitute(module("com.mbientlab:metawear-persistence")).using(project(":metawear-persistence")) + substitute(module("com.mbientlab:metawear-firmware")).using(project(":metawear-firmware")) + } +} +``` + +```kotlin +// app/build.gradle.kts — planned Maven coordinates (not yet published; version TBD) +dependencies { + implementation("com.mbientlab:metawear-protocol:") + implementation("com.mbientlab:metawear-core:") + implementation("com.mbientlab:metawear-persistence:") // optional + implementation("com.mbientlab:metawear-firmware:") // optional +} +``` + +The consuming project needs JDK 21, AGP 9.x, and `minSdk >= 26`. + +### Permissions + +The library declares the Bluetooth permissions in its manifest (merged into yours) but never requests them. Before calling `startScan()` or `connect()`, your app must have been **granted**: + +- **API 31+**: `BLUETOOTH_SCAN` and `BLUETOOTH_CONNECT` +- **API 26–30**: `BLUETOOTH` / `BLUETOOTH_ADMIN` and `ACCESS_FINE_LOCATION` + +```kotlin +val required = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + arrayOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT) +} else { + arrayOf(Manifest.permission.ACCESS_FINE_LOCATION) +} +// request via ActivityResultContracts.RequestMultiplePermissions() +``` + +Scanning with the permissions missing fails the scan flow; connecting without `BLUETOOTH_CONNECT` throws a `SecurityException` from the Android stack. + +### Scan and connect + +```kotlin +import com.mbientlab.metawear.core.AndroidMetaWear + +// One scanner per app — owns the shared scan source and vends one MetaWearDevice per board. +val scanner = AndroidMetaWear.scanner(applicationContext) +scanner.startScan() + +// Drive a Compose list from scanner.discoveredDevices (StateFlow>). +delay(5.seconds) +scanner.stopScan() + +val device = scanner.discoveredDevices.value.values.firstOrNull() ?: return +device.connect() // suspends until Idle: device info + module discovery done +println(device.deviceInfo?.model?.displayName) +``` + +### Stream the accelerometer + +```kotlin +import com.mbientlab.metawear.sensor.AccelerometerBmi160 + +val sensor = AccelerometerBmi160(AccelerometerBmi160.Odr.HZ100, AccelerometerBmi160.Range.G2) +val stream: Flow> = device.startStream(sensor) + +val job = launch { + stream.collect { sample -> + println("${sample.time} ${sample.value.x} ${sample.value.y} ${sample.value.z}") + } +} +delay(5.seconds) +device.stopStreaming(sensor) +job.cancel() +``` + +On a MetaMotion S use `AccelerometerBmi270(...)` — or let the SDK pick from the module table with `Accelerometer.make(...)` (see [Auto-select accelerometer](#auto-select-accelerometer)). + +### Control the LED + +```kotlin +import com.mbientlab.metawear.sensor.Led +import com.mbientlab.metawear.sensor.LedPattern + +// Set a pattern then play it +device.send(Led.SetPattern(Led.Color.GREEN, LedPattern.breathe)) +device.send(Led.Play()) + +// Stop and clear after 3 seconds +delay(3.seconds) +device.send(Led.Stop()) +``` + +### Buzz the haptic motor + +```kotlin +import com.mbientlab.metawear.sensor.Haptic + +device.send(Haptic.motor(dutyCycle = 80, pulseWidth = 500)) +``` + +--- + +## Architecture + +``` +┌──────────────────────────────────────────────────────┐ +│ Your App / Compose UI │ +│ (ViewModels collecting StateFlow / Flow) │ +├──────────────────────────────────────────────────────┤ +│ MetaWearScanner (StateFlow maps) │ +│ MetaWearDevice (StateFlow, Mutex) │ +├──────────────────────────────────────────────────────┤ +│ Module layer (sensor/*) │ +│ AccelerometerBmi160/270, Gyroscope…, Led, Timer, │ +│ Event, Macro, Gpio, DataProcessor, Settings, … │ +├──────────────────────────────────────────────────────┤ +│ ProtocolRouter (internal, per device) │ +│ Packet / PacketParser (static helpers) │ +├──────────────────────────────────────────────────────┤ +│ BleTransport (interface, pure JVM) │ +├────────────────────────┬─────────────────────────────┤ +│ AndroidBleScanSource │ NordicBleTransport │ +│ (shared scan) │ (per device, Nordic BLE) │ +├────────────────────────┴─────────────────────────────┤ +│ MockBleTransport (JVM unit tests) │ +│ DemoBleTransport (app demo mode, JVM tests) │ +│ connectedAndroidTest (hardware suites) │ +└──────────────────────────────────────────────────────┘ +``` + +`:metawear-protocol` holds everything above the platform line — including the `BleTransport` *interface* (it is pure JVM: `java.util.UUID`, `ByteArray`, `Flow`) — so the protocol router and device layer stay JVM-testable against `MockBleTransport`. Only the Nordic-backed implementation is Android-specific and belongs in `:metawear-core`. Peripherals are identified by their Android MAC address, so the transport seam uses an opaque `String` identifier. + +### Key design decisions + +| Concern | Choice | Why | +|---------------------|-----------------------------------------------------------|----------------------------------------------------------------------------------------------| +| Async streams | `kotlinx.coroutines.Flow` (cold) + `StateFlow` for state | Errors propagate on BLE drop; cancellation stops the stream; Compose collects natively | +| Thread safety | `MutableStateFlow.compareAndSet` for connection transitions + one `Mutex` (`opMutex`) serializing stream/log start-stop | Two sensors' command sequences can never interleave on the wire | +| BLE wrapper | Nordic Kotlin BLE Library behind the `BleTransport` seam | Coroutine-native GATT with per-connection serialization; the seam keeps the SDK testable | +| Scan admission | Unfiltered scan + `isMetaWearAdvertisement` (name prefix or service UUID) | Boards don't reliably advertise the service UUID; renamed boards still admitted | +| Time | `kotlinx.datetime.Instant` | Pure-JVM, no `java.time` dependency in the protocol module | +| Serialization | Hand-rolled JSON (`BoardState`, firmware catalog) | No serialization plugin or Android JSON dependency in JVM-tested code | +| Hardware tests | Instrumented (`connectedAndroidTest`) that **self-skip** without a board | Same task is safe on a bench without hardware | + +--- + +## Layer-by-layer breakdown + +### MetaWearScanner + +Exposes discovery state as `StateFlow` maps for Compose / ViewModel consumption. One scanner per app. The scan itself runs on the shared `AndroidBleScanSource`; each discovered peripheral gets its own `NordicBleTransport` and `MetaWearDevice` from the device factory wired by `AndroidMetaWear.scanner(context)`. + +```kotlin +class MetaWearScanner( + scanTransport: BleTransport, + scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default), + deviceFactory: (identifier: String) -> MetaWearDevice, +) { + val discoveredDevices: StateFlow> // MAC → device, admitted boards only + val advertisedNames: StateFlow> // most-recent local name per MAC + val advertisementRssi: StateFlow> // most-recent RSSI (dBm) per MAC + val isScanning: StateFlow + + fun startScan() + fun stopScan() + fun noteAdvertisedName(identifier: String, name: String) // seed the cache after a rename + fun clearAdvertisedName(identifier: String) // force next scan to recapture + fun deviceForKnownIdentifier(identifier: String): MetaWearDevice // remembered device, no re-discovery + + companion object { + fun isMetaWearAdvertisement(name: String, serviceUUIDs: List): Boolean + } +} +``` + +- Scans run **without** a service-UUID filter: MetaWear boards don't reliably include the custom service UUID in advertisements. Admission goes through `isMetaWearAdvertisement`, which accepts either the default `"MetaWear"` local-name prefix **or** the MetaWear service UUID when the packet carries it — so a board renamed to `"bob"` via `Settings.SetDeviceName` is still admitted. MetaBoot-mode boards (name `"MetaBoot"`, Nordic DFU service) match neither and stay excluded on purpose — the normal connect flow can't talk to a bootloader. +- `advertisedNames` and `advertisementRssi` are updated on every advertisement, **before** the admission filter, and only when the value actually changed (advertisements repeat several times a second per board; `StateFlow` subscribers shouldn't be invalidated at advertising rate). +- `deviceForKnownIdentifier` returns the same `MetaWearDevice` instance every time for one MAC, and if the scanner later sees that MAC on air the **same** instance is promoted into `discoveredDevices` — callers never observe two devices (two transports racing over one peripheral) for one identifier. +- After a rename, a connected board doesn't advertise, so the name cache is known-stale until disconnect; `noteAdvertisedName` lets UI update immediately and the next real advertisement reconciles it. + +### MetaWearDevice + +The main entry point for one board. All operations are `suspend` functions; state transitions are guarded by an atomic compare-and-set on the state flow (connection) and a `Mutex` serializing stream/log start-stop command sequences. Invalid transitions throw `MetaWearException.InvalidState`. + +``` +Disconnected → Connecting → Idle → Streaming + → Logging + → Downloading(progress) +``` + +```kotlin +sealed class DeviceState { + data object Disconnected : DeviceState() + data object Connecting : DeviceState() + data object Idle : DeviceState() + data object Streaming : DeviceState() + data object Logging : DeviceState() + data class Downloading(val progress: Double) : DeviceState() // 0.0..1.0 +} +``` + +Key members: + +```kotlin +class MetaWearDevice( + val identifier: String, // MAC address on Android + transport: BleTransport, + scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default), +) { + val state: StateFlow + var deviceInfo: DeviceInformation? // populated by connect() + var modules: Map // module-discovery table from connect() + var logReferenceDate: Instant? // wall-clock instant of device tick 0 + var onUnexpectedDisconnect: ((Throwable) -> Unit)? + + suspend fun connect() + suspend fun reconnect() + suspend fun disconnect() + suspend fun sendExpectingDisconnect(command: Command, timeout: Duration = 5.seconds) + suspend fun factoryReset() + suspend fun restart() + + suspend fun startStream(sensor: Streamable, usePacked: Boolean = true): Flow> + suspend fun stopStreaming(sensor: Streamable) + + suspend fun send(command: Command) + suspend fun send(sequence: CommandSequence) + + suspend fun read(readable: Readable): Timestamped + fun poll(readable: Pollable, every: Duration): Flow> + suspend fun readRSSI(): Int + + fun moduleInfo(module: Module): ModuleInfo? + val hasGyroscope: Boolean; val hasMagnetometer: Boolean + val hasBarometer: Boolean; val hasSensorFusion: Boolean + + fun captureBoardState(): BoardState? + fun restoreBoardState(state: BoardState) +} + +// Logging surface — extension functions in DeviceLogging.kt +suspend fun MetaWearDevice.startLogging(loggable: Loggable) +suspend fun MetaWearDevice.stopLogging(loggable: Loggable) +suspend fun MetaWearDevice.startLogging(logger: PolledLogger): PolledLoggerHandles +suspend fun MetaWearDevice.stopLogging(logger: PolledLogger, handles: PolledLoggerHandles) +suspend fun MetaWearDevice.startLogging(handle: ProcessorHandle, key: String) +suspend fun MetaWearDevice.stopLogging(key: String) +suspend fun MetaWearDevice.downloadLogs(expectEntries: Boolean = false): Flow>> +suspend fun MetaWearDevice.downloadLogs(loggable: Loggable): Flow>>> +suspend fun MetaWearDevice.downloadLogs(logger: PolledLogger): Flow>>> +suspend fun MetaWearDevice.downloadLogs(key: String, decode: (ByteArray) -> S): Flow>>> +suspend fun MetaWearDevice.clearLog() +suspend fun MetaWearDevice.flushLogPage(): Boolean +suspend fun MetaWearDevice.stopOnBoardLogging() +suspend fun MetaWearDevice.queryActiveLoggers(): List +suspend fun MetaWearDevice.queryActiveProcessors(): List +suspend fun MetaWearDevice.recoverLoggers(loggable: Loggable) +suspend fun MetaWearDevice.recoverLoggers(logger: PolledLogger) +suspend fun MetaWearDevice.createAnonymousDataSignals(): List // DeviceAnonymousSignals.kt +``` + +Notes on the lifecycle: + +- `connect()` atomically moves `Disconnected → Connecting`; a concurrent `connect()` fails fast. It connects the transport, starts the protocol router, reads the Device Information Service, discovers modules, and reads the log time reference; on any failure the link is torn down so the reported `Disconnected` is the truth. +- `disconnect()` is intentional: it finishes processor flows cleanly, stops the router, and suppresses `onUnexpectedDisconnect` for that drop. An unexpected drop moves the device to `Disconnected`, fails open streams with the underlying error, and invokes `onUnexpectedDisconnect` — but **preserves the logger registry**, so a reconnect can download without re-registering. +- `sendExpectingDisconnect(command)` is for commands that reboot the board or drop the link (`Debug.Reset()`, `Debug.JumpToBootloader()`, …): it swaps in a one-shot drop signal before sending, waits for the drop (or falls back to a local teardown after `timeout`), and converges on `Disconnected` without firing `onUnexpectedDisconnect`. +- `factoryReset()` issues the seven-write scrub sequence — stop logging, drop all log entries, remove all logger triggers, remove all events, remove all data processors, erase all macros, reset-after-GC — followed by an immediate `Debug.Reset()` fallback for firmware revisions (notably MMS 1.5.0) that ignore reset-after-GC when nothing is pending. `restart()` reboots **without** erasing anything: flash-resident data (log entries, macros) survives; volatile state (timers, events, sensor enables) is cleared. Reconnect after ~1 s in both cases. +- Streaming guards: the same `(module, register)` can't be started twice; **sensor fusion and the raw IMU sensors (accelerometer / gyroscope / magnetometer) are mutually exclusive**; all simultaneous fusion outputs must share one mode/range (a second output with a different config is rejected with `InvalidState` instead of being silently ignored by the board). If a start sequence fails mid-way the sensor is rolled back so a retry isn't blocked with "already streaming". + +### BLE transport split + +The shared scan source is separate from the per-peripheral connection transport: + +| Type | Module | Role | +|-------------------------|----------------------|--------------------------------------------------------------------------------------------------------------------------------------------| +| `BleTransport` | `:metawear-protocol` | The seam: `scan(services)`, `connect(identifier)`, `disconnect()`, `write(data, characteristic, type)`, `read(characteristic)`, `notifications(characteristic)`, `readRSSI()` | +| `AndroidBleScanSource` | `:metawear-core` | Unfiltered BLE scan → `Flow` (`identifier` = MAC, `name`, `rssi`, `manufacturerData`, `serviceUUIDs`). Scan-only; connection calls throw. | +| `NordicBleTransport` | `:metawear-core` | One per device. GATT connect / write / read / notify / RSSI on the Nordic Kotlin BLE Library. | +| `MockBleTransport` | `:metawear-protocol` | Public in-memory transport for unit tests. Inject notifications with `inject(notification, characteristic)`; inspect `writtenCommands`. No hardware required. | +| `DemoBleTransport` | `:app` | Protocol-level MetaMotion S emulator behind the same seam (see [Demo mode](#demo-mode)). | + +`NordicBleTransport` connect flow and transport notes: + +- GATT connect with `autoConnect = false` and a bounded retry (3 attempts, 500 ms apart, 20 s timeout each) that absorbs Android's transient status-133 (`GATT_ERROR`) failures. +- Full service discovery; every characteristic of every service is indexed by UUID, so Device Information Service reads (service `0x180A`, not the MetaWear service) resolve by characteristic UUID alone. +- MTU request for **247** bytes: the default 23-byte MTU leaves a 20-byte payload, too small for packed streaming (3 samples × 6 bytes + 2-byte header, and firmware past 1.4 pads further). +- Notifications enabled on the MetaWear notify characteristic (`326A9006-…`) up front, so the router can subscribe without an extra descriptor round-trip. +- GATT operations are serialized by the Nordic client's internal per-connection mutex — Android allows only one outstanding GATT operation — so the transport adds no locking of its own. +- Write-without-response for commands; write-with-response for macro recording. + +### ProtocolRouter + +Internal to each device. Routes raw BLE notification bytes to the right handler by `(module_id, register_id & 0x3F)` key: + +- Read responses (register `| 0x80` bit set) → resume the FIFO-queued suspended reader +- Unsolicited notifications → one-shot notify waiters (I2C/SPI reads, ADD/ENTRY acknowledgements) and ongoing subscription channels (streaming sensors) +- Multiple concurrent reads on the same key are queued and resolved FIFO +- Read timeout is 5 s (`ProtocolRouter.READ_TIMEOUT`) → `MetaWearException.Timeout`; slot-enumeration probes use a 1 s timeout because the firmware never answers an empty slot +- Per-key subscription buffers hold 256 packets and drop the **oldest** on overflow (sensor notifications arrive at up to ~200 packets/s; a stalled collector must not grow memory unbounded) +- All waiters and streams are failed when `stop()` is called or the transport's notification flow terminates (disconnect) +- Module discovery reads register `0x00` of every `Module` and records `implementation`, `revision`, and `extra` bytes into `ModuleInfo` (`isPresent` = implementation ≠ `0xFF`) + +Data-processor streaming uses a per-id demux on top of the router: one shared `(0x09, 0x03)` subscription fans packets out to per-processor-id flows, so multiple processors stream simultaneously; the flows complete cleanly on an intentional disconnect and fail with the underlying error on an unexpected one. + +--- + +## Sensor protocols + +Every sensor type is a plain Kotlin value that implements one of these interfaces (`com.mbientlab.metawear.protocol`): + +```kotlin +interface Sensor { + val module: Module + val dataRegister: Int + val packedDataRegister: Int? get() = null +} + +// Streams data continuously (accelerometer, gyro, magnetometer, sensor fusion, barometer, switch, GPIO) +interface Streamable : Sensor { + val configureCommands: List + val enableCommand: ByteArray + val startCommand: ByteArray + val stopCommand: ByteArray + val disableCommand: ByteArray + val warmupCommands: List get() = emptyList() // e.g. magnetometer power-on (+ warmupDelayNanos) + fun parseSample(packet: ByteArray): S + fun parsePackedSamples(packet: ByteArray): List = emptyList() +} + +// Streams AND logs to on-device flash +interface Loggable : Streamable { + val loggerKey: String + val logDataChunks: List get() = listOf(LogChunk(0, 4), LogChunk(4, 2)) + fun parseLogSample(data: ByteArray): S +} + +// One-shot read sensors (battery, MAC, humidity, log length, …) +interface Readable : Sensor { + val readCommand: ByteArray + fun parseSample(packet: ByteArray): S +} + +// A readable whose value changes over time — works with device.poll(readable, every) +interface Pollable : Readable + +// A readable that can be logged on-board via the timer → event → logger chain +interface PolledLoggable : Readable { + val logDataChunks: List + val loggerTriggerIndex: Int get() = 0xFF +} + +// Fire-and-forget commands (LED, haptic, debug, timer control, GPIO, macro) +interface Command { + val commandData: ByteArray +} + +// Fire-and-forget actions that require more than one BLE write +// (e.g. BMI270 feature enable/disable pairs, long scan-response splits) +interface CommandSequence { + val commands: List +} +``` + +`device.send(...)` is overloaded for both `Command` and `CommandSequence` — a single call site regardless of whether the action emits one or many writes. + +### Generic read and poll + +Any `Readable` works with `device.read(...)`; any `Pollable` also works with `device.poll(readable, every)`, which returns a cold `Flow`: + +```kotlin +val humidity = device.read(Humidity()) // Timestamped, % RH +val entries = device.read(LogLength()) // Timestamped +val mac = device.read(Settings.ReadMacAddress()) // Timestamped (alias: MacAddress) +val reset = device.read(LastResetTime()) // Timestamped — { epoch: Instant, resetUID: Int } +val celsius = device.read(Thermometer(channel = 0)) // Timestamped + +device.poll(Settings.ReadBatteryState(), every = 30.seconds).collect { sample -> + updateBatteryUI(sample.value.charge, sample.value.voltage) // BatteryState(voltage mV, charge %) +} +``` + +`poll` reads the sensor immediately, emits the timestamped sample, delays `every`, and repeats. Cancelling the collecting coroutine stops the loop; a read error terminates the flow. + +Built-in `Pollable` implementations: `LoggingEnabled`, `LogLength`, `LastResetTime`, `Settings.ReadBatteryState`, `Settings.ReadMacAddress`, `Settings.ReadPowerStatus`, `Settings.ReadChargeStatus`, `Humidity`, `BarometerPressureRead`, `Thermometer`, `SensorFusionCalibrationState`. + +--- + +## Supported sensors and modules + +All 22 board modules are covered. Each subsection names the Kotlin type, the module ID, and the configuration shape. + +### Accelerometer (module 0x03) + +```kotlin +// BMI160 (MetaMotion R / RL — model 5) +// Odr: HZ0_78, HZ1_56, HZ3_12, HZ6_25, HZ12_5, HZ25, HZ50, HZ100, HZ200, HZ400, HZ800, HZ1600 +// Range: G2, G4, G8, G16 +val acc = AccelerometerBmi160(odr = AccelerometerBmi160.Odr.HZ100, range = AccelerometerBmi160.Range.G2) + +// BMI270 (MetaMotion S — model 8) +// Same Odr / Range entries as BMI160 (different config bytes on the wire) +val acc = AccelerometerBmi270(odr = AccelerometerBmi270.Odr.HZ100, range = AccelerometerBmi270.Range.G2) +``` + +Both implement `Loggable` (`loggerKey = "acceleration"`, data register `0x04`). Packed data (3 samples per BLE packet) is used automatically when `usePacked = true` (default) — packed register `0x1C` on the BMI160, `0x05` on the BMI270. Scale factors (LSB/g): ±2 g = 16384, ±4 g = 8192, ±8 g = 4096, ±16 g = 2048. + +`Accelerometer` is the type-erased sealed wrapper (`Accelerometer.Bmi160(sensor)` / `Accelerometer.Bmi270(sensor)`) with `odrHz`, `rangeG`, `withOdr(...)`, `withRange(...)`, and the `Accelerometer.make(impl, odrHz, rangeG)` factory — see [Auto-select accelerometer](#auto-select-accelerometer). + +#### Step counter / detector + +```kotlin +// BMI160 — register 0x18 config, read on 0x1A, detector notifications on 0x19 +device.send(AccelerometerBmi160Steps.ConfigureStepCounter(AccelerometerBmi160Steps.StepCounterMode.NORMAL)) // NORMAL / SENSITIVE / ROBUST +device.send(AccelerometerBmi160Steps.EnableStepDetector()) +val steps = AccelerometerBmi160Steps.parseStepCount(packet) // from a ReadStepCounter response + +// BMI270 — watermark-triggered counter (CommandSequence), detector notifications on 0x0B +device.send(AccelerometerBmi270Steps.ConfigureStepCounter(trigger = 1)) +device.send(AccelerometerBmi270Steps.EnableStepDetector()) +val steps = AccelerometerBmi270Steps.parseStepCount(packet) +``` + +### Gyroscope (module 0x13) + +```kotlin +// BMI160 (MetaMotion R / RL — model 5) +// Odr: HZ25, HZ50, HZ100, HZ200, HZ400, HZ800, HZ1600, HZ3200 +// Range: DPS2000, DPS1000, DPS500, DPS250, DPS125 +val gyro = GyroscopeBmi160(odr = GyroscopeBmi160.Odr.HZ100, range = GyroscopeBmi160.Range.DPS2000) + +// BMI270 (MetaMotion S — model 8) +// Same options as BMI160 +val gyro = GyroscopeBmi270(odr = GyroscopeBmi270.Odr.HZ100, range = GyroscopeBmi270.Range.DPS2000) + +// BMI270 only: write axis offsets +device.send(GyroscopeBmi270.Offsets(x = 0, y = 0, z = 0)) +``` + +Both implement `Loggable` (`loggerKey = "angular-velocity"`). Data register is `0x05` on the BMI160 (packed `0x07`) and `0x04` on the BMI270 (packed `0x05`). `Gyroscope` is the sealed wrapper with `Gyroscope.make(impl, odrHz, rangeDps)` (impl `0` = BMI160, `1` = BMI270 from `moduleInfo(Module.GYRO)?.implementation`). + +### Bosch motion detectors (accelerometer interrupts) + +Orientation, any-motion, and tap interrupts are generated on-chip by the BMI160 / BMI270. `AccelerometerBosch` ships the configure / enable / disable commands and the packet decoders. + +```kotlin +// Orientation — fires when the device rotates through one of 8 states (notification register 0x11) +// BMI160-only; constructing with ChipVariant.BMI270 throws MetaWearException.OperationFailed. +device.send(AccelerometerBosch.EnableOrientation(chip = AccelerometerBosch.ChipVariant.BMI160)) +val orientation = AccelerometerBosch.parseOrientation(packet) // AccelerometerBosch.SensorOrientation.FACE_UP_PORTRAIT_UPRIGHT … + +// Any-motion — fires when motion exceeds threshold on any axis (config 0x0A, enable 0x09, notify 0x0B) +device.send(AccelerometerBosch.ConfigureAnyMotion( + chip = AccelerometerBosch.ChipVariant.BMI270, count = 4, thresholdG = 0.75f, rangeG = 8.0f, +)) +device.send(AccelerometerBosch.EnableAnyMotion()) +val event = AccelerometerBosch.parseAnyMotion(packet) // event.isPositive, event.xAxisActive / yAxisActive / zAxisActive + +// Tap — single + double tap (config 0x0D, enable 0x0C, notify 0x0E) +device.send(AccelerometerBosch.ConfigureTap( + shockTime = AccelerometerBosch.TapShockTime.MS50, + quietTime = AccelerometerBosch.TapQuietTime.MS30, + doubleTapWindow = AccelerometerBosch.DoubleTapWindow.MS250, + thresholdG = 2.0f, rangeG = 8.0f, +)) +device.send(AccelerometerBosch.EnableTap(single = true, double = true)) +val tap = AccelerometerBosch.parseTap(packet) // tap.type (SINGLE / DOUBLE), tap.isPositive +``` + +To receive the interrupt packets, wrap the notification register in a small `Streamable` and use `startStream` — the interface is public and `startStream` handles the subscribe write and the accelerometer start for you: + +```kotlin +class TapInterrupt : Streamable { + override val module = Module.ACCELEROMETER + override val dataRegister = 0x0E + override val configureCommands = listOf( + AccelerometerBosch.ConfigureTap(thresholdG = 2.0f, rangeG = 8.0f).commandData, + ) + override val enableCommand = AccelerometerBosch.EnableTap(single = true, double = true).commandData + override val startCommand = Packet.command(Module.ACCELEROMETER, 0x01, 0x01) // start sampling + override val stopCommand = Packet.command(Module.ACCELEROMETER, 0x01, 0x00) + override val disableCommand = AccelerometerBosch.DisableTap().commandData + override fun parseSample(packet: ByteArray) = AccelerometerBosch.parseTap(packet) +} + +device.startStream(TapInterrupt()).collect { tap -> println(tap.value.type) } +``` + +An interrupt stream shares the accelerometer module with the data stream, so the streaming guard treats it as the same module — run one or the other, or fold the interrupt into a data processor / event instead. + +### BMI270 extra features (activity, wrist, no-motion, significant motion, downsampling) + +BMI270-only features exposed via `AccelerometerBmi270Features`. `Configure…` and `SetDownsampling` are `Command`s; the `Enable…` / `Disable…` pairs (which emit both `FEATURE_INTERRUPT_ENABLE` and `FEATURE_ENABLE` writes) are `CommandSequence`s. Both ship through the same `device.send(...)` entry point. + +```kotlin +// Activity classification — STILL / WALKING / RUNNING / UNKNOWN (register 0x0C) +device.send(AccelerometerBmi270Features.EnableActivityDetection()) +val activity = AccelerometerBmi270Features.parseActivity(packet) // AccelerometerBmi270Features.Activity + +// Wrist gesture (register 0x0A) — PUSH_ARM_DOWN, PIVOT_UP, SHAKE, ARM_FLICK_IN, ARM_FLICK_OUT +device.send(AccelerometerBmi270Features.ConfigureWristGesture(arm = AccelerometerBmi270Features.WristArm.RIGHT)) +device.send(AccelerometerBmi270Features.EnableWristGesture()) +val event = AccelerometerBmi270Features.parseWristEvent(packet) // event.kind (WAKEUP / GESTURE), event.gestureCode + +// Wrist wakeup (register 0x0A) — shares the parse path with wrist gesture +device.send(AccelerometerBmi270Features.ConfigureWristWakeup()) +device.send(AccelerometerBmi270Features.EnableWristWakeup()) + +// No-motion (register 0x09) — distinct from any-motion +device.send(AccelerometerBmi270Features.ConfigureNoMotion( + duration = 5, threshold = 0xAA, selectX = true, selectY = true, selectZ = true, +)) +device.send(AccelerometerBmi270Features.EnableNoMotion()) + +// Significant motion +device.send(AccelerometerBmi270Features.ConfigureSignificantMotion(blocksize = 250)) +device.send(AccelerometerBmi270Features.EnableSignificantMotion()) + +// FIFO downsampling — reduce the logged sample rate per axis-group +device.send(AccelerometerBmi270Features.SetDownsampling( + gyroOrdinal = 2, gyroFilterData = true, + accOrdinal = 2, accFilterData = true, +)) +``` + +Every feature has a matching `Disable…` sequence (`DisableActivityDetection()`, `DisableWristGesture()`, `DisableWristWakeup()`, `DisableNoMotion()`, `DisableSignificantMotion()`). Notification registers are exposed as constants (`ACTIVITY_REGISTER`, `WRIST_EVENT_REGISTER`, `AccelerometerBmi270Steps.STEP_REGISTER`). + +### Magnetometer (BMM150, module 0x15) + +```kotlin +val mag = Magnetometer(preset = Magnetometer.Preset.LOW_POWER) +// Presets: LOW_POWER, REGULAR, ENHANCED_REGULAR, HIGH_ACCURACY +// Manual: Magnetometer(xyReps = 9, zReps = 15, odr = Magnetometer.Odr.HZ10) // Odr: HZ2 … HZ30 +// Scale: 16 LSB/µT; Loggable, loggerKey "magnetic-field" +// Data register 0x05, packed register 0x09 (module revision ≥ 1) + +device.send(Magnetometer.Suspend()) // low-power suspend +``` + +`startStream` sends the magnetometer's warm-up (power-mode) command and waits 200 ms before configuring — the `warmupCommands` / `warmupDelayNanos` hooks on `Streamable`. + +### Barometer (BMP280 / BME280, module 0x12) + +```kotlin +// BMP280 (motion boards) +val baro = Barometer( + oversampling = Barometer.Oversampling.STANDARD, // SKIP, ULTRA_LOW_POWER, LOW_POWER, STANDARD, HIGH, ULTRA_HIGH + iirFilter = Barometer.IirFilter.AVG4, // OFF, AVG2, AVG4, AVG8, AVG16 + standbyTime = Barometer.BmpStandbyTime.MS62_5, // MS0_5 … MS4000 +) +// BME280 (environmental boards) — standby indices 6/7 differ +val baroBme = Barometer(Barometer.Oversampling.STANDARD, Barometer.IirFilter.OFF, Barometer.BmeStandbyTime.MS10) + +device.startStream(baro).collect { p -> println("${p.value} Pa") } // Loggable, register 0x01, loggerKey "pressure" +device.startStream(Altimeter(barometerConfig = baro)).collect { m -> println("${m.value} m") } // Streamable, register 0x02 + +val pressure = device.read(BarometerPressureRead()) // one-shot Pollable (barometer must be running in cyclic mode) +``` + +`moduleInfo(Module.BAROMETER)?.implementation` reports the chip variant (`Barometer.Variant.BMP280` = 0, `BME280` = 1). + +### Ambient light (LTR329, module 0x14) + +```kotlin +val als = AmbientLight( + gain = AmbientLight.Gain.X1, // X1 X2 X4 X8 X48 X96 + integrationTime = AmbientLight.IntegrationTime.MS100, // MS50 … MS400 + measurementRate = AmbientLight.MeasurementRate.MS500, // MS50 … MS2000 +) +device.startStream(als).collect { sample -> + val lux = AmbientLight.lux(sample.value) // raw Long milli-lux → Float lux + println("ambient: $lux lx") +} +``` + +`AmbientLight` is `Loggable` (`loggerKey = "illuminance"`, register `0x03`); the app decodes logged sessions to lux the same way. + +### Humidity (BME280, module 0x16 — MetaEnvironment) + +```kotlin +// One-shot read +val percent = device.readHumidity() // Float, % RH +// or through the generic pipeline +val sample = device.read(Humidity()) // Timestamped + +// Configure oversampling once per session (X1 X2 X4 X8 X16) +device.setHumidityOversampling(Humidity.Oversampling.X4) +``` + +`Humidity` is both `Pollable` and `PolledLoggable` — it can be polled live or logged on-board through a `PolledLogger` (see [Logging polled readables](#logging-polled-readables)). + +### Sensor fusion (BMM150 + BMI160/270, module 0x19) + +```kotlin +SensorFusionQuaternion(mode = SensorFusionMode.NDOF) // → Quaternion (w, x, y, z) +SensorFusionEuler(mode = SensorFusionMode.NDOF) // → EulerAngles (heading, pitch, roll, yaw) +SensorFusionGravity(mode = SensorFusionMode.IMU_PLUS) // → CartesianFloat (g) +SensorFusionLinearAcceleration(mode = SensorFusionMode.NDOF) // → CartesianFloat (g, gravity removed) +SensorFusionCorrectedAcc() // → CorrectedCartesianFloat (g, + accuracy) +SensorFusionCorrectedGyro() // → CorrectedCartesianFloat (dps, + accuracy) +SensorFusionCorrectedMag() // → CorrectedCartesianFloat (µT, + accuracy) +// Modes: NDOF (9-DOF), IMU_PLUS (6-DOF), COMPASS, M4G (SLEEP disables the engine) +// Ranges: accRange = SensorFusionAccRange.G2 … G16, gyroRange = SensorFusionGyroRange.DPS2000 … DPS250 +``` + +All seven outputs are `Loggable` (logger keys `"quaternion"`, `"euler-angles"`, `"gravity"`, `"linear-acceleration"`, `"corrected-acceleration"`, `"corrected-angular-velocity"`, `"corrected-magnetic-field"`). Several can stream simultaneously — they share one on-board engine, so `startStream` only sends the config once and adds each output's enable bit; stopping one output while others run only clears that bit. + +The fusion module is fed by the underlying accelerometer + gyroscope (+ magnetometer for `NDOF` / `COMPASS` / `M4G`) on the same board, and `startStream` / `startLogging` configures and starts those sensors for you. The BMI160 and BMI270 encode their config bytes differently, so the fusion signal needs to know which chip is on the board. Pass it via `chip` (defaults to `SensorFusionChip.BMI160`): + +```kotlin +// Auto-detect from the gyro module's implementation byte +// (0 = BMI160 on MetaMotion R / RL, 1 = BMI270 on MetaMotion S). +val chip = device.moduleInfo(Module.GYRO)?.implementation + ?.let { SensorFusionChip.fromGyroImpl(it) } + ?: SensorFusionChip.BMI160 + +val q = SensorFusionQuaternion(mode = SensorFusionMode.NDOF, chip = chip) +device.startStream(q).collect { s -> println(s.value) } +``` + +If you skip the chip argument, the SDK assumes BMI160. On a MetaMotion S (BMI270) the underlying acc/gyro would receive the wrong config bytes silently — the fusion algorithm runs, but at the wrong ODR / range — so always pass the detected chip on those boards. + +Related commands: `SensorFusionClearEnabledMask()`, `SensorFusionResetOrientation()`. Calibration lives in `SensorFusionCalibration.kt` — see [Sensor fusion calibration](#sensor-fusion-calibration). + +### LED (module 0x02) + +```kotlin +// Single channel +device.send(Led.SetPattern(Led.Color.GREEN, LedPattern.blink)) +device.send(Led.Play()) +device.send(Led.Stop()) // stop + clear (default) +device.send(Led.Stop(clearPattern = false)) // stop only +device.send(Led.Pause()) // pause without clearing +device.send(Led.Autoplay()) // play now and auto-play future patterns + +// Built-in presets (LedPattern companion) +// LedPattern.solid — always on (lowIntensity == highIntensity, never dims) +// LedPattern.blink — 50 ms on / 450 ms off +// LedPattern.breathe — 725 ms rise / 500 ms high / 725 ms fall over a 2 s pulse +// LedPattern.flash — 3 short 100 ms pulses + +// Multi-channel shorthand — resets, sets patterns, and plays in one call +device.setLed( + red = LedPattern(highIntensity = 10, riseTime = 100, highTime = 200, + fallTime = 100, pulseDuration = 800, repeatCount = 0xFF), + green = LedPattern(highIntensity = 31, riseTime = 100, highTime = 300, + fallTime = 100, pulseDuration = 800, repeatCount = 0xFF), + autoPlay = true, +) +device.stopLed() +``` + +Colors: `GREEN` (0), `RED` (1), `BLUE` (2). Intensity 0–31. `repeatCount = 0xFF` = infinite; a raw `0` is undefined behaviour on the firmware, so the encoder rewrites it to `0xFF`. `LedPattern` also has a `delay` field (ms before the pattern starts) to phase-offset channels. + +### GPIO (module 0x05) + +```kotlin +// Digital output +device.send(Gpio.SetHigh(pin = 0)) +device.send(Gpio.SetLow(pin = 0)) +device.send(Gpio.SetPull(pin = 0, pull = Gpio.Pull.UP)) // UP / DOWN / NONE + +// One-shot reads +val state: Boolean = device.readDigital(pin = 0) +val adcCount: Int = device.readAnalogADC(pin = 0) // raw 10-bit ADC count (0–1023) +val voltage: Int = device.readAnalogAbsolute(pin = 0) // millivolts (0–3300) + +// Analog reads accept optional pull-up / pull-down / virtual-pin / delay parameters +device.readAnalogADC(pin = 0, parameters = Gpio.AnalogReadParameters(pullupPin = 1, delayMicroseconds = 100)) + +// Pin-change stream +val signal = GpioPinChange(pin = 0, changeType = Gpio.ChangeType.ANY) // RISING / FALLING / ANY +device.startStream(signal).collect { sample -> + println("pin ${sample.value.pin} → ${if (sample.value.isHigh) "high" else "low"}") // GpioSample +} +``` + +Command classes are also available directly: `Gpio.ConfigurePinChange(pin, type)`, `Gpio.StartPinMonitor(pin)`, `Gpio.StopPinMonitor(pin)`, `Gpio.DigitalRead(pin, silent)`, `Gpio.AnalogRead(mode, pin, silent, parameters)`. `GpioAnalogSignal(pin, mode)` exposes an analog pin as a data-processor source. + +### Switch / button (module 0x01) + +```kotlin +device.startStream(Switch()).collect { event -> + println(if (event.value) "pressed" else "released") // Streamable +} +``` + +### Haptic (module 0x08) + +```kotlin +device.send(Haptic.motor(dutyCycle = 80, pulseWidth = 500)) // ERM motor: duty 0–100 %, pulse ms +device.send(Haptic.buzzer(pulseWidth = 200)) // piezo buzzer +// Explicit form: Haptic.Pulse(mode = Haptic.Mode.MOTOR, dutyCycle = 100, pulseWidth = 500) +``` + +### Temperature (module 0x04) + +```kotlin +val celsius = device.read(Thermometer(channel = 0)).value +// Channel sources: NRF_DIE (0), EXT_THERMISTOR, BMP280, PRESET_THERMISTOR — layout varies by board +// (MetaWear R: [NRF_DIE, EXT_THERM]; RPro: [NRF_DIE, PRESET_THERM, EXT_THERM, BMP280]) + +// Configure an external thermistor's pin mapping +device.send(ThermometerConfigureExt(channel = 1, dataPin = 0, pulldownPin = 1, activeHigh = false)) +``` + +`Thermometer(channel, silent = false)` is `Pollable` and `PolledLoggable` (`loggerTriggerIndex = channel`), so each channel can be polled live or logged on-board. `TemperatureChannel` carries named constants (`NRF`, `EXTERNAL_THERMISTOR`, `BMP280`, `BOSCH`, …) and `ThermometerSource` the source enum. The module-discovery `extra` bytes for `Module.TEMPERATURE` list the source of each channel on the connected board. + +### Timer (module 0x0C) + +On-device periodic timer — fires completely independently of BLE once started. + +```kotlin +// Create and start a 500 ms repeating timer +val timer = device.createTimer(periodMs = 500) +device.setTimerNotify(timer, enabled = true) +device.startTimer(timer) + +// Stream tick notifications over BLE (Flow of the firing timer id) +device.streamTimer(timer).collect { timerId -> /* … */ } + +// Tear down +device.stopTimer(timer) +device.setTimerNotify(timer, enabled = false) +device.removeTimer(timer) +device.removeAllTimers() // broad-stroke cleanup of all 8 timer slots + +// Parameters +device.createTimer(periodMs = 1000, repetitions = MetaWearTimer.INFINITE, immediate = false) +// repetitions: 0xFFFF = MetaWearTimer.INFINITE; immediate = true fires at t=0 +``` + +### Event (module 0x0A) + +Bind a board signal (timer tick, button press, GPIO change, disconnect) to a command that executes on-board — no BLE connection required once configured. + +```kotlin +// Flash the green LED every time the timer fires — works even if BLE disconnects +val timer = device.createTimer(periodMs = 500) +device.send(Led.SetPattern(Led.Color.GREEN, LedPattern.blink)) +val event = device.createEvent( + source = EventSource.timerFired(timer), + action = EventAction.from(Led.Play()), +) +device.startTimer(timer) + +// Other event sources +EventSource.buttonChanged() // fires on every button state change +EventSource.gpioChanged(pin = 0) // fires on GPIO pin-change notification +EventSource.disconnected() // fires when the host drops the connection (settings rev ≥ 2) +EventSource(module = Module.TIMER, register = 0x06, dataId = 0xFF) // any (module, register, id) triple + +// Tear down +device.removeEvent(event) +device.removeAllEvents() +``` + +`EventAction(module, register, params)` describes the destination command; `EventAction.from(command)` splits any `Command`'s bytes into that shape. + +#### Source → destination data slicing (`EventDataToken`) + +Optional instruction appended to the ENTRY command that tells the firmware to copy `length` bytes starting at `sourceOffset` of the source signal's payload into the destination command's params starting at `destOffset` when the event fires. Without a token the destination params are written as-is. + +```kotlin +// Route 4 bytes from source offset 2 into destination offset 3 +val event = device.createEvent( + source = EventSource.timerFired(timer), + action = action, + dataToken = EventDataToken(length = 4, sourceOffset = 2, destOffset = 3), +) +// Constraints: length 1…7 (3 bits), sourceOffset 0…15 (4 bits), destOffset any byte +``` + +### Macro (module 0x0F) + +Record a sequence of commands into device flash. Execute manually or automatically on every power-on. + +```kotlin +// Record: set pattern + play (stored in flash) +val macro = device.recordMacro( + executeOnBoot = false, + commands = listOf( + Led.SetPattern(Led.Color.GREEN, LedPattern.blink), + Led.Play(), + ), +) + +// Execute manually +device.executeMacro(macro) + +// Or record a boot macro — runs automatically on every power-on +val bootMacro = device.recordMacro( + executeOnBoot = true, + commands = listOf(Led.SetPattern(Led.Color.BLUE, LedPattern.flash), Led.Play()), +) + +// Erase all macros +device.eraseAllMacros() +``` + +Commands longer than 13 bytes are split into ADD_PARTIAL + ADD_COMMAND packets automatically. Macro writes use write-with-response. + +#### Embedding `createEvent` in a macro + +Use the body-based overload when the macro needs to embed a multi-write action — `createEvent(...)` being the primary case. The `MacroRecorder` buffers each call's wire bytes and replays them under one BEGIN…END recording session, so the firmware re-creates the event binding every time the macro runs. + +```kotlin +// Bind button → green LED flash, persisted across reboots +val macro = device.recordMacro(executeOnBoot = true) { recorder -> + recorder.send(Led.SetPattern(Led.Color.GREEN, LedPattern.flash)) + recorder.createEvent( + source = EventSource.buttonChanged(), + action = EventAction.from(Led.Play()), + ) +} +``` + +`MacroRecorder` exposes `send(command: Command)`, `send(sequence: CommandSequence)`, `sendRaw(data: ByteArray)`, and `createEvent(source, action, dataToken)`. Embedded events do not return an `Event.id` — the firmware assigns a fresh ID at replay time. Use `removeAllEvents()` (or `eraseAllMacros()` to also clear persistence) for cleanup. + +### Serial passthrough — I2C / SPI (module 0x0D) + +Communicate with external sensors or ICs wired to the MetaWear's I2C or SPI bus. + +```kotlin +// I2C write — 0x00 to register 0x6B of the device at address 0x68 (e.g. wake an MPU-6050) +device.send(Serial.I2cWrite(deviceAddress = 0x68, registerAddress = 0x6B, data = byteArrayOf(0x00))) + +// I2C read — 1 byte from register 0x75 (WHO_AM_I) of the device at 0x68 +val bytes = device.i2cRead(deviceAddress = 0x68, registerAddress = 0x75, length = 1) +println("WHO_AM_I: " + bytes.joinToString { "0x%02X".format(it) }) + +// SPI — pin/mode/clock parameter block +val spi = Serial.SpiParameters( + slaveSelectPin = 10, clockPin = 0, mosiPin = 11, misoPin = 7, + mode = Serial.SpiMode.MODE3, // MODE0 (CPOL=0/CPHA=0) … MODE3 (CPOL=1/CPHA=1) + frequency = Serial.SpiClock.F1_MHZ, // F125_KHZ F250_KHZ F500_KHZ F1_MHZ F2_MHZ F4_MHZ F8_MHZ + lsbFirst = false, useNrfPins = false, +) + +// SPI write — send 0x9F (READ_ID) +device.send(Serial.SpiWrite(parameters = spi, data = byteArrayOf(0x9F.toByte()))) + +// SPI read — 3 bytes (e.g. flash JEDEC ID), optionally writing 0x9F first +val id = device.spiRead(parameters = spi, length = 3, writeData = byteArrayOf(0x9F.toByte())) +``` + +I2C writes carry at most `Serial.I2cWrite.MAX_PAYLOAD_LENGTH` (10) bytes. Reads reply with a plain notification (not a bit-7 read response), which the device awaits for you. + +### iBeacon (module 0x07) + +```kotlin +device.send(IBeacon.SetUuid(UUID.randomUUID())) +device.send(IBeacon.SetMajor(1)) +device.send(IBeacon.SetMinor(2)) +device.send(IBeacon.SetRxPower(-55)) +device.send(IBeacon.SetTxPower(-4)) +device.send(IBeacon.SetPeriod(periodMs = 700)) +device.send(IBeacon.Enable()) +// ... +device.send(IBeacon.Disable()) +``` + +### Data processor (module 0x09) + +The data processor lets you chain on-device signal transforms so the board filters and reduces data before it ever reaches your app over BLE. + +#### Create a processor + +```kotlin +// RSS of raw accelerometer — reduces 3-axis to a scalar magnitude +val rssHandle = device.createProcessor(DataProcessor.Rss(), source = AccelerometerSignal()) + +// Average the RSS output over a 4-sample window +val avgHandle = device.createProcessor(DataProcessor.Average(sampleSize = 4), source = rssHandle) + +// Threshold — emit when the average crosses 0.5 g (= 8192 at the ±2 g 16384 LSB/g scale) +val threshHandle = device.createProcessor( + DataProcessor.Threshold(boundary = 8192, hysteresis = 0, + mode = DataProcessor.Threshold.Mode.BINARY, signed = false), + source = avgHandle, +) +``` + +The processor chain only produces output while its root sensor is sampling. Start the source either by streaming it (`startStream(acc)`) or — when you don't want the raw notifications on the air — by sending the sensor's configure / enable / start commands yourself through a tiny `Command` wrapper (this is what the app does for its prepare / teardown steps): + +```kotlin +class RawCommand(override val commandData: ByteArray) : Command + +val acc = AccelerometerBmi270(AccelerometerBmi270.Odr.HZ50, AccelerometerBmi270.Range.G2) +for (cmd in acc.configureCommands + acc.enableCommands + acc.startCommands) device.send(RawCommand(cmd)) +// … later … +for (cmd in acc.stopCommands + acc.disableCommands) device.send(RawCommand(cmd)) +``` + +#### Stream a processor's output + +```kotlin +device.streamProcessor(threshHandle).collect { packet -> + // packet = [0x09, 0x03, proc_id, data_bytes...] + val value = PacketParser.parseInt32LE(packet, 3) // BINARY threshold outputs an Int32 ±1 + println("threshold crossed: " + if (value > 0) "above" else "below") +} ``` -Opens directly in Android Studio; the Kotlin toolchain targets JDK 21. -`:metawear-core` uses AGP 9's built-in Kotlin (no `org.jetbrains.kotlin.android` -plugin — AGP 8.x does not run on this repo's Gradle 9.6). `:app` adds the -Compose compiler Gradle plugin (`org.jetbrains.kotlin.plugin.compose`) pinned -to **2.2.10 — AGP 9.2.1's embedded Kotlin compiler version**, not the 2.1.20 -used by the pure-JVM module's explicit Kotlin plugin. +#### Stop and remove + +```kotlin +device.stopStreamingProcessor(threshHandle) +device.removeProcessor(threshHandle) + +// Remove everything: +device.removeAllProcessors() +``` + +#### Available processor types + +| Type | Class | Output | +|---------------|-----------------------------|-----------------------------------------------------------| +| Passthrough | `DataProcessor.Passthrough` | Gate — `Mode.ALL` / `CONDITIONAL` / `COUNT` | +| Accumulator | `DataProcessor.Accumulator` | Running sum | +| Counter | `DataProcessor.Counter` | Event count | +| Average (LPF) | `DataProcessor.Average` | Rolling average | +| RMS combiner | `DataProcessor.Rms` | Scalar magnitude (root-mean-square) | +| RSS combiner | `DataProcessor.Rss` | Scalar magnitude (root-sum-square) | +| Time delay | `DataProcessor.Time` | Rate-limited samples (`Mode.ABSOLUTE` or `DIFFERENTIAL`) | +| Math | `DataProcessor.Math` | Arithmetic transform (`ADD MULTIPLY DIVIDE MODULO EXPONENT SQRT LSHIFT RSHIFT SUBTRACT ABS CONSTANT`) | +| Sample delay | `DataProcessor.Sample` | Burst of N buffered samples | +| Comparator | `DataProcessor.Comparator` | Filter by compare against a reference (`EQ NEQ LT LTE GT GTE`) | +| Threshold | `DataProcessor.Threshold` | Crossing events (`Mode.ABSOLUTE` or `BINARY` ±1) | +| Delta | `DataProcessor.Delta` | Emit when input changes by ≥ magnitude (`ABSOLUTE` / `DIFFERENTIAL` / `BINARY`) | +| Pulse | `DataProcessor.Pulse` | Detect pulses → `Output.WIDTH` / `AREA` / `PEAK` / `ON_DETECT` | +| Buffer | `DataProcessor.Buffer` | Hold last sample (read on demand or fused) | +| Packer | `DataProcessor.Packer` | Pack N samples per BLE packet | +| Accounter | `DataProcessor.Accounter` | Prepend timestamp (`Mode.TIME`) or packet counter (`COUNT`) | +| Fuser | `DataProcessor.Fuser` | Combine latest primary + up to 12 buffered secondaries | + +Chaining — `ProcessorHandle` implements `Signal`, so any processor's output can feed directly into the next `createProcessor` call. The handle carries the board-assigned `id` plus the output `nChannels`, `channelSize`, and `isSigned` so the next stage's config bytes are computed correctly without any manual bookkeeping. Sensor sources ship as `Signal` values: `SwitchSignal`, `AccelerometerSignal`, `GyroscopeSignal`, `GpioAnalogSignal(pin, mode)`, `TemperatureSignal(channel)`, `SensorFusionQuaternionSignal`, `SensorFusionEulerSignal`, `SensorFusionGravitySignal`, `SensorFusionLinearAccelerationSignal`. + +#### Recipes + +Common processor chains. Each recipe lists how many on-device processor slots it consumes (the board has a fixed pool — typically 28). + +**Fire on every Nth event** — count, take mod N, compare. Pair two comparators sharing the modulo output to split a stream into N classes (e.g. odd/even). + +```kotlin +// Bind switch presses +val pressed = device.createProcessor( + DataProcessor.Comparator(DataProcessor.Comparator.Operation.EQ, reference = 1, signed = false), + source = SwitchSignal()) // slot 1 + +// Counter → Math(% N) → two Comparators +val counter = device.createProcessor(DataProcessor.Counter(outputSize = 1), source = pressed) // slot 2 +val modN = device.createProcessor( + DataProcessor.Math(DataProcessor.Math.Operation.MODULO, rhs = 2, signed = false, outputSize = 1), + source = counter) // slot 3 +val isEven = device.createProcessor( + DataProcessor.Comparator(DataProcessor.Comparator.Operation.EQ, reference = 0, signed = false), + source = modN) // slot 4 +val isOdd = device.createProcessor( + DataProcessor.Comparator(DataProcessor.Comparator.Operation.EQ, reference = 1, signed = false), + source = modN) // slot 5 + +// isEven / isOdd now act as event sources: +device.createEvent( + source = EventSource(Module.DATA_PROCESSOR, register = 0x03, dataId = isEven.id), + action = EventAction.from(Led.Play()), +) +``` + +Slot cost: 5 (or 4 if you only need one of odd/even, or 3 if you don't need the press-edge filter and the source already gives you a single-sample-per-event signal). + +**Activity gate (magnitude crosses threshold)** — reduce 3-axis to scalar, then threshold or compare. Useful for activity / freefall / impact detection without streaming raw axes. + +```kotlin +val mag = device.createProcessor(DataProcessor.Rss(), source = AccelerometerSignal()) // slot 1 +val active = device.createProcessor( + DataProcessor.Threshold(boundary = 8192, // 0.5 g at ±2 g range + hysteresis = 0, + mode = DataProcessor.Threshold.Mode.BINARY, + signed = false), + source = mag) // slot 2 +// `active` emits +1 on rising edge, –1 on falling edge. +``` + +Slot cost: 2. Bind `active` as an event source to drive an LED, or feed it into `startLogging(handle, key)` (see [Logging a processor handle](#logging-a-processor-handle)) to record activity transitions to flash. + +**Throttle a high-rate signal before logging** — `DataProcessor.Time(periodMs = 1000, mode = Mode.ABSOLUTE)` on `SensorFusionEulerSignal()` takes one sample per period; log its handle with `startLogging(throttle, key = "euler-1hz")`. The full walk-through is in [Logging a processor handle](#logging-a-processor-handle). + +Slot cost: 1. `Mode.DIFFERENTIAL` outputs the *delta* between successive periods instead of a raw sample — handy for derivative-style telemetry. + +> Cleanup: chains hold each processor's slot until you call `device.removeProcessor(handle)` (or `removeAllProcessors()`). Events bound to a processor must be torn down first (`removeAllEvents()`) — events reference processors, processors reference each other, and the firmware will reject a remove that has live downstream consumers. + +### Settings (module 0x11) + +```kotlin +device.send(Settings.SetDeviceName("MySensor")) // max 26 ASCII bytes (truncates) +device.send(Settings.SetDeviceName.validating("MySensor")) // throws OperationFailed on invalid chars / length +Settings.isNameValid("MySensor") // pre-check: [A-Za-z0-9_- ], ≤ 26 + +device.send(Settings.SetTxPower(Settings.TxPower.MINUS_4)) // PLUS_4, ZERO, MINUS_4 … MINUS_40 +device.send(Settings.SetAdvertisingInterval(intervalMs = 417, timeoutSec = 0)) // 20–10240 ms; 0 = advertise forever +device.send(Settings.StartAdvertising()) +device.send(Settings.SetConnectionParameters.lowLatency) // 7.5 ms interval — high-rate streaming +device.send(Settings.SetConnectionParameters.balanced) // 30 ms +device.send(Settings.SetConnectionParameters.powerSaving) // longer interval, latency 4 +device.setScanResponse(byteArrayOf(/* raw scan-response payload */)) // long payloads split into a CommandSequence + +// Reads (all Pollable) +val battery = device.read(Settings.ReadBatteryState()).value // BatteryState(voltage, charge) +val power = device.read(Settings.ReadPowerStatus()).value // Int +val charge = device.read(Settings.ReadChargeStatus()).value // Int + +// Whitelist / radio extras (settings revision permitting) +device.send(Settings.SetWhitelistFilterMode(Settings.WhitelistFilterMode.SCAN_AND_CONNECTION_REQUESTS)) +device.send(Settings.AddWhitelistAddress(index = 0, address = Settings.BluetoothAddress.parse("AA:BB:CC:DD:EE:FF"))) +device.send(Settings.SetThreeVoltPower(enabled = true)) +device.send(Settings.SetForce1MPhy(enabled = true)) +``` + +**Verifying a device-name change.** The firmware exposes `SetDeviceName` (register 0x11/0x01) as a write-only opcode — there is no protocol read-back — and a connected board doesn't advertise. To confirm a rename took effect you must disconnect and observe the next advertisement: + +```kotlin +device.send(Settings.SetDeviceName("MySensor")) +device.disconnect() +delay(500.milliseconds) // let the radio resume advertising + +scanner.clearAdvertisedName(device.identifier) // discard pre-rename cache +scanner.startScan() +// observe scanner.advertisedNames.value[device.identifier] … +``` + +Because admission also matches the MetaWear service UUID, a renamed board keeps appearing in `discoveredDevices` — you don't need to keep the `"MetaWear"` prefix. + +### Debug (module 0xFE) + +```kotlin +device.sendExpectingDisconnect(Debug.Reset()) // soft reset (BLE drops) +device.sendExpectingDisconnect(Debug.JumpToBootloader()) // DFU mode +device.sendExpectingDisconnect(Debug.Disconnect()) // board-initiated disconnect +device.send(Debug.ResetAfterGc()) // reset after macro/log garbage collection +device.send(Debug.EnablePowerSave()) // low-power sleep +device.send(Debug.SetStackOverflowAssertion(enable = true)) +val overflow = device.read(Debug.ReadStackOverflowState()).value // OverflowState(length, assertEnabled) +val queues = device.read(Debug.ReadScheduleQueueUsage()).value // List +device.send(Debug.SpoofButtonEvent(value = 1)) // fake a button press on the board +``` + +Use `sendExpectingDisconnect` for the commands that drop the link so the device converges on `Disconnected` without firing `onUnexpectedDisconnect`. + +--- + +## Logging + +### Start / stop / download + +```kotlin +val sensor = AccelerometerBmi270(AccelerometerBmi270.Odr.HZ50, AccelerometerBmi270.Range.G2) + +device.startLogging(sensor) +// ... time passes, board logs to flash at up to 800 Hz, with or without a BLE link ... +device.stopLogging(sensor) + +// Typed download — cumulative progress + decoded samples +device.downloadLogs(sensor).collect { progress -> + println("${(progress.percentComplete * 100).toInt()}% ${progress.data.size} samples so far") +} + +device.clearLog() +``` + +- `startLogging` can be called once per sensor while the device is already `Logging` to stack multiple distinct sensors into one session; registering the same `loggerKey` twice is rejected (it would orphan a logger ID on flash and clobber the registry). +- `stopLogging` doesn't require `Logging` state (the first stop in a multi-sensor session already moved the device to `Idle`); it flushes the in-RAM page while the module is still live, stops the logging module, and sends the sensor's stop/disable commands. Logger IDs stay in the local registry so `downloadLogs` can decode afterwards. +- `downloadLogs()` (raw) moves the device to `Downloading(progress)`, force-flushes the active page, reads and settles `LOG_LENGTH`, and streams cumulative `Download>` snapshots (`data`, `percentComplete`, `totalEntries`, `entriesDownloaded`). A 60 s inactivity watchdog aborts with `MetaWearException.Timeout` if the firmware stops sending. Pass `expectEntries = true` when you *know* the board logged (local records exist) — a zero `LOG_LENGTH` then triggers a longer flush-settle wait instead of an instant empty download. +- `downloadLogs(loggable)` maps the raw flow through the registry into `Download>>`; `LoggedSample` carries `date` (wall clock), `tickMs` (ms since reset), and `value`. +- `clearLog()` drops all entries and, on MMS (logging revision ≥ 3), waits up to 60 s for the firmware's Drop-Entries completion notice so the next session doesn't arm loggers on a board still grinding through NAND garbage collection. `stopOnBoardLogging()` just stops the logging module. + +### Flushing the last log page (MMS only) + +MetaMotion S boards (logging revision ≥ 3) buffer the final partial flash page in RAM. Both `stopLogging` and `downloadLogs` call `flushLogPage()` for you, so a short session (a few seconds at low ODR) is never stranded; call it yourself if you stop the module manually. On older boards the call is a no-op and returns `false`. + +```kotlin +device.stopLogging(sensor) +val flushed = device.flushLogPage() // true on MMS, false elsewhere +``` + +### CSV export + +Any list of logged or streamed samples can be converted to a `DataTable` and exported as CSV. `DataConvertible` knows the column layout for `CartesianFloat`, `Quaternion`, `EulerAngles`, `CorrectedCartesianFloat`, `Float`, and `Boolean` (convert raw `Long` illuminance to lux `Float` first); quaternion tables gain derived `heading,pitch,roll` columns (`Quaternion.derivedEulerAngles`) matching the firmware's Euler convention. + +```kotlin +// From logged samples — columns: epoch,elapsed_ms,x,y,z +val table = DataTable.fromLogged(entries, name = "acceleration") +println(table.csvString) +table.writeCsv(File(context.cacheDir, "accel.csv")) + +// From streamed samples — columns: epoch,x,y,z +val streamed = mutableListOf>() +// ... fill from stream ... +val table = DataTable.fromStreamed(streamed, name = "acceleration") +``` + +### Sensor fusion calibration + +```kotlin +// Read calibration while sensor fusion is running (0 = unreliable … 3 = high) +val cal = device.read(SensorFusionCalibrationState()).value +println("Accel: ${cal.accelerometer} Gyro: ${cal.gyroscope} Mag: ${cal.magnetometer}") + +// Or poll it while coaching the user through the calibration motions +device.poll(SensorFusionCalibrationState(), every = 2.seconds).collect { /* … */ } + +// Persist / restore the 10-byte per-sensor calibration blobs +device.send(SensorFusionWriteAccCalibration(data.acc)) +device.send(SensorFusionWriteGyroCalibration(data.gyro)) +device.send(SensorFusionWriteMagCalibration(data.mag)) +``` + +`SensorFusionCalibrationState` needs sensor fusion revision ≥ 1. `SensorFusionCalibrationData(acc, gyro, mag)` holds the blobs. + +### Auto-select accelerometer + +```kotlin +// Picks BMI160 or BMI270 from the module table read during connect(), snapping ODR / range +// to the nearest supported value (impl 1 = BMI160, 4 = BMI270). +val impl = device.moduleInfo(Module.ACCELEROMETER)?.implementation ?: -1 +val acc = Accelerometer.make(impl, odrHz = 100.0, rangeG = 2f) // Accelerometer? (null on unknown chip) +if (acc != null) { + device.startStream(acc) // or device.startLogging(acc) +} + +// Same for the gyroscope (impl 0 = BMI160, 1 = BMI270) +val gyro = Gyroscope.make(device.moduleInfo(Module.GYRO)?.implementation ?: -1, odrHz = 100.0, rangeDps = 2000f) +``` + +### Log time anchor + +During `connect()`, the SDK reads the board's current log tick (`[0x0B, 0x84]`) and converts it to a wall-clock `Instant` stored in `device.logReferenceDate`. Downloaded `LoggedSample` values carry both a `date` (wall clock, derived from that anchor when available) and a `tickMs` (ms since device reset). `RawLogEntry.epochMs` exposes the same tick → ms conversion for raw entries. + +### Logger registry across reconnects + +Logger subscriptions survive an unexpected BLE disconnect: the board keeps the same logger IDs, and the SDK intentionally preserves `loggerRegistry` on an unexpected drop so a `reconnect()` can download without re-registering. If the *process* restarted (registry lost) but the board is still holding entries, rebuild it from the board's trigger table: + +```kotlin +device.connect() +device.recoverLoggers(sensor) // matches queryActiveLoggers() by module + register +device.downloadLogs(sensor).collect { /* … */ } +``` + +`queryActiveLoggers()` / `queryActiveProcessors()` enumerate the board's logger and processor slots (`ActiveLogger`, `ActiveProcessor`) with a 1 s per-probe timeout — the firmware never answers an empty slot, so every enumeration ends with one timed-out probe. Both take a pre-fetched enumeration overload (`recoverLoggers(sensor, active)`) when you're recovering several sensors at once. + +### Logging polled readables + +Read-only sensors (temperature, humidity, one-shot pressure) can't be streamed, but they can be logged on-board through a **timer → event → logger** chain that the SDK builds for you: the board fires the read every `periodMs` and writes each response to flash with no host involvement — it keeps working across disconnects and app close. + +```kotlin +val logger = PolledLogger(readable = Thermometer(channel = 0), periodMs = 1_000) + +val handles: PolledLoggerHandles = device.startLogging(logger) // timerID, eventID, loggerIDs — persist these! +// ... time passes ... +device.stopLogging(logger, handles) // stop + remove timer, remove event, stop logging +device.downloadLogs(logger).collect { progress -> /* LoggedSample */ } +device.clearLog() + +// After an app restart with the board still logging: +device.recoverLoggers(logger) // rebuilds the registry from queryActiveLoggers() +``` + +Anything implementing `PolledLoggable` works: `Thermometer`, `Humidity`, or your own adapter over a one-shot read register (the app's `PolledPressure` does this for barometer pressure). + +### Board state capture / restore -### Hardware smoke tests +After a full `connect()` handshake you can persist the discovered device information, module table, and log time anchor, and pre-populate a fresh `MetaWearDevice` with them before it connects — so an app can render model / firmware / module details for a remembered board while it is still offline. -`:metawear-core` ships an instrumented smoke suite that exercises a real -MetaMotion S (BMI270) end-to-end: scan → connect (device info + module -discovery) → battery read → LED pattern → 2 s accelerometer stream (~1 g at -rest) → disconnect. +```kotlin +// After connect(), snapshot current state +val state = device.captureBoardState() ?: return +val json: String = state.encode() // JSON text +// …persist `json` to DataStore / SharedPreferences / Room / a file… -1. Connect an Android phone with USB debugging enabled (API 31+). +// Next session, before connect(): +val restored = BoardState.decode(json) +device.restoreBoardState(restored) // throws OperationFailed if not disconnected +// device.deviceInfo / device.modules / device.logReferenceDate are now populated offline + +device.connect() // refreshes them from the live board +if (!restored.isCompatible(device.deviceInfo!!)) { // firmware or hardware revision changed + // invalidate anything you derived from the cached state +} +``` + +`BoardState` is a value type with `equals`/`hashCode`; its JSON schema is versioned (`BoardState.CURRENT_SCHEMA_VERSION`) so caches written by a newer SDK are rejected safely (`decode` throws `OperationFailed` when the stored schema is newer than the SDK supports). `restoreBoardState` performs no compatibility validation of its own — that's what `isCompatible(liveInfo)` is for. + +### Anonymous signals (recover loggers the SDK didn't configure) + +If the board still holds active loggers and data processors from a prior session — or from another SDK — call `createAnonymousDataSignals()` to reconstruct `List` with canonical identifiers and typed decode closures, then download as normal. + +```kotlin +device.connect() +val signals = device.createAnonymousDataSignals() +for (sig in signals) { + println("${sig.identifier} → loggers ${sig.loggerIDs} root ${sig.rootModule}") +} + +// Each AnonymousSignal exposes: +// .identifier — canonical name (e.g. "acceleration", "angular-velocity:rms?id=0") +// .rootModule — underlying Module the chain reads from +// .chunks — List; .loggerIDs = chunks.map { it.id } +// .decode(ByteArray) -> List +// Output = Cartesian | Scalar | Quaternion | Euler | CorrectedCartesian +``` + +Wiring downloaded log entries into a signal's decode closure means grouping `RawLogEntry.rawData` bytes by `loggerIDs` and feeding each reassembled payload to `decode`. The app's `AnonymousSignalDecoder` (`data/ForeignLog.kt`) does exactly this to download foreign logs. + +Scale factors (accel / gyro range) are read from the live board at call time (`[0x03, 0x83]`, `[0x13, 0x83]`) — if you change range afterward, call `createAnonymousDataSignals()` again. + +### Logging a processor handle + +The typed `startLogging(loggable)` overload is sensor-shaped — it reads `Loggable.logDataChunks` and parses samples back via `parseLogSample`. For the output of a data-processor chain (throttle, RMS, accumulator, fuser, …) the SDK exposes a key-based overload that takes the `ProcessorHandle` directly and lets you supply your own decoder at download time: + +```kotlin +// Throttle 100 Hz Euler fusion down to 1 Hz, log 10 s, then download. +val euler = SensorFusionEuler(mode = SensorFusionMode.NDOF, chip = SensorFusionChip.BMI270) +for (cmd in euler.configureCommands + euler.enableCommands + euler.startCommands) device.send(RawCommand(cmd)) + +val throttle = device.createProcessor( + DataProcessor.Time(periodMs = 1000, mode = DataProcessor.Time.Mode.ABSOLUTE), + source = SensorFusionEulerSignal(), +) +val key = "euler-throttle-1hz" +device.startLogging(throttle, key = key) +delay(10.seconds) +device.stopLogging(key = key) +for (cmd in euler.stopCommands + euler.disableCommands) device.send(RawCommand(cmd)) +device.flushLogPage() // MMS only (no-op elsewhere) + +device.downloadLogs(key = key) { data -> + euler.parseLogSample(data) // any (ByteArray) -> S decoder +}.collect { progress -> + println("${progress.percentComplete} ${progress.data.size} samples") +} +``` + +What's going on: + +- `startLogging(handle, key)` splits the handle's full output (`nChannels × channelSize`) into ≤ 4-byte chunks (the firmware's per-entry byte limit), issues one `[0x0B, 0x02, 0x09, 0x03, proc_id, packed]` subscribe per chunk, registers the resulting logger IDs under `key`, and transitions the device to `Logging`. It does not touch any sensor lifecycle — starting and stopping the source that feeds the chain is your job. +- `downloadLogs(key, decode)` reassembles entries by logger-ID order and hands each reconstructed payload to your decoder. + +Sensor-fusion outputs are exposed as `Signal` values for use as processor sources: `SensorFusionEulerSignal`, `SensorFusionQuaternionSignal`, `SensorFusionGravitySignal`, `SensorFusionLinearAccelerationSignal`. + +--- + +## Firmware updates + +`:metawear-firmware` is a separate module so apps that never flash firmware don't pull in the Nordic DFU library. Every update entry point returns a cold `Flow`: drive a progress bar from the same collection that catches failure, and cancel the collector to abort the transfer. + +### Check and update a connected board + +```kotlin +import com.mbientlab.metawear.firmware.checkForFirmwareUpdate +import com.mbientlab.metawear.firmware.updateFirmwareToLatest + +// Is something newer on the MbientLab release catalog? +val build = device.checkForFirmwareUpdate() // FirmwareBuild? — null when already current +if (build != null) println("Update available: ${build.firmwareRev}") + +// Flash the latest release. The flow finishes after FETCHING_CATALOG with no COMPLETED event +// if the board is already current (pass forceReinstall = true to reflash regardless). +device.updateFirmwareToLatest(context).collect { progress -> + println("${progress.state} ${progress.percentComplete.toInt()}% part ${progress.currentPart}/${progress.totalParts}") +} + +// The flash reboots the board and leaves the cached device state stale: +device.connect() +``` + +`DFUProgress.State` walks `FETCHING_CATALOG → DOWNLOADING_FIRMWARE → BOOTLOADER_HANDOFF → SCANNING → CONNECTING → STARTING → UPLOADING → VALIDATING → DISCONNECTING → COMPLETED` (or `ABORTED`); `percentComplete` is only populated during `UPLOADING`, and `bytesPerSecond` reports the transfer rate. + +`updateFirmware(context, zipUrl)` flashes an explicit firmware file instead of the catalog build — an `https://` URL or a `file://` URI; `.zip` (Nordic DFU distribution package), or raw `.bin` / `.hex`. Either path handles the bootloader handoff itself: it sends `[0xFE, 0x02]` via `sendExpectingDisconnect(Debug.JumpToBootloader())`, waits for the board to drop the link as it reboots into MetaBoot, then runs the Nordic DFU transfer against the same MAC address. + +An outdated bootloader is handled automatically on the catalog path: after the handoff, `MetaBootProbe` reads the installed bootloader version from MetaBoot's Device Information service, `BootloaderInterlock` compares it against the build's `required-bootloader`, and the needed bootloader-flavor flash(es) are chained before the application stage. Multi-stage flashes surface through `DFUProgress.currentPart` / `totalParts`; `COMPLETED` is emitted only when the final stage finishes. + +`FirmwareServer` is the catalog client (`availableBuilds`, `latestBuild`, `build`, `updateAvailable`, `downloadFirmware`; default catalog `https://mbientlab.com/releases/metawear/info2.json`), `FirmwareBuild` describes one artifact (`hardwareRev`, `modelNumber`, `buildFlavor`, `firmwareRev`, `filename`, `requiredBootloader`, `firmwareUrl`), and `FirmwareException` is the sealed error taxonomy (`BadServerResponse`, `NoAvailableFirmware`, `InvalidFirmwareFile`, `BootloaderUpgradeUnavailable`, `DeviceNotIdle`, `DfuFailed`, `Aborted`, …). + +### Android specifics + +- The DFU transfer runs inside an Android service. The library declares `MetaWearDfuService` (a `DfuBaseService` subclass) in its manifest, and manifest merging carries the registration into your app — nothing to add unless you subclass it for a foreground notification target, in which case also create Nordic's notification channel via `DfuServiceInitiator.createDfuNotificationChannel(context)` first. +- Firmware updates need the same `BLUETOOTH_CONNECT` runtime permission as the rest of the SDK. +- The catalog JSON is parsed by a minimal hand-rolled parser (Android's `org.json` is stubbed out on the JVM unit-test classpath), keeping all **66 firmware tests** — version compare, catalog selection, server/mock-fetcher, bootloader interlock — plain JVM unit tests. The `DfuSession` / `MetaBootProbe` wrappers are hardware-only and carry no unit-test coverage. + +### Boards stuck in MetaBoot + +A board whose application flash never completed sits in bootloader mode: it advertises as **"MetaBoot"** with the Nordic DFU service instead of the MetaWear service, so `MetaWearScanner` deliberately does not admit it and the normal connect flow can't talk to it. The SDK's DFU path recovers such a board when it already knows the MAC (the `updateFirmware*` flows scan for the bootloader by address after the handoff), but there is no standalone MetaBoot scan mode or rescue-by-address entry point yet — see [What's not yet implemented](#whats-not-yet-implemented). + +--- + +## Persistence (Room) + +`:metawear-persistence` stores downloaded log sessions in a Room database (`com.mbientlab.metawear.persistence`). It depends on `:metawear-protocol` and ships its own JVM test suite (46 tests, against an in-memory fake of the DAO) plus an instrumented suite (13 tests) that re-checks what the fake can only mirror — the generated SQL's sort orders and counts, the foreign-key cascade, and the epoch-millis converter — against a real Room database on-device. + +### One database per app, one store per call site + +```kotlin +import com.mbientlab.metawear.persistence.* + +val db = PersistenceDatabase.create(context) // or PersistenceDatabase.createInMemory(context) for tests +val store = PersistenceStore(db.persistenceDao()) +``` + +All `PersistenceStore` methods are `suspend` functions over the Room DAO. + +### Save a download session + +```kotlin +val samples: List> = collectAllSamples(device.downloadLogs(sensor)) // your code + +val snapshot = store.saveSession( + deviceID = device.identifier, + deviceInfo = device.deviceInfo!!, + sensorKind = CartesianFloatPersistable.persistenceKind, // "cartesian" + samples = samples, + persistable = CartesianFloatPersistable, + label = "Accelerometer · ±2 g · 50 Hz", // optional display hint + deviceName = scanner.advertisedNames.value[device.identifier], +) +println("Saved session ${snapshot.id} with ${snapshot.sampleCount} samples") +``` + +`SessionSnapshot` is a plain immutable value (`id`, `deviceID`, `sensorKind`, `startDate`, `endDate`, `sampleCount`, `deviceSerial`, `deviceModel`, `deviceFirmware`, `label`, `deviceName`, `groupID`) — safe to hand to Compose. The Room `@Entity` types (`SessionRecord`, `SampleRecord`, with a cascade foreign key so deleting a session deletes its samples) stay inside the store. + +### Fetch / reconstruct / export + +```kotlin +// All sessions for one device, newest first (or store.fetchAllSessions()) +val sessions = store.fetchSessions(deviceID = device.identifier) + +// Rehydrate typed samples +val acceleration = store.fetchSamples(sessionID = snapshot.id, persistable = CartesianFloatPersistable) + +// One-step CSV — columns epoch,elapsed_ms,x,y,z +val table = store.exportTable(sessionID = snapshot.id, persistable = CartesianFloatPersistable) +table.writeCsv(File(context.cacheDir, "session.csv")) +``` + +`fetchSamples` and `exportTable` validate that the session's `sensorKind` matches the requested codec and throw `PersistenceException.KindMismatch` otherwise; `saveSession` with zero samples throws `PersistenceException.EmptySampleSet`; unknown IDs throw `PersistenceException.SessionNotFound`. + +### Delete + +```kotlin +store.deleteSession(id = snapshot.id) +store.deleteAllSessions(deviceID = device.identifier) +store.deleteAll() +``` + +### Supported sample types + +`Persistable` is a small codec interface that pairs a `persistenceKind` discriminator with a flat `(f0, f1, f2, f3, accuracy)` packing (`PersistedValues`). One singleton codec ships per SDK sample type — `FloatPersistable` (`"float"`), `BoolPersistable` (`"bool"`), `CartesianFloatPersistable` (`"cartesian"`), `CorrectedCartesianFloatPersistable` (`"corrected-cartesian"`), `QuaternionPersistable` (`"quaternion"`), `EulerAnglesPersistable` (`"euler"`) — in `PersistableConformances.kt`. `Instant`s persist as epoch milliseconds; sessions are identified by store-assigned UUID strings and devices by their Android MAC identifier. Adding a new sensor type means adding one codec object. + +--- + +## Data modes + +### Streaming (live) + +BLE delivers data as fast as the connection interval allows (~100 Hz practical aggregate on most phones — the app's `BandwidthAdvisor` warns past that). Packed mode sends 3 samples per BLE packet, tripling effective throughput for IMU sensors. + +``` +MetaWear → BLE notifications (packed, ~33/sec at 100 Hz) + → unpack 3 samples per notification + → Flow> +``` + +### Logging (on-device flash) + +Sensors log to flash at up to 800+ Hz independent of BLE. Download when done. + +``` +MetaWear flash → BLE burst download (1 or 2 entries per notification) + → parse 9-byte log entries (tick → epoch) + → Flow>>> +``` + +Log entry format (9 bytes, `PacketParser.parseLogEntry`): +``` +Byte 0: (reset_uid[2:0] << 5) | log_id[4:0] +Bytes 1–4: tick (32-bit LE, ~1.465 ms/tick) +Bytes 5–8: raw sensor data (32-bit LE) +``` + +A single-entry readout notification is therefore 2 (header) + 9 = 11 bytes; a paired one is 20. Tick math: `PacketParser.MS_PER_TICK = (48.0 / 32768.0) × 1000 ≈ 1.4648 ms/tick`. Signals wider than 4 bytes are split into ≤ 4-byte chunks (`LogChunk(offset, length)`), one logger ID per chunk, and reassembled in logger-ID order on download. + +--- + +## BLE packet format + +``` +Byte 0: module_id (e.g. 0x03 = accelerometer) +Byte 1: register_id (| 0x80 for READ requests; response echoes this bit set; + | 0x40 = "silent" read used by event-triggered / polled-logger reads) +Byte 2+: payload (little-endian) +``` + +Commands → `command` characteristic (`Uuids.command`, `326A9001-85CB-9195-D9DD-464CFBBAE75A`), write-without-response. Responses/notifications → `notify` characteristic (`Uuids.notify`, `326A9006-…`). The MetaWear service is `326A9000-…`. Macros use write-with-response. Device Information Service reads (`0x180A`: model `0x2A24`, serial `0x2A25`, firmware `0x2A26`, hardware `0x2A27`, manufacturer `0x2A29`) resolve by characteristic UUID. + +Module IDs (`com.mbientlab.metawear.protocol.Module`): + +| ID | Module | ID | Module | +|---|---|---|---| +| 0x01 | Switch | 0x0D | Serial (I2C / SPI) | +| 0x02 | LED | 0x0F | Macro | +| 0x03 | Accelerometer | 0x11 | Settings | +| 0x04 | Temperature | 0x12 | Barometer | +| 0x05 | GPIO | 0x13 | Gyroscope | +| 0x07 | iBeacon | 0x14 | Ambient light | +| 0x08 | Haptic | 0x15 | Magnetometer | +| 0x09 | Data processor | 0x16 | Humidity | +| 0x0A | Event | 0x19 | Sensor fusion | +| 0x0B | Logging | 0xFE | Debug | +| 0x0C | Timer | | | + +`Packet.command(module, register, vararg payload)` and `Packet.read(module, register, …)` build outgoing bytes; `PacketParser` holds the little-endian decoders (`parseInt16LE`, `parseUInt32LE`, `parseFloat32LE`, `parseCartesianFloat`, `parsePackedCartesianFloat`, `parseQuaternion`, `parseEulerAngles`, `parsePressure`, `parseAltitude`, `parseTemperature`, `parseIlluminance`, `parseHumidity`, `parseMacAddress`, `parseBatteryState`, `parseLogEntry`, …). + +--- + +## Testing + +### Unit tests (no hardware required) + +```bash +./gradlew :metawear-protocol:test +./gradlew :metawear-persistence:testDebugUnitTest +./gradlew :metawear-firmware:testDebugUnitTest +./gradlew :app:testDebugUnitTest +# or target one suite: +./gradlew :metawear-protocol:test --tests '*TimerTest*' +``` + +Four JVM test source sets ship with the repo — **1274 tests** in total: + +- **`:metawear-protocol`** — 1042 tests across 43 files. The full SDK surface, run against `MockBleTransport`, including reference byte vectors from the MetaWear C++ SDK's Python test suite. No hardware required. +- **`:metawear-persistence`** — 46 tests (`PersistableConformanceTest`, `PersistenceStoreTest`, `SessionExportTest`, `AttributionStampTest`) against an in-memory fake DAO — no hardware, no on-disk side effects. +- **`:metawear-firmware`** — 66 tests (`BootloaderInterlockTest`, `DFUProgressTest`, `FirmwareBuildTest`, `FirmwareCatalogTest`, `FirmwareExceptionTest`, `FirmwareServerTest`, `MetaWearVersionTest`). +- **`:app`** — 120 tests: ring buffer / decimation / effective-Hz math, quaternion cube and tare frame, CSV exporters, session grouping, foreign-log decisions, group-capture coordination, and end-to-end demo-emulator walks through the real `MetaWearDevice`. + +`:metawear-protocol` coverage by file (ordered roughly by dependency): + +| Suite file(s) | What it covers | +|------------------------------------------------------------|--------------------------------------------------------------------------------------| +| `protocol/PacketBuilderTest`, `PacketParserTest`, `ProtocolRegressionTest` | Command / read packet construction; raw byte → Kotlin type parsing for all sensors; wire vectors from the C++ SDK test suite | +| `protocol/ProtocolRouterTest` | Notification routing, module discovery, concurrent reads, timeouts | +| `transport/MockBleTransportTest`, `UuidsTest` | Mock transport semantics, in-band termination; UUID constants | +| `model/BoardModelTest`, `BoardStateTest` | Model detection; capture / restore of discovered modules, JSON round-trip, schema versioning | +| `model/DataTableTest`, `AnonymousSignalTest` | CSV table construction and export, derived Euler; reconstruction of unknown loggers, chunk partitioning, identifier scheme | +| `sensor/AccelerometerCommandTest`, `AccelerometerBoschTest`, `AccelerometerBmi270FeaturesTest` | BMI160 / BMI270 config bytes and packed registers; orientation / any-motion / tap / step; activity, wrist, no-motion, significant motion, downsampling | +| `sensor/GyroscopeCommandTest`, `MagnetometerCommandTest` | Gyro config bytes, ranges, offsets; magnetometer presets, manual config, suspend, warm-up | +| `sensor/BarometerCommandTest`, `AmbientLightTest`, `HumidityTest`, `TemperatureTest` | BMP280 / BME280 config, altimeter, pressure read; LTR329 config + lux; humidity read + oversampling; channel / silent reads, ext-thermistor config, polled logging | +| `sensor/SensorFusionCommandTest`, `SensorFusionTest` | Mode / range / chip config bytes, seven outputs, shared engine, calibration | +| `sensor/LedTest`, `SwitchHapticTest`, `GpioTest` | LED pattern bytes and presets, `setLed` / `stopLed`; switch stream, haptic pulses; GPIO outputs, one-shot reads, analog parameters, pin-change stream | +| `sensor/TimerTest`, `EventTest`, `MacroTest` | Timer create/start/stop/remove, tick stream; event sources, ENTRY format, data token; recordMacro (list + body), ADD_PARTIAL, execute, erase | +| `sensor/SerialTest`, `IBeaconTest` | I2C / SPI write + read bytes and response parsing; iBeacon UUID / major / minor / power / period bytes | +| `sensor/DataProcessorTest`, `DataProcessorDemuxTest` | ADD command bytes and config bits for all 17 processor types; per-id demux | +| `sensor/SettingsTest`, `DebugTest`, `MiscReadablesTest` | Device name validation, TX power, advertising, connection params, whitelist, reads; debug commands; `LogLength`, `LastResetTime`, `LoggingEnabled`, MAC | +| `MetaWearDeviceTest`, `MetaWearScannerTest` | State machine, connect/disconnect, streaming guards, fusion / IMU conflicts; admission rule, name / RSSI caches, known-device promotion | +| `GenericReadPollTest` | Generic `device.read` and `device.poll` for `Readable` / `Pollable` | +| `LoggingTest`, `PolledLoggingTest`, `LogFinishingTest` | startLogging commands, `RawLogEntry` parsing, chunk config, download, clearLog; timer → event → logger chain and recovery; log time anchor, registry persistence | +| `DeviceAnonymousSignalsTest`, `FactoryResetTest` | Live-board scale reads and signal reconstruction; seven-write reset sequence, fallback, post-reset state | +| `ProductionGapTest` | Concurrent reads, multi-sensor streaming, reconnect, device-name validation, edge cases | + +### MockBleTransport + +Inject notifications and inspect written commands in tests: + +```kotlin +val transport = MockBleTransport().apply { + setReadResponse(Uuids.manufacturerName, "MbientLab".toByteArray()) + setReadResponse(Uuids.modelNumber, "8".toByteArray()) + setReadResponse(Uuids.serialNumber, "A0B1C2".toByteArray()) + setReadResponse(Uuids.firmwareRevision, "1.7.3".toByteArray()) + setReadResponse(Uuids.hardwareRevision, "0.1".toByteArray()) +} +val device = MetaWearDevice("AA:BB:CC:DD:EE:FF", transport, backgroundScope) + +// Inject a response to a read command +transport.inject(bytes(0x0C, 0x82, 0x01), Uuids.notify) + +// Inspect what the device wrote +val cmds: List = transport.writtenCommands // or writtenData for (data, characteristic, type) +transport.clearWrites() + +// Fault injection +transport.connectError = MetaWearException.BluetoothPoweredOff +transport.simulateDisconnect() // fails open streams / waiters +transport.mockRssi = -70 +``` + +`bytes(…)` is the test helper `internal fun bytes(vararg v: Int): ByteArray` — see [Design notes](#design-notes) for why. + +### Hardware integration tests + +Hardware tests require a real MetaWear nearby. They live in `:metawear-core`'s `androidTest` source set and run through `connectedAndroidTest` on a phone with USB debugging enabled; the test runner grants the Bluetooth permissions itself (`GrantPermissionRule`). + +1. Connect an Android phone with USB debugging enabled (API 31+ recommended). 2. Charge a MetaMotion S and place it within BLE range. 3. Run: @@ -315,30 +1754,74 @@ JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home" \ ./gradlew :metawear-core:connectedAndroidTest ``` -The suite scans for the first advertised "MetaWear" name for 10 s and **skips -itself cleanly** (JUnit assumption) when no board is in range, so the task is -safe to run on benches without hardware. With several boards in range it picks -the lowest MAC for run-to-run stability. +`HardwareSmokeTest` alone exercises a real MetaMotion S (BMI270) end-to-end: scan → connect (device info + module discovery) → battery read → LED pattern → 2 s accelerometer stream (~1 g at rest) → disconnect. `HardwareSupport` scans for the first advertised `"MetaWear"` name for 10 s and every test **skips itself cleanly** (JUnit `assumeTrue`) when no board is in range, so the task is safe to run on benches without hardware. With several boards in range it picks the lowest MAC for run-to-run stability. Modules that are not present on the connected board (e.g. humidity on a board without a BME280) are skipped within each test. The HTML report lands in `metawear-core/build/reports/androidTests/connected/`. + +Hardware suites — 43 tests across 11 files, verified on a Pixel 7 + MetaMotion S: + +| Suite | What it covers | +|------------------------------------|------------------------------------------------------------------------------------------------------------------| +| `HardwareSmokeTest` | Scan finds a MetaWear with MAC identifier + sane RSSI; connect reaches `Idle` with device info + modules; battery read; green LED flash plays and stops; packed accelerometer stream ~1 g at rest; disconnect returns to `Disconnected` | +| `GyroscopeBmi270HardwareTest` | Unpacked stream then back to `Idle`; packed stream at higher throughput | +| `MagnetometerHardwareTest` | Low-power stream; high-accuracy preset gives a plausible Earth field; packed stream and suspend when the module revision supports them | +| `SensorFusionHardwareTest` | Quaternion stream then `Idle`; unit-magnitude quaternion; gravity ~1 g; quaternion + Euler together on the shared engine | +| `SwitchHardwareTest` | Switch stream starts and stops without error | +| `HapticHardwareTest` | Motor pulse, buzzer pulse, motor at max duty cycle | +| `GpioHardwareTest` | Digital read with pull-up / pull-down, 10-bit ADC, absolute millivolts on the rail, pin-change stream lifecycle | +| `SettingsHardwareTest` | Realistic battery, device name set / restore, every TX power level, RSSI while connected, start advertising | +| `EnvironmentSensorHardwareTest` | NRF die temperature, barometer pressure, altimeter, humidity (skips when not fitted) | +| `ReadHardwareTest` | Generic `device.read` round-trip for every temperature channel, battery, last reset time, log length, MAC | +| `LoggingHardwareTest` | Accelerometer log → download round trip, log length zero after clear / increasing while logging, `queryActiveLoggers` empty after clear | + +The persistence module's instrumented suite (`PersistenceDatabaseTest`, 13 tests) needs only a phone, not a board: + +```bash +./gradlew :metawear-persistence:connectedAndroidTest +``` + +See [`HARDWARE.md`](HARDWARE.md) for the full step-by-step procedure (phone setup, `adb`, the suites, and a manual app pass), plus troubleshooting. + +--- + +## Demo mode + +`DemoBleTransport` (`app/src/main/kotlin/com/mbientlab/metawear/app/demo/`) is a protocol-level MetaMotion S emulator behind the same `BleTransport` seam the real transport implements. It emulates a connected MetaMotion S on firmware 1.7.3: module discovery, Device Information / battery / MAC / temperature / humidity / pressure / illuminance / log reads, synthetic waveforms on every sensor including the packed registers, all seven fusion outputs, altitude and ambient light, and full logging round trips — streamed sensors, the polled timer / event / logger chain, and logger recovery across a simulated process restart. + +- Turn it on with the **Demo mode** switch on the scan screen (no build flag or launch argument). The scan list shows a "Simulated MetaWear" board (`DE:30:DE:30:DE:30`), and the group-logging screen exposes a three-board simulated fleet. +- `DemoBleTransport.Identity.board(index)` mints up to 16 distinguishable identities (MAC, serial, waveform phase offset), so fleet flows work end-to-end with no hardware. +- The class is pure Kotlin (no Android imports), so the JVM unit tests drive it through the real `MetaWearDevice` — including full group-capture walks against the real persistence store. + +The 3D orientation view is deliberately a Canvas wireframe cube: only the quaternion frame math is load-bearing, and it stays dependency-free. + +--- ## Design notes -- **`ByteArray` equality** is reference-based in Kotlin — tests compare command - bytes with `assertArrayEquals`, never `==`; value classes wrapping `ByteArray` - (`ScanResult`, `MockBleTransport.Write`) override `equals` with `contentEquals`. -- Bytes are built from `Int` literals via a `bytes(…)` test helper to avoid the - `byteArrayOf(0x80)` "does not fit in Byte" compile error. -- `MockBleTransport` notification flows deliver termination **in-band** (as - sealed events through the channel) rather than via `Channel.close(cause)`: - closing a channel does not wake a receiver parked on a coroutines-test - `backgroundScope` dispatcher, while `trySend` does. In-band markers also - guarantee that packets buffered before a failure are delivered first, then - the failure is thrown. -- In tests, prefer `runCurrent()` (or suspending the test body) over - `advanceUntilIdle()` when the thing you're waiting on runs in - `backgroundScope` — `advanceUntilIdle` only drains foreground tasks. -- The protocol router's read timeout uses `withTimeout` + - `invokeOnCancellation` (atomic with respect to resume), so the waiter map is - simply pruned — no tombstoning needed to stay consistent under cancellation. -- Expected-failure `async` blocks in tests catch inside the block - (`async { runCatching { … } }`) — a failed `async` child cancels the test - scope even if the `await` is wrapped. +Kotlin / coroutines details the codebase relies on — read these before touching the tests: + +- **`ByteArray` equality** is reference-based in Kotlin — tests compare command bytes with `assertArrayEquals`, never `==`; value classes wrapping `ByteArray` (`ScanResult`, `MockBleTransport.Write`) override `equals` with `contentEquals`. +- Bytes are built from `Int` literals via a `bytes(…)` test helper to avoid the `byteArrayOf(0x80)` "does not fit in Byte" compile error (in production code use `0x9F.toByte()` or `Packet.command(...)`, which takes `Int` varargs). +- `MockBleTransport` notification flows deliver termination **in-band** (as sealed events through the channel) rather than via `Channel.close(cause)`: closing a channel does not wake a receiver parked on a coroutines-test `backgroundScope` dispatcher, while `trySend` does. In-band markers also guarantee that packets buffered before a failure are delivered first, then the failure is thrown. +- In tests, prefer `runCurrent()` (or suspending the test body) over `advanceUntilIdle()` when the thing you're waiting on runs in `backgroundScope` — `advanceUntilIdle` only drains foreground tasks. +- The protocol router's read timeout uses `withTimeout` + `invokeOnCancellation` (atomic with respect to resume), so the waiter map is simply pruned — no tombstoning needed to stay consistent under cancellation. +- Expected-failure `async` blocks in tests catch inside the block (`async { runCatching { … } }`) — a failed `async` child cancels the test scope even if the `await` is wrapped. +- Subscription and processor channels use `BufferOverflow.DROP_OLDEST` (256 packets): a stalled collector sheds the oldest samples rather than growing memory without bound. +- `MetaWearDevice` and `MetaWearScanner` take a `CoroutineScope` (default: a process-lifetime `SupervisorJob() + Dispatchers.Default`); pass a narrower scope (e.g. a ViewModel's) to tie SDK work to a lifecycle. Tests pass `runTest`'s `backgroundScope` for virtual time. + +--- + +## What's not yet implemented + +| Gap | Blocks | Workaround | +|---|---|---| +| Standalone MetaBoot scan mode / rescue-by-address firmware flash | Reflashing a board that is stranded in bootloader mode when you don't already have a `MetaWearDevice` for it | The `updateFirmware*` flows recover a board whose MAC you know once the handoff has happened; otherwise reflash with the Nordic nRF Connect app | +| First-class `Streamable` wrappers for the accelerometer interrupt registers (orientation, any-motion, tap, activity, wrist, no-motion, step) | Receiving interrupt packets without writing an adapter | Wrap the notification register in a small `Streamable` (see [Bosch motion detectors](#bosch-motion-detectors-accelerometer-interrupts)) — the commands and decoders ship | +| A `prepareSignalSource` / `teardownSignalSource` helper (start a sensor as a processor source without a live stream) | One-liner source setup for processor chains and processor-handle logging | Send the sensor's `configureCommands` / `enableCommands` / `startCommands` through a `Command` wrapper (see [Data processor](#data-processor-module-0x09)) | +| Charging-status as a `Loggable` | Persisting charge transitions to flash | `device.poll(Settings.ReadChargeStatus(), every = …)` covers the live-observation case | +| Maven Central publication | `implementation("com.mbientlab:metawear-…")` from a plain Gradle build | Include the modules from source or use a composite build (see [Add the SDK to your build](#add-the-sdk-to-your-build)) | +| App-side unit tests for `NordicBleTransport` / `DfuSession` / `MetaBootProbe` | JVM coverage of the Android-only wrappers | Covered by the instrumented hardware suites and manual firmware-update passes | + +--- + +## License + +See [`LICENSE.md`](LICENSE.md). Copyright MbientLab Inc. From 82d44a80d99aebf4f4e6982728e61efe73f3f617 Mon Sep 17 00:00:00 2001 From: lkasso Date: Sun, 16 Aug 2026 19:43:37 -0700 Subject: [PATCH 2/9] App: brand palette, launcher icon, Material 3 presentation pass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Exact brand palette (#FE9500 primary plus teal-green success, gold warning, coral danger, dusty-blue info) as light and dark Material 3 schemes with dynamic color off; chart channel colors unchanged. - Adaptive launcher icon from the "m" brand artwork: the glyph's enclosing circle is positioned inside the 66 dp safe zone (verified under circle and squircle masks), monochrome layer, legacy mipmaps. - Branded scan background: solid orange field, light-mode veil, five scan ripples from the bottom-leading corner, watermark "m"; device rows as Material cards on top. - Native Material patterns throughout (TopAppBar, grouped ListItem cards, FilterChips, Switch, Snackbar, AlertDialog confirms, sliders, system share sheet); status/battery/RSSI chips; per-screen section ordering aligned across the app family. Presentation only — no logic or ViewModel changes; 120 app tests unchanged and green. Co-Authored-By: Claude Fable 5 --- app/build.gradle.kts | 1 + app/src/main/AndroidManifest.xml | 2 + .../mbientlab/metawear/app/export/CsvShare.kt | 25 + .../mbientlab/metawear/app/ui/AppNavHost.kt | 37 +- .../metawear/app/ui/components/Components.kt | 274 +++++++---- .../metawear/app/ui/components/Formatters.kt | 50 ++ .../ui/components/FusionCalibrationBadge.kt | 101 +++-- .../metawear/app/ui/components/LineChart.kt | 12 +- .../app/ui/components/QuaternionCubeView.kt | 12 +- .../app/ui/components/SensorChartCard.kt | 184 ++++++++ .../metawear/app/ui/components/SensorIcons.kt | 39 ++ .../metawear/app/ui/components/StatusChips.kt | 247 ++++++++++ .../app/ui/controls/ControlsScreen.kt | 210 ++++++--- .../app/ui/device/DeviceDetailScreen.kt | 350 ++++++++++---- .../app/ui/device/DeviceInfoScreen.kt | 107 +++-- .../app/ui/firmware/FirmwareScreen.kt | 231 ++++++---- .../app/ui/logging/GroupLoggingScreen.kt | 395 ++++++++++------ .../metawear/app/ui/logging/LoggingScreen.kt | 332 ++++++++++---- .../app/ui/scan/BrandScanBackground.kt | 72 +++ .../metawear/app/ui/scan/ScanScreen.kt | 427 +++++++++++++----- .../app/ui/sessions/SessionDetailScreen.kt | 168 +++++++ .../app/ui/sessions/SessionReplayView.kt | 76 ++-- .../app/ui/sessions/SessionsScreen.kt | 222 ++++----- .../app/ui/settings/SettingsScreen.kt | 421 +++++++++-------- .../app/ui/stream/LiveStreamScreen.kt | 266 +++++------ .../app/ui/stream/SensorConfigSection.kt | 90 ++-- .../metawear/app/ui/theme/Palette.kt | 47 ++ .../mbientlab/metawear/app/ui/theme/Theme.kt | 130 ++++-- .../res/drawable/ic_launcher_monochrome.xml | 12 + .../main/res/mipmap-anydpi/ic_launcher.xml | 10 + .../res/mipmap-anydpi/ic_launcher_round.xml | 10 + app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 8703 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 35574 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 8005 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 4462 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 17499 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 4239 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 13822 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 58616 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 12676 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 27068 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 119283 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 24542 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 43693 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 177484 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 39183 bytes .../res/values/ic_launcher_background.xml | 5 + gradle/libs.versions.toml | 3 + 48 files changed, 3256 insertions(+), 1312 deletions(-) create mode 100644 app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/Formatters.kt create mode 100644 app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/SensorChartCard.kt create mode 100644 app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/SensorIcons.kt create mode 100644 app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/StatusChips.kt create mode 100644 app/src/main/kotlin/com/mbientlab/metawear/app/ui/scan/BrandScanBackground.kt create mode 100644 app/src/main/kotlin/com/mbientlab/metawear/app/ui/sessions/SessionDetailScreen.kt create mode 100644 app/src/main/kotlin/com/mbientlab/metawear/app/ui/theme/Palette.kt create mode 100644 app/src/main/res/drawable/ic_launcher_monochrome.xml create mode 100644 app/src/main/res/mipmap-anydpi/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/values/ic_launcher_background.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3370ef4..a5f2d73 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -52,6 +52,7 @@ dependencies { implementation(libs.androidx.compose.ui) implementation(libs.androidx.compose.foundation) implementation(libs.androidx.compose.material3) + implementation(libs.androidx.compose.material.icons.extended) implementation(libs.androidx.activity.compose) implementation(libs.androidx.navigation.compose) implementation(libs.androidx.lifecycle.viewmodel.compose) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a24e274..305e15b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,6 +30,8 @@ diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/export/CsvShare.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/export/CsvShare.kt index 1a6a533..c29dcfd 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/export/CsvShare.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/export/CsvShare.kt @@ -2,6 +2,7 @@ package com.mbientlab.metawear.app.export import android.content.Context import android.content.Intent +import android.net.Uri import androidx.core.content.FileProvider import java.io.File @@ -25,4 +26,28 @@ object CsvShare { } context.startActivity(Intent.createChooser(send, "Export $filename")) } + + /** + * Share several CSVs in one go (`ACTION_SEND_MULTIPLE`) — one chooser for + * a whole live-stream buffer instead of one per channel. A single file + * falls back to [share]. + */ + fun shareAll(context: Context, files: List>) { + if (files.isEmpty()) return + if (files.size == 1) return share(context, files[0].first, files[0].second) + val dir = File(context.cacheDir, "exports").apply { mkdirs() } + val uris = ArrayList() + for ((filename, csv) in files) { + val file = File(dir, filename) + file.writeText(csv) + uris += FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", file) + } + val send = Intent(Intent.ACTION_SEND_MULTIPLE).apply { + type = "text/csv" + putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris) + putExtra(Intent.EXTRA_SUBJECT, "MetaWear export (${files.size} files)") + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + context.startActivity(Intent.createChooser(send, "Export ${files.size} CSV files")) + } } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/AppNavHost.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/AppNavHost.kt index 8c26756..6bcb185 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/AppNavHost.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/AppNavHost.kt @@ -5,9 +5,11 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import androidx.navigation.navArgument import com.mbientlab.metawear.app.ui.controls.ControlsScreen import com.mbientlab.metawear.app.ui.device.DeviceDetailScreen import com.mbientlab.metawear.app.ui.device.DeviceInfoScreen @@ -15,14 +17,16 @@ import com.mbientlab.metawear.app.ui.firmware.FirmwareScreen import com.mbientlab.metawear.app.ui.logging.GroupLoggingScreen import com.mbientlab.metawear.app.ui.logging.LoggingScreen import com.mbientlab.metawear.app.ui.scan.ScanScreen +import com.mbientlab.metawear.app.ui.sessions.SessionDetailScreen import com.mbientlab.metawear.app.ui.sessions.SessionsScreen import com.mbientlab.metawear.app.ui.settings.SettingsScreen import com.mbientlab.metawear.app.ui.stream.LiveStreamScreen -/** Navigation graph: scan → device hub → feature screens. */ +/** Navigation graph: scan → device hub → feature screens (+ session history/detail). */ @Composable fun AppNavHost() { val navController = rememberNavController() + val back: () -> Unit = { navController.popBackStack() } NavHost( navController = navController, @@ -35,23 +39,38 @@ fun AppNavHost() { ScanScreen( onDeviceSelected = { navController.navigate("device") }, onGroupLogging = { navController.navigate("group") }, + onSessionHistory = { navController.navigate("sessions") }, ) } - composable("group") { GroupLoggingScreen() } + composable("group") { + GroupLoggingScreen(onBack = back, onSessionHistory = { navController.navigate("sessions") }) + } composable("device") { DeviceDetailScreen( onNavigate = { route -> navController.navigate(route) }, onDisconnected = { navController.popBackStack("scan", inclusive = false) }, + onBack = back, ) } - composable("info") { DeviceInfoScreen() } - composable("stream") { LiveStreamScreen() } - composable("logging") { LoggingScreen() } - composable("sessions") { SessionsScreen() } - composable("controls") { ControlsScreen() } + composable("info") { DeviceInfoScreen(onBack = back) } + composable("stream") { LiveStreamScreen(onBack = back) } + composable("logging") { LoggingScreen(onBack = back) } + composable("sessions") { + SessionsScreen(onBack = back, onOpenSession = { id -> navController.navigate("session/$id") }) + } + composable( + route = "session/{id}", + arguments = listOf(navArgument("id") { type = NavType.StringType }), + ) { entry -> + SessionDetailScreen(sessionId = entry.arguments?.getString("id").orEmpty(), onBack = back) + } + composable("controls") { ControlsScreen(onBack = back) } composable("settings") { - SettingsScreen(onFactoryReset = { navController.popBackStack("device", inclusive = false) }) + SettingsScreen( + onFactoryReset = { navController.popBackStack("device", inclusive = false) }, + onBack = back, + ) } - composable("firmware") { FirmwareScreen() } + composable("firmware") { FirmwareScreen(onBack = back) } } } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/Components.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/Components.kt index 64b297c..ed8219b 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/Components.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/Components.kt @@ -1,125 +1,239 @@ package com.mbientlab.metawear.app.ui.components -import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults -import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem +import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text -import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.mbientlab.metawear.DeviceState -import com.mbientlab.metawear.app.ui.theme.GlassAccent -import com.mbientlab.metawear.app.ui.theme.GlassError -import com.mbientlab.metawear.app.ui.theme.GlassGood -import com.mbientlab.metawear.app.ui.theme.GlassSurfaceHigh -import com.mbientlab.metawear.app.ui.theme.GlassTextDim -import com.mbientlab.metawear.app.ui.theme.GlassWarn +import com.mbientlab.metawear.app.ui.theme.forText -/** Translucent rounded card — the app's "glass" surface treatment. */ +/** Card shape used across the app — a touch rounder than the Material default. */ +val BrandCardShape = RoundedCornerShape(20.dp) + +/** + * The app's standard card: a filled Material card on the low surface tone, + * with a Column body. Pass [onClick] to make the whole card tappable and + * [contentPadding] = 0 when the body is a stack of [ListItem] rows. + */ @Composable -fun GlassCard( +fun BrandCard( modifier: Modifier = Modifier, + onClick: (() -> Unit)? = null, + contentPadding: PaddingValues = PaddingValues(16.dp), + containerColor: Color = MaterialTheme.colorScheme.surfaceContainerLow, + verticalSpacing: Dp = 8.dp, content: @Composable ColumnScope.() -> Unit, ) { - ElevatedCard( - modifier = modifier.fillMaxWidth(), - shape = RoundedCornerShape(16.dp), - colors = CardDefaults.elevatedCardColors( - containerColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.85f), - ), - ) { + val colors = CardDefaults.cardColors(containerColor = containerColor) + val body: @Composable ColumnScope.() -> Unit = { Column( - modifier = Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.padding(contentPadding), + verticalArrangement = Arrangement.spacedBy(verticalSpacing), content = content, ) } + if (onClick != null) { + Card( + onClick = onClick, + modifier = modifier.fillMaxWidth(), + shape = BrandCardShape, + colors = colors, + content = body, + ) + } else { + Card( + modifier = modifier.fillMaxWidth(), + shape = BrandCardShape, + colors = colors, + content = body, + ) + } } +/** + * A card whose body is a list of rows ([ListItem]s separated by + * [androidx.compose.material3.HorizontalDivider]) — the grouped-settings idiom. + */ @Composable -fun SectionHeader(title: String) { - Text( - title.uppercase(), - style = MaterialTheme.typography.labelMedium, - color = GlassTextDim, - modifier = Modifier.padding(top = 8.dp), +fun GroupCard( + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit, +) { + BrandCard( + modifier = modifier, + contentPadding = PaddingValues(0.dp), + verticalSpacing = 0.dp, + content = content, ) } -/** Label on the left, value on the right — the settings-list staple. */ +/** List subheader: sits above a card group. */ @Composable -fun LabeledValue(label: String, value: String) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Text(label, color = GlassTextDim, style = MaterialTheme.typography.bodyMedium) - Text(value, style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Medium) - } +fun SectionHeader(title: String, color: Color = MaterialTheme.colorScheme.secondary) { + Text( + title, + style = MaterialTheme.typography.titleSmall, + color = color, + modifier = Modifier.padding(start = 4.dp, top = 8.dp, end = 4.dp), + ) } -/** Colored capsule reporting the device connection state. */ +/** Explanatory footer text below a card group. */ @Composable -fun StateBadge(state: DeviceState) { - val (label, color) = when (state) { - DeviceState.Disconnected -> "Disconnected" to GlassTextDim - DeviceState.Connecting -> "Connecting…" to GlassWarn - DeviceState.Idle -> "Connected" to GlassGood - DeviceState.Streaming -> "Streaming" to GlassAccent - DeviceState.Logging -> "Logging" to GlassAccent - is DeviceState.Downloading -> "Downloading ${(state.progress * 100).toInt()}%" to GlassAccent - } +fun SectionFooter(text: String, color: Color = MaterialTheme.colorScheme.onSurfaceVariant) { Text( - label, - style = MaterialTheme.typography.labelMedium, + text, + style = MaterialTheme.typography.bodySmall, color = color, - modifier = Modifier - .background(GlassSurfaceHigh, RoundedCornerShape(50)) - .padding(horizontal = 10.dp, vertical = 4.dp), + modifier = Modifier.padding(horizontal = 4.dp), ) } -/** Signal-strength pill: rough RSSI to quality words + color. */ +/** Transparent list-item colors so rows sit on the enclosing card. */ @Composable -fun RssiPill(rssi: Int?) { - val (label, color) = when { - rssi == null -> "—" to GlassTextDim - rssi >= -60 -> "$rssi dBm" to GlassGood - rssi >= -80 -> "$rssi dBm" to GlassWarn - else -> "$rssi dBm" to GlassError - } - Text(label, style = MaterialTheme.typography.labelMedium, color = color) -} +fun cardListItemColors() = ListItemDefaults.colors(containerColor = Color.Transparent) -/** Inline error banner with dismissal. */ +/** Label on the left, value on the right — the settings-list staple. */ @Composable -fun ErrorBanner(message: String?, onDismiss: () -> Unit) { - if (message == null) return - GlassCard { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { +fun LabeledValue(label: String, value: String, monospace: Boolean = false) { + ListItem( + headlineContent = { Text(label) }, + trailingContent = { Text( - message, - color = GlassError, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.weight(1f), + value, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontFamily = if (monospace) FontFamily.Monospace else null, + maxLines = 1, + overflow = TextOverflow.Ellipsis, ) - TextButton(onClick = onDismiss) { Text("Dismiss") } - } + }, + colors = cardListItemColors(), + ) +} + +/** Tappable row with a leading icon — for actions inside a [GroupCard]. */ +@Composable +fun ActionRow( + title: String, + icon: ImageVector, + onClick: () -> Unit, + modifier: Modifier = Modifier, + /** Emphasis color (e.g. danger for destructive rows); null = brand icon, plain title. */ + tint: Color? = null, + supporting: String? = null, + enabled: Boolean = true, +) { + val disabledColor = MaterialTheme.colorScheme.onSurfaceVariant + val titleColor = when { + !enabled -> disabledColor + tint != null -> tint.forText() + else -> MaterialTheme.colorScheme.onSurface + } + val iconColor = when { + !enabled -> disabledColor + else -> tint ?: MaterialTheme.colorScheme.primary + } + ListItem( + headlineContent = { Text(title, color = titleColor) }, + supportingContent = supporting?.let { { Text(it) } }, + leadingContent = { Icon(icon, contentDescription = null, tint = iconColor) }, + colors = cardListItemColors(), + modifier = modifier.then( + if (enabled) Modifier.clickable(onClick = onClick) else Modifier, + ), + ) +} + +/** A transient message for the scaffold's snackbar; [onShown] clears the source state. */ +data class Notice(val message: String, val onShown: () -> Unit) + +/** + * Screen frame: top app bar (with optional back navigation and actions) and + * a snackbar host that surfaces the screen's latest error/status [notice]. + * The container is transparent so screens can paint their own background. + */ +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AppScaffold( + title: String, + modifier: Modifier = Modifier, + onBack: (() -> Unit)? = null, + notice: Notice? = null, + /** Title/icon color for the top bar when the screen paints its own chrome (e.g. white on orange). */ + topBarContentColor: Color? = null, + containerColor: Color = MaterialTheme.colorScheme.background, + actions: @Composable RowScope.() -> Unit = {}, + content: @Composable (PaddingValues) -> Unit, +) { + val snackbarHost = remember { SnackbarHostState() } + LaunchedEffect(notice?.message) { + val current = notice ?: return@LaunchedEffect + snackbarHost.showSnackbar(current.message, duration = SnackbarDuration.Long) + current.onShown() } + Scaffold( + modifier = modifier, + containerColor = containerColor, + snackbarHost = { SnackbarHost(snackbarHost) }, + topBar = { + TopAppBar( + title = { Text(title, maxLines = 1, overflow = TextOverflow.Ellipsis) }, + navigationIcon = { + if (onBack != null) { + IconButton(onClick = onBack) { + Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back") + } + } + }, + actions = actions, + colors = if (topBarContentColor != null) { + TopAppBarDefaults.topAppBarColors( + containerColor = Color.Transparent, + scrolledContainerColor = Color.Transparent, + titleContentColor = topBarContentColor, + actionIconContentColor = topBarContentColor, + navigationIconContentColor = topBarContentColor, + ) + } else { + TopAppBarDefaults.topAppBarColors( + containerColor = Color.Transparent, + scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainer, + ) + }, + ) + }, + content = content, + ) } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/Formatters.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/Formatters.kt new file mode 100644 index 0000000..a41fd9c --- /dev/null +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/Formatters.kt @@ -0,0 +1,50 @@ +package com.mbientlab.metawear.app.ui.components + +import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle +import java.util.Locale +import kotlinx.datetime.Instant + +private val dateTimeFormatter: DateTimeFormatter = + DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT) +private val timeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss") +private val clockFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm") + +private fun Instant.zoned() = java.time.Instant.ofEpochMilli(toEpochMilliseconds()).atZone(ZoneId.systemDefault()) + +/** "Aug 16, 2026, 7:15 PM" (locale-dependent). */ +fun Instant.formatDateTime(): String = dateTimeFormatter.format(zoned()) + +/** "19:15:03" */ +fun Instant.formatTime(): String = timeFormatter.format(zoned()) + +/** "19:15" */ +fun Instant.formatClock(): String = clockFormatter.format(zoned()) + +/** "mm:ss" under an hour, "h:mm:ss" beyond. */ +fun formatDuration(totalSeconds: Int): String { + val s = totalSeconds.coerceAtLeast(0) + val hours = s / 3600 + val minutes = (s % 3600) / 60 + val seconds = s % 60 + return if (hours > 0) { + String.format(Locale.US, "%d:%02d:%02d", hours, minutes, seconds) + } else { + String.format(Locale.US, "%d:%02d", minutes, seconds) + } +} + +/** Fixed-precision value by unit: 2 digits for fine units, 1 for moderate, 0 for coarse. */ +fun formatMeasurement(value: Float, unit: String): String { + val digits = when (unit) { + "g", "", "ratio" -> 2 + "°C", "%", "µT", "lux", "m" -> 1 + else -> 0 + } + return String.format(Locale.US, "%.${digits}f", value) +} + +/** "50 Hz" for whole rates, "12.5 Hz" otherwise. */ +fun formatHz(hz: Double): String = + if (hz == hz.toLong().toDouble()) "${hz.toLong()} Hz" else String.format(Locale.US, "%.1f Hz", hz) diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/FusionCalibrationBadge.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/FusionCalibrationBadge.kt index 6f8ee85..c0bc152 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/FusionCalibrationBadge.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/FusionCalibrationBadge.kt @@ -1,55 +1,61 @@ package com.mbientlab.metawear.app.ui.components -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CenterFocusStrong +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.Verified +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.mbientlab.metawear.app.core.CalibrationReadiness -import com.mbientlab.metawear.app.ui.theme.GlassAccent -import com.mbientlab.metawear.app.ui.theme.GlassError -import com.mbientlab.metawear.app.ui.theme.GlassGood -import com.mbientlab.metawear.app.ui.theme.GlassTextDim -import com.mbientlab.metawear.app.ui.theme.GlassWarn +import com.mbientlab.metawear.app.ui.theme.Palette import com.mbientlab.metawear.sensor.SensorFusionCalibration /** - * Live fusion-calibration badge: three A/G/M accuracy chips plus per-sensor - * coaching. The bar is MEDIUM — a demoted-indoors magnetometer must not read - * as a problem. + * Live fusion-calibration card: readiness headline, three A/G/M accuracy + * chips, and per-sensor coaching for whichever sensor is still below the + * bar. The bar is MEDIUM — a demoted-indoors magnetometer must not read as + * a problem. */ @Composable fun FusionCalibrationBadge(calibration: SensorFusionCalibration) { val state = CalibrationReadiness.state(calibration) + val (icon, tint) = when (state) { + CalibrationReadiness.State.CALIBRATING -> Icons.Filled.CenterFocusStrong to Palette.warning + CalibrationReadiness.State.READY -> Icons.Filled.CheckCircle to Palette.info + CalibrationReadiness.State.FULLY_CALIBRATED -> Icons.Filled.Verified to Palette.success + } - GlassCard { + BrandCard(verticalSpacing = 6.dp) { Row( modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, + horizontalArrangement = Arrangement.spacedBy(10.dp), verticalAlignment = Alignment.CenterVertically, ) { + Icon(icon, contentDescription = null, tint = tint) Text( CalibrationReadiness.title(state), style = MaterialTheme.typography.titleSmall, - color = when (state) { - CalibrationReadiness.State.FULLY_CALIBRATED -> GlassGood - CalibrationReadiness.State.READY -> GlassAccent - CalibrationReadiness.State.CALIBRATING -> GlassWarn - }, + modifier = Modifier.weight(1f), ) - Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { - LevelChip("A", calibration.accelerometer) - LevelChip("G", calibration.gyroscope) - LevelChip("M", calibration.magnetometer) - } + LevelChip("A", calibration.accelerometer) + LevelChip("G", calibration.gyroscope) + LevelChip("M", calibration.magnetometer) } when (state) { @@ -57,35 +63,50 @@ fun FusionCalibrationBadge(calibration: SensorFusionCalibration) { CalibrationReadiness.State.READY -> Text( CalibrationReadiness.READY_FOOTNOTE, style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, + color = MaterialTheme.colorScheme.onSurfaceVariant, ) CalibrationReadiness.State.CALIBRATING -> { CalibrationReadiness.coaching(calibration).forEach { coaching -> - Text( - "${coaching.sensor}: ${coaching.advice}", - style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, - ) + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + Text( + coaching.sensor, + style = MaterialTheme.typography.labelSmall, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.width(14.dp), + textAlign = TextAlign.Center, + ) + Text( + coaching.advice, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.weight(1f), + ) + } } } } } } +/** One accuracy chip: white letter on a level-colored capsule. */ @Composable private fun LevelChip(label: String, level: Int) { val color = when { - level <= 0 -> GlassError - level == 1 -> GlassWarn - level == 2 -> GlassAccent - else -> GlassGood + level <= 0 -> Palette.danger + level == 1 -> Palette.warning + level == 2 -> Palette.info + else -> Palette.success + } + Surface(shape = CircleShape, color = color, contentColor = Color.White) { + Text( + label, + style = MaterialTheme.typography.labelMedium, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center, + modifier = Modifier + .size(width = 26.dp, height = 22.dp) + .padding(top = 2.dp), + ) } - Text( - "$label $level", - style = MaterialTheme.typography.labelMedium, - color = Color(0xFF06222E), - modifier = Modifier - .background(color, RoundedCornerShape(50)) - .padding(horizontal = 8.dp, vertical = 2.dp), - ) } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/LineChart.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/LineChart.kt index 37e149d..1a49a84 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/LineChart.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/LineChart.kt @@ -3,15 +3,16 @@ package com.mbientlab.metawear.app.ui.components import androidx.compose.foundation.Canvas import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Path import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.mbientlab.metawear.app.core.AnyChartSample -import com.mbientlab.metawear.app.ui.theme.GlassSurfaceHigh /** * Self-contained Canvas line chart for the decimated live buffer. One path @@ -27,13 +28,14 @@ fun LineChart( modifier: Modifier = Modifier, height: Dp = 160.dp, ) { + val gridColor = MaterialTheme.colorScheme.outlineVariant Canvas( modifier = modifier .fillMaxWidth() .height(height), ) { // Frame + midline. - drawRect(GlassSurfaceHigh, style = Stroke(width = 1.dp.toPx())) + drawRect(gridColor, style = Stroke(width = 1.dp.toPx())) if (samples.size < 2) return@Canvas @@ -67,9 +69,9 @@ fun LineChart( val midValue = if (minY <= 0f && 0f <= maxY) 0f else (minY + maxY) / 2f val midY = size.height * (1f - (midValue - minY) / ySpan) drawLine( - GlassSurfaceHigh, - start = androidx.compose.ui.geometry.Offset(0f, midY), - end = androidx.compose.ui.geometry.Offset(size.width, midY), + gridColor, + start = Offset(0f, midY), + end = Offset(size.width, midY), strokeWidth = 1.dp.toPx(), ) diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/QuaternionCubeView.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/QuaternionCubeView.kt index de4da14..d443b45 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/QuaternionCubeView.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/QuaternionCubeView.kt @@ -6,11 +6,12 @@ import androidx.compose.foundation.layout.height import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset +import androidx.compose.material3.MaterialTheme +import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.mbientlab.metawear.app.core.QuaternionCube -import com.mbientlab.metawear.app.ui.theme.GlassAccent -import com.mbientlab.metawear.app.ui.theme.GlassSurfaceHigh +import com.mbientlab.metawear.app.ui.theme.Palette /** * Dependency-free 3D orientation cube: rotates a wireframe cube by the live @@ -27,6 +28,7 @@ fun QuaternionCubeView( modifier: Modifier = Modifier, height: Dp = 180.dp, ) { + val frameColor = MaterialTheme.colorScheme.outlineVariant Canvas( modifier = modifier .fillMaxWidth() @@ -46,7 +48,7 @@ fun QuaternionCubeView( val depth = ((vertices[a].z + vertices[b].z) / 2f / 1.75f).coerceIn(-1f, 1f) val t = (depth + 1f) / 2f drawLine( - color = GlassAccent.copy(alpha = 0.25f + 0.75f * t), + color = Palette.accent.copy(alpha = 0.25f + 0.75f * t), start = projected[a], end = projected[b], strokeWidth = (1f + 2f * t).dp.toPx(), @@ -55,9 +57,9 @@ fun QuaternionCubeView( // Vertex dots for the front-most corners. vertices.forEachIndexed { index, v -> if (v.z > 0f) { - drawCircle(GlassAccent, radius = 3.dp.toPx(), center = projected[index]) + drawCircle(Palette.accent, radius = 3.dp.toPx(), center = projected[index]) } } - drawRect(GlassSurfaceHigh, style = androidx.compose.ui.graphics.drawscope.Stroke(1.dp.toPx())) + drawRect(frameColor, style = Stroke(1.dp.toPx())) } } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/SensorChartCard.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/SensorChartCard.kt new file mode 100644 index 0000000..c61da48 --- /dev/null +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/SensorChartCard.kt @@ -0,0 +1,184 @@ +package com.mbientlab.metawear.app.ui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Tag +import androidx.compose.material.icons.outlined.Schedule +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.VerticalDivider +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.mbientlab.metawear.app.core.AnyChartSample +import com.mbientlab.metawear.app.core.AxisStyle +import com.mbientlab.metawear.app.ui.theme.ChannelColors +import com.mbientlab.metawear.app.ui.theme.FourChannelColors +import com.mbientlab.metawear.app.ui.theme.Palette +import java.text.NumberFormat +import kotlinx.coroutines.delay +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant + +/** Chart line colors for a given axis style (four-channel outputs lead with purple). */ +fun AxisStyle.chartColors(): List = if (chartChannels == 4) FourChannelColors else ChannelColors + +/** + * Compact stats strip above the chart cards: elapsed session time (ticking + * once a second) and the running total sample count across all channels. + */ +@Composable +fun SessionStatsBar(startedAt: Instant, totalSamples: Int) { + var elapsedSeconds by remember(startedAt) { mutableIntStateOf(0) } + LaunchedEffect(startedAt) { + while (true) { + elapsedSeconds = ((Clock.System.now() - startedAt).inWholeSeconds).toInt() + delay(1_000) + } + } + Surface( + shape = MaterialTheme.shapes.medium, + color = MaterialTheme.colorScheme.surfaceContainer, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp, vertical = 8.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Stat(Icons.Outlined.Schedule, formatDuration(elapsedSeconds)) + VerticalDivider(modifier = Modifier.height(14.dp)) + Stat(Icons.Filled.Tag, "${NumberFormat.getIntegerInstance().format(totalSamples)} samples") + } + } +} + +@Composable +private fun Stat(icon: ImageVector, label: String) { + Row(horizontalArrangement = Arrangement.spacedBy(6.dp), verticalAlignment = Alignment.CenterVertically) { + Icon(icon, contentDescription = null, tint = Palette.accent, modifier = Modifier.size(16.dp)) + Text(label, style = MaterialTheme.typography.labelMedium, fontFamily = FontFamily.Monospace) + } +} + +/** + * Row of small chips, one per channel, showing the latest value in the same + * color as the chart line — precise numbers without eyeballing the trace. + */ +@Composable +fun SensorReadoutChips(style: AxisStyle, latest: AnyChartSample?, colors: List = style.chartColors()) { + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { + style.labels.take(style.chartChannels).forEachIndexed { index, label -> + val color = colors.getOrElse(index) { colors.last() } + val value = latest?.channel(index) + Surface(shape = CircleShape, color = color.copy(alpha = 0.12f)) { + Row( + modifier = Modifier.padding(horizontal = 8.dp, vertical = 3.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Box(Modifier.size(6.dp).background(color, CircleShape)) + Text( + label, + style = MaterialTheme.typography.labelSmall, + fontWeight = FontWeight.SemiBold, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Text( + value?.let { formatMeasurement(it, style.unit) } ?: "—", + style = MaterialTheme.typography.labelSmall, + fontFamily = FontFamily.Monospace, + color = if (value != null) MaterialTheme.colorScheme.onSurface else MaterialTheme.colorScheme.outline, + ) + } + } + } + } +} + +/** + * One sensor's card: icon + title with the effective rate on the right, the + * per-channel readout chips, then the line chart (or a "waiting" placeholder + * until the first samples land). + */ +@Composable +fun SensorChartCard( + title: String, + icon: ImageVector, + samples: List, + latest: AnyChartSample?, + style: AxisStyle, + rateLabel: String? = null, + trailing: (@Composable () -> Unit)? = null, + footer: (@Composable () -> Unit)? = null, +) { + BrandCard(verticalSpacing = 8.dp) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon(icon, contentDescription = null, tint = Palette.accent, modifier = Modifier.size(20.dp)) + Text(title, style = MaterialTheme.typography.titleSmall, modifier = Modifier.weight(1f)) + if (rateLabel != null) { + Text( + rateLabel, + style = MaterialTheme.typography.labelSmall, + fontFamily = FontFamily.Monospace, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + trailing?.invoke() + } + SensorReadoutChips(style = style, latest = latest) + if (samples.size < 2) { + Column( + modifier = Modifier + .fillMaxWidth() + .height(160.dp), + verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + CircularProgressIndicator(modifier = Modifier.size(24.dp), strokeWidth = 2.dp) + Text( + "Waiting for first sample…", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } else { + LineChart( + samples = samples, + channelCount = style.chartChannels, + colors = style.chartColors(), + yRange = style.yRange, + modifier = Modifier.padding(top = 4.dp), + height = 180.dp, + ) + } + footer?.invoke() + } +} diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/SensorIcons.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/SensorIcons.kt new file mode 100644 index 0000000..3f6beb8 --- /dev/null +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/SensorIcons.kt @@ -0,0 +1,39 @@ +package com.mbientlab.metawear.app.ui.components + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Cached +import androidx.compose.material.icons.filled.DeviceThermostat +import androidx.compose.material.icons.filled.Explore +import androidx.compose.material.icons.filled.OpenWith +import androidx.compose.material.icons.filled.RadioButtonChecked +import androidx.compose.material.icons.filled.Speed +import androidx.compose.material.icons.filled.Terrain +import androidx.compose.material.icons.filled.ViewInAr +import androidx.compose.material.icons.filled.WaterDrop +import androidx.compose.material.icons.filled.WbSunny +import androidx.compose.ui.graphics.vector.ImageVector +import com.mbientlab.metawear.app.core.SensorKey + +/** One Material icon per sensor family — shared by pickers, chart cards, and history rows. */ +val SensorKey.icon: ImageVector + get() = when (this) { + SensorKey.ACCELEROMETER -> Icons.Filled.OpenWith + SensorKey.GYROSCOPE -> Icons.Filled.Cached + SensorKey.MAGNETOMETER -> Icons.Filled.Explore + SensorKey.TEMPERATURE -> Icons.Filled.DeviceThermostat + SensorKey.HUMIDITY -> Icons.Filled.WaterDrop + SensorKey.PRESSURE, SensorKey.PRESSURE_STREAMED -> Icons.Filled.Speed + SensorKey.ALTITUDE -> Icons.Filled.Terrain + SensorKey.AMBIENT_LIGHT -> Icons.Filled.WbSunny + else -> Icons.Filled.ViewInAr // sensor-fusion outputs + } + +/** + * Icon for a saved session, recovered from its capture-time label + * ("Gyroscope · ±2000 dps · 25 Hz" → gyroscope). Falls back to a generic + * record glyph for labels that don't map (foreign recoveries, legacy rows). + */ +fun sensorIconForLabel(label: String?): ImageVector { + val head = label?.substringBefore(" · ") ?: return Icons.Filled.RadioButtonChecked + return SensorKey.entries.firstOrNull { it.title == head }?.icon ?: Icons.Filled.RadioButtonChecked +} diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/StatusChips.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/StatusChips.kt new file mode 100644 index 0000000..5346a49 --- /dev/null +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/StatusChips.kt @@ -0,0 +1,247 @@ +package com.mbientlab.metawear.app.ui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.TrendingUp +import androidx.compose.material.icons.filled.Battery0Bar +import androidx.compose.material.icons.filled.Battery2Bar +import androidx.compose.material.icons.filled.Battery3Bar +import androidx.compose.material.icons.filled.Battery5Bar +import androidx.compose.material.icons.filled.BatteryFull +import androidx.compose.material.icons.filled.BluetoothDisabled +import androidx.compose.material.icons.filled.BluetoothSearching +import androidx.compose.material.icons.filled.Circle +import androidx.compose.material.icons.filled.Download +import androidx.compose.material.icons.filled.FiberManualRecord +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material.icons.outlined.CheckCircle +import androidx.compose.material.icons.outlined.Circle +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.FilledTonalButton +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.mbientlab.metawear.DeviceState +import com.mbientlab.metawear.app.core.BandwidthAdvisor +import com.mbientlab.metawear.app.ui.theme.Palette +import com.mbientlab.metawear.app.ui.theme.forText +import com.mbientlab.metawear.model.BatteryState + +/** Compact tonal status indicator: tinted capsule with an icon and a short label. */ +@Composable +fun StatusChip( + text: String, + tint: Color, + modifier: Modifier = Modifier, + icon: ImageVector? = null, + leading: (@Composable () -> Unit)? = null, + trailing: (@Composable () -> Unit)? = null, +) { + Surface( + modifier = modifier, + shape = CircleShape, + color = tint.copy(alpha = 0.15f), + contentColor = tint.forText(), + ) { + Row( + modifier = Modifier.padding(horizontal = 10.dp, vertical = 5.dp), + horizontalArrangement = Arrangement.spacedBy(6.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + if (leading != null) { + leading() + } else if (icon != null) { + Icon(icon, contentDescription = null, modifier = Modifier.size(14.dp)) + } + Text(text, style = MaterialTheme.typography.labelMedium, maxLines = 1) + trailing?.invoke() + } + } +} + +/** How a device on the scan screen relates to this phone right now. */ +sealed interface DeviceConnectionStatus { + data class Connected(val rssi: Int?) : DeviceConnectionStatus + data object Connecting : DeviceConnectionStatus + data class Available(val rssi: Int?) : DeviceConnectionStatus + data object Offline : DeviceConnectionStatus +} + +/** + * Scan-row status: connected (green, with bars), connecting (gold + spinner), + * available (blue bars + dBm), or offline (gray). + */ +@Composable +fun DeviceStatusChip(status: DeviceConnectionStatus) { + when (status) { + is DeviceConnectionStatus.Connected -> StatusChip( + text = "Connected", + tint = Palette.success, + icon = Icons.Filled.Circle, + trailing = status.rssi?.let { { RssiBars(dBm = it, tint = Palette.success) } }, + ) + DeviceConnectionStatus.Connecting -> StatusChip( + text = "Connecting…", + tint = Palette.warning, + leading = { + CircularProgressIndicator( + modifier = Modifier.size(12.dp), + strokeWidth = 1.5.dp, + color = Palette.warning, + ) + }, + ) + is DeviceConnectionStatus.Available -> StatusChip( + text = status.rssi?.let { "$it dBm" } ?: "Available", + tint = Palette.info, + leading = { RssiBars(dBm = status.rssi) }, + ) + DeviceConnectionStatus.Offline -> StatusChip( + text = "Offline", + tint = Palette.neutral, + icon = Icons.Outlined.Circle, + ) + } +} + +/** + * Four signal bars. Defaults to the info blue so the bars match the blue + * capsule they usually sit in — deliberately NOT the brand orange. + */ +@Composable +fun RssiBars(dBm: Int?, tint: Color = Palette.info) { + val bars = when { + dBm == null -> 0 + dBm < -90 -> 1 + dBm < -75 -> 2 + dBm < -60 -> 3 + else -> 4 + } + val off = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.3f) + Row( + horizontalArrangement = Arrangement.spacedBy(2.dp), + verticalAlignment = Alignment.Bottom, + ) { + repeat(4) { index -> + Box( + modifier = Modifier + .width(3.dp) + .height((6 + index * 3).dp) + .background(if (index < bars) tint else off, CircleShape), + ) + } + } +} + +/** Live link quality: bars + dBm in the info blue. */ +@Composable +fun RssiChip(dBm: Int) { + StatusChip(text = "$dBm dBm", tint = Palette.info, leading = { RssiBars(dBm) }) +} + +/** + * Connection/activity state chip. [isLogging] forces the red "Logging" look + * for a board that is recording autonomously while the SDK reports idle. + */ +@Composable +fun StateChip(state: DeviceState, isLogging: Boolean = false) { + val (label, icon, tint) = when { + isLogging -> Triple("Logging", Icons.Filled.FiberManualRecord, Palette.danger) + state == DeviceState.Disconnected -> Triple("Disconnected", Icons.Filled.BluetoothDisabled, Palette.danger) + state == DeviceState.Connecting -> Triple("Connecting…", Icons.Filled.BluetoothSearching, Palette.warning) + state == DeviceState.Idle -> Triple("Idle", Icons.Outlined.CheckCircle, Palette.warning) + state == DeviceState.Streaming -> Triple("Streaming", Icons.AutoMirrored.Filled.TrendingUp, Palette.info) + state == DeviceState.Logging -> Triple("Logging", Icons.Filled.FiberManualRecord, Palette.danger) + state is DeviceState.Downloading -> + Triple("Downloading ${(state.progress * 100).toInt()}%", Icons.Filled.Download, Palette.accent) + else -> Triple("—", Icons.Outlined.Circle, Palette.neutral) + } + StatusChip(text = label, tint = tint, icon = icon) +} + +/** Battery charge chip, colored by level (coral < 15 %, gold < 35 %, green above). */ +@Composable +fun BatteryChip(battery: BatteryState?) { + val charge = battery?.charge + val icon = when { + charge == null || charge < 10 -> Icons.Filled.Battery0Bar + charge < 25 -> Icons.Filled.Battery2Bar + charge < 50 -> Icons.Filled.Battery3Bar + charge < 75 -> Icons.Filled.Battery5Bar + else -> Icons.Filled.BatteryFull + } + val tint = when { + charge == null -> Palette.neutral + charge < 15 -> Palette.danger + charge < 35 -> Palette.warning + else -> Palette.success + } + StatusChip(text = charge?.let { "$it%" } ?: "—", tint = tint, icon = icon) +} + +/** + * Small tonal pill for secondary facts (model name, firmware) in a header. + * [emphasized] switches to the primary-container tone (brand orange tint). + */ +@Composable +fun InfoPill(text: String, emphasized: Boolean = false, monospace: Boolean = false) { + Surface( + shape = CircleShape, + color = if (emphasized) MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.surfaceContainerHigh, + contentColor = if (emphasized) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSurfaceVariant, + ) { + Text( + text, + style = MaterialTheme.typography.labelMedium, + fontFamily = if (monospace) FontFamily.Monospace else null, + fontWeight = FontWeight.Medium, + modifier = Modifier.padding(horizontal = 10.dp, vertical = 5.dp), + ) + } +} + +/** + * Bandwidth advisor: shown only when the combined sample rate exceeds the + * BLE ceiling, with a one-tap "halve all rates" remedy. + */ +@Composable +fun BandwidthCard(aggregateHz: Double, onHalve: () -> Unit) { + if (aggregateHz <= BandwidthAdvisor.BLE_SAFE_CEILING_HZ) return + BrandCard(containerColor = Palette.warning.copy(alpha = 0.15f)) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon(Icons.Filled.Warning, contentDescription = null, tint = Palette.warning) + Column(Modifier.weight(1f)) { + Text("Combined rate ${aggregateHz.toInt()} Hz", style = MaterialTheme.typography.titleSmall) + Text( + "Above BLE limit — samples may drop.", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + FilledTonalButton(onClick = onHalve) { Text("Halve rates") } + } + } +} diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/controls/ControlsScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/controls/ControlsScreen.kt index 95aa7fc..3787157 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/controls/ControlsScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/controls/ControlsScreen.kt @@ -1,13 +1,26 @@ package com.mbientlab.metawear.app.ui.controls import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.material.icons.filled.Stop +import androidx.compose.material.icons.filled.Vibration +import androidx.compose.material.icons.filled.VolumeUp +import androidx.compose.material.icons.outlined.Lightbulb import androidx.compose.material3.Button import androidx.compose.material3.FilterChip +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Slider @@ -15,18 +28,23 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import com.mbientlab.metawear.app.ui.appViewModel -import com.mbientlab.metawear.app.ui.components.ErrorBanner -import com.mbientlab.metawear.app.ui.components.GlassCard +import com.mbientlab.metawear.app.ui.components.AppScaffold +import com.mbientlab.metawear.app.ui.components.BrandCard +import com.mbientlab.metawear.app.ui.components.Notice +import com.mbientlab.metawear.app.ui.components.SectionFooter import com.mbientlab.metawear.app.ui.components.SectionHeader -import com.mbientlab.metawear.app.ui.theme.GlassTextDim +import com.mbientlab.metawear.app.ui.theme.Palette import com.mbientlab.metawear.app.vm.ControlsViewModel import com.mbientlab.metawear.sensor.Led +/** LED (color, pattern, play/stop) and haptic (duty, pulse, motor/buzzer) controls. */ @Composable -fun ControlsScreen() { +fun ControlsScreen(onBack: () -> Unit) { val vm = appViewModel(::ControlsViewModel) val ledColor by vm.ledColor.collectAsState() val ledPattern by vm.ledPattern.collectAsState() @@ -34,80 +52,134 @@ fun ControlsScreen() { val motorPulseMs by vm.motorPulseMs.collectAsState() val lastError by vm.lastError.collectAsState() - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(10.dp), - ) { - item { Text("Controls", style = MaterialTheme.typography.headlineSmall) } - item { ErrorBanner(lastError) { vm.clearError() } } - - item { SectionHeader("LED") } - item { - GlassCard { - Text("Color", style = MaterialTheme.typography.labelMedium, color = GlassTextDim) - Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { - Led.Color.entries.forEach { color -> - FilterChip( - selected = ledColor == color, - onClick = { vm.setLedColor(color) }, - label = { Text(color.name.lowercase().replaceFirstChar { it.uppercase() }) }, - ) + AppScaffold( + title = "Controls", + onBack = onBack, + notice = lastError?.let { Notice(it) { vm.clearError() } }, + ) { padding -> + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues( + start = 16.dp, + end = 16.dp, + top = padding.calculateTopPadding() + 4.dp, + bottom = padding.calculateBottomPadding() + 24.dp, + ), + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { + item { SectionHeader("LED") } + item { + BrandCard(verticalSpacing = 10.dp) { + Row(horizontalArrangement = Arrangement.spacedBy(10.dp), verticalAlignment = Alignment.CenterVertically) { + Icon(Icons.Outlined.Lightbulb, contentDescription = null, tint = Palette.accent) + Text("Pattern playback", style = MaterialTheme.typography.titleSmall) } - } - Text("Pattern", style = MaterialTheme.typography.labelMedium, color = GlassTextDim) - Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { - ControlsViewModel.PatternPreset.entries.forEach { preset -> - FilterChip( - selected = ledPattern == preset, - onClick = { vm.setLedPattern(preset) }, - label = { Text(preset.title) }, - ) + ChoiceRow("Color") { + Led.Color.entries.forEach { color -> + FilterChip( + selected = ledColor == color, + onClick = { vm.setLedColor(color) }, + label = { Text(color.name.lowercase().replaceFirstChar { it.uppercase() }) }, + ) + } } - } - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - Button(onClick = { vm.playLed() }, modifier = Modifier.weight(1f)) { Text("Play") } - OutlinedButton(onClick = { vm.stopLedPlayback() }, modifier = Modifier.weight(1f)) { - Text("Stop") + ChoiceRow("Pattern") { + ControlsViewModel.PatternPreset.entries.forEach { preset -> + FilterChip( + selected = ledPattern == preset, + onClick = { vm.setLedPattern(preset) }, + label = { Text(preset.title) }, + ) + } + } + HorizontalDivider() + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Button(onClick = { vm.playLed() }, modifier = Modifier.weight(1f)) { + Icon(Icons.Filled.PlayArrow, contentDescription = null, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(6.dp)) + Text("Play") + } + OutlinedButton(onClick = { vm.stopLedPlayback() }, modifier = Modifier.weight(1f)) { + Icon(Icons.Filled.Stop, contentDescription = null, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(6.dp)) + Text("Stop") + } } } } - } - item { SectionHeader("Haptic") } - item { - GlassCard { - Text("Strength $motorDuty%", style = MaterialTheme.typography.labelMedium, color = GlassTextDim) - Slider( - value = motorDuty.toFloat(), - onValueChange = { vm.setMotorDuty(it.toInt()) }, - valueRange = 0f..100f, - ) - Text("Pulse $motorPulseMs ms", style = MaterialTheme.typography.labelMedium, color = GlassTextDim) - Slider( - value = motorPulseMs.toFloat(), - onValueChange = { vm.setMotorPulseMs(it.toInt()) }, - valueRange = 50f..2000f, - ) - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - Button(onClick = { vm.pulseMotor() }, modifier = Modifier.weight(1f)) { Text("Buzz Motor") } - OutlinedButton(onClick = { vm.pulseBuzzer() }, modifier = Modifier.weight(1f)) { - Text("Buzzer") + item { SectionHeader("Haptic") } + item { + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { + BrandCard(verticalSpacing = 4.dp) { + SliderRow( + label = "Duty cycle", + value = "$motorDuty%", + ) { + Slider( + value = motorDuty.toFloat(), + onValueChange = { vm.setMotorDuty(it.toInt()) }, + valueRange = 0f..100f, + steps = 9, + ) + } + SliderRow( + label = "Pulse width", + value = "$motorPulseMs ms", + ) { + Slider( + value = motorPulseMs.toFloat(), + onValueChange = { vm.setMotorPulseMs(it.toInt()) }, + valueRange = 50f..2000f, + ) + } + HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp)) + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + ) { + Button(onClick = { vm.pulseMotor() }, modifier = Modifier.weight(1f)) { + Icon(Icons.Filled.Vibration, contentDescription = null, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(6.dp)) + Text("Motor") + } + OutlinedButton(onClick = { vm.pulseBuzzer() }, modifier = Modifier.weight(1f)) { + Icon(Icons.Filled.VolumeUp, contentDescription = null, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(6.dp)) + Text("Buzzer") + } + } } + SectionFooter("MMR/MMRL boards carry a motor; MMC/MMS route this to the buzzer output.") } - Text( - "MMR/MMRL boards carry a motor; MMC/MMS route this to the buzzer output.", - style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, - ) } } } } + +@Composable +private fun ChoiceRow(label: String, chips: @Composable () -> Unit) { + Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { + Text(label, style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onSurfaceVariant) + Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { chips() } + } +} + +@Composable +private fun SliderRow(label: String, value: String, slider: @Composable () -> Unit) { + Column { + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { + Text(label, style = MaterialTheme.typography.bodyMedium) + Text( + value, + style = MaterialTheme.typography.bodyMedium, + fontFamily = FontFamily.Monospace, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + slider() + } +} diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/device/DeviceDetailScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/device/DeviceDetailScreen.kt index ee66d69..c1fe49b 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/device/DeviceDetailScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/device/DeviceDetailScreen.kt @@ -1,151 +1,303 @@ package com.mbientlab.metawear.app.ui.device +import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.TrendingUp +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Download +import androidx.compose.material.icons.filled.History +import androidx.compose.material.icons.filled.Inbox +import androidx.compose.material.icons.filled.RadioButtonChecked +import androidx.compose.material.icons.filled.Tune +import androidx.compose.material.icons.outlined.Cancel +import androidx.compose.material.icons.outlined.Info +import androidx.compose.material.icons.outlined.Lightbulb +import androidx.compose.material.icons.outlined.Settings +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.FilledTonalButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import com.mbientlab.metawear.DeviceState +import com.mbientlab.metawear.app.data.LogSessionRecord +import com.mbientlab.metawear.app.ui.appContainer import com.mbientlab.metawear.app.ui.appViewModel -import com.mbientlab.metawear.app.ui.components.ErrorBanner -import com.mbientlab.metawear.app.ui.components.GlassCard -import com.mbientlab.metawear.app.ui.components.LabeledValue -import com.mbientlab.metawear.app.ui.components.SectionHeader -import com.mbientlab.metawear.app.ui.components.StateBadge -import com.mbientlab.metawear.app.ui.theme.GlassTextDim +import com.mbientlab.metawear.app.ui.components.AppScaffold +import com.mbientlab.metawear.app.ui.components.BatteryChip +import com.mbientlab.metawear.app.ui.components.BrandCard +import com.mbientlab.metawear.app.ui.components.BrandCardShape +import com.mbientlab.metawear.app.ui.components.InfoPill +import com.mbientlab.metawear.app.ui.components.Notice +import com.mbientlab.metawear.app.ui.components.StateChip +import com.mbientlab.metawear.app.ui.theme.Palette import com.mbientlab.metawear.app.vm.DeviceViewModel -/** One navigable feature row of the device hub. */ -data class FeatureDestination(val title: String, val subtitle: String, val route: String) +/** One activity tile of the device hub. */ +private data class ActivityTile(val title: String, val icon: ImageVector, val route: String) -private val features = listOf( - FeatureDestination("Live Stream", "Charts, xyz readout, effective Hz", "stream"), - FeatureDestination("Logging", "Record to flash, download sessions", "logging"), - FeatureDestination("Sessions", "History and CSV export", "sessions"), - FeatureDestination("Controls", "LED patterns and haptic pulses", "controls"), - FeatureDestination("Device Info", "Model, firmware, modules", "info"), - FeatureDestination("Settings", "Name, advertising, factory reset", "settings"), - FeatureDestination("Firmware", "Check and install updates", "firmware"), +private val activityTiles = listOf( + ActivityTile("Live Stream", Icons.AutoMirrored.Filled.TrendingUp, "stream"), + ActivityTile("Logging", Icons.Filled.RadioButtonChecked, "logging"), + ActivityTile("Controls", Icons.Filled.Tune, "controls"), + ActivityTile("Device Info", Icons.Outlined.Info, "info"), + ActivityTile("Settings", Icons.Outlined.Settings, "settings"), + ActivityTile("Session History", Icons.Filled.History, "sessions"), ) +/** + * Device hub: identity header (MAC, state, battery, model, firmware) above a + * grid of activity tiles. Identify/Disconnect live in the top bar. + */ @Composable -fun DeviceDetailScreen(onNavigate: (String) -> Unit, onDisconnected: () -> Unit) { +fun DeviceDetailScreen(onNavigate: (String) -> Unit, onDisconnected: () -> Unit, onBack: () -> Unit) { val vm = appViewModel(::DeviceViewModel) + val container = appContainer() val state by vm.state.collectAsState() val info by vm.deviceInfo.collectAsState() val battery by vm.battery.collectAsState() + val mac by vm.macAddress.collectAsState() val isConnecting by vm.isConnecting.collectAsState() val lastError by vm.lastError.collectAsState() val foreignLog by vm.foreignLog.collectAsState() val foreignStatus by vm.foreignStatus.collectAsState() + val records by container.logSessions.records.collectAsState() - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(10.dp), - ) { - item { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, + val deviceId = vm.device?.identifier + val isDeviceLogging = records.any { + it.deviceId == deviceId && it.status == LogSessionRecord.Status.RUNNING + } + val notice = lastError?.let { Notice(it) { vm.clearError() } } + ?: foreignStatus?.let { Notice(it) { vm.clearForeignStatus() } } + + Box(Modifier.fillMaxSize()) { + AppScaffold( + title = vm.displayName, + onBack = onBack, + notice = notice, + actions = { + IconButton(onClick = { vm.identify() }, enabled = state != DeviceState.Disconnected) { + Icon(Icons.Outlined.Lightbulb, contentDescription = "Identify") + } + IconButton( + onClick = { + vm.disconnect() + onDisconnected() + }, + enabled = state != DeviceState.Disconnected, + ) { + Icon(Icons.Outlined.Cancel, contentDescription = "Disconnect", tint = Palette.danger) + } + }, + ) { padding -> + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues( + start = 16.dp, + end = 16.dp, + top = padding.calculateTopPadding() + 4.dp, + bottom = padding.calculateBottomPadding() + 24.dp, + ), + verticalArrangement = Arrangement.spacedBy(12.dp), ) { - Column(Modifier.weight(1f)) { - Text(vm.displayName, style = MaterialTheme.typography.headlineSmall) - Text( - vm.device?.identifier ?: "", - style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, - ) + item { + BrandCard(verticalSpacing = 12.dp) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + mac ?: deviceId ?: "—", + style = MaterialTheme.typography.titleSmall, + fontFamily = FontFamily.Monospace, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.weight(1f), + ) + StateChip(state = state, isLogging = isDeviceLogging) + } + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + BatteryChip(battery) + info?.let { device -> + InfoPill(device.model.displayName, emphasized = true) + InfoPill("fw ${device.firmwareRevision}", monospace = true) + } + } + if (state == DeviceState.Disconnected && !isConnecting) { + OutlinedButton(onClick = { vm.reconnect() }) { Text("Reconnect") } + } + } } - if (isConnecting) CircularProgressIndicator(Modifier.padding(4.dp)) else StateBadge(state) - } - } - - item { ErrorBanner(lastError) { vm.clearError() } } - foreignLog?.let { orphan -> - item { - GlassCard { - Text("Log from another session", style = MaterialTheme.typography.titleSmall) - Text( - buildString { - append("This board carries a log this app didn't start") - if (orphan.entryCount > 0) append(" (${orphan.entryCount} entries)") - if (orphan.isActivelyLogging) append(" — and it is still recording") - append(". Recover it into session history, or discard it.") - }, - style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, - ) - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - TextButton(onClick = { vm.downloadForeignLog() }) { Text("Download") } - TextButton(onClick = { vm.discardForeignLog() }) { Text("Discard") } + foreignLog?.let { orphan -> + item { + BrandCard { + Row( + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + if (orphan.isActivelyLogging) Icons.Filled.RadioButtonChecked else Icons.Filled.Inbox, + contentDescription = null, + tint = Palette.accent, + ) + Column(Modifier.weight(1f)) { + Text( + if (orphan.isActivelyLogging) "Logging in progress from another device" + else "Data on this board", + style = MaterialTheme.typography.titleSmall, + ) + Text( + when { + orphan.isActivelyLogging && orphan.entryCount > 0 -> + "${orphan.entryCount} entries so far" + orphan.isActivelyLogging -> + "Recording — first entries are still buffering to flash" + else -> "${orphan.entryCount} entries recorded" + }, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + Text( + "Downloading saves the recording to Session History" + + (if (orphan.isActivelyLogging) " and stops the logging." else ".") + + " Sensor types are reconstructed from the board's own logger metadata.", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + FilledTonalButton(onClick = { vm.downloadForeignLog() }) { + Icon(Icons.Filled.Download, contentDescription = null, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(6.dp)) + Text(if (orphan.isActivelyLogging) "Stop & Download" else "Download") + } + TextButton( + onClick = { vm.discardForeignLog() }, + colors = ButtonDefaults.textButtonColors(contentColor = Palette.danger), + ) { + Icon(Icons.Filled.Delete, contentDescription = null, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(6.dp)) + Text("Discard") + } + } + } } } - } - } - foreignStatus?.let { status -> - item { - GlassCard { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - status, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.weight(1f), - ) - TextButton(onClick = { vm.clearForeignStatus() }) { Text("OK") } + + items(activityTiles.chunked(2).size) { rowIndex -> + val pair = activityTiles.chunked(2)[rowIndex] + Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { + pair.forEach { tile -> + ActivityTileCard( + tile = tile, + modifier = Modifier.weight(1f), + onClick = { onNavigate(tile.route) }, + ) + } + if (pair.size == 1) Spacer(Modifier.weight(1f)) } } } } - item { - GlassCard { - LabeledValue("Model", info?.modelNumber ?: "—") - LabeledValue("Firmware", info?.firmwareRevision ?: "—") - LabeledValue( - "Battery", - battery?.let { "${it.charge}% · ${it.voltage} mV" } ?: "—", - ) - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { - TextButton(onClick = { vm.identify() }) { Text("Identify (LED)") } - if (state == DeviceState.Disconnected) { - TextButton(onClick = { vm.reconnect() }) { Text("Reconnect") } - } else { - TextButton(onClick = { - vm.disconnect() - onDisconnected() - }) { Text("Disconnect") } - } - } - } + if (isConnecting) { + ConnectingOverlay(deviceName = vm.displayName) } + } +} + +/** Activity tile: brand-orange icon over a title, one color across the grid. */ +@Composable +private fun ActivityTileCard(tile: ActivityTile, modifier: Modifier = Modifier, onClick: () -> Unit) { + Card( + onClick = onClick, + modifier = modifier.heightIn(min = 120.dp), + shape = BrandCardShape, + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceContainerLow), + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + Icon(tile.icon, contentDescription = null, tint = Palette.accent, modifier = Modifier.size(28.dp)) + Text(tile.title, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold) + } + } +} - item { SectionHeader("Features") } - items(features.size) { index -> - val feature = features[index] - GlassCard(modifier = Modifier.clickable { onNavigate(feature.route) }) { - Text(feature.title, style = MaterialTheme.typography.titleSmall) - Text(feature.subtitle, style = MaterialTheme.typography.bodySmall, color = GlassTextDim) +/** + * Scrim shown while the BLE handshake runs. Absorbs taps so a second + * connection can't be started against a device that isn't ready yet. + */ +@Composable +private fun ConnectingOverlay(deviceName: String) { + Box( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.45f)) + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null, + onClick = {}, + ), + contentAlignment = Alignment.Center, + ) { + Card(shape = MaterialTheme.shapes.large) { + Column( + modifier = Modifier.padding(horizontal = 28.dp, vertical = 24.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(14.dp), + ) { + CircularProgressIndicator() + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Text("Connecting", style = MaterialTheme.typography.titleMedium) + Spacer(Modifier.height(2.dp)) + Text( + deviceName, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } } } } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/device/DeviceInfoScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/device/DeviceInfoScreen.kt index 094a98f..80b00d9 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/device/DeviceInfoScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/device/DeviceInfoScreen.kt @@ -1,66 +1,105 @@ package com.mbientlab.metawear.app.ui.device import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import com.mbientlab.metawear.app.ui.appViewModel -import com.mbientlab.metawear.app.ui.components.GlassCard +import com.mbientlab.metawear.app.ui.components.AppScaffold +import com.mbientlab.metawear.app.ui.components.GroupCard import com.mbientlab.metawear.app.ui.components.LabeledValue import com.mbientlab.metawear.app.ui.components.SectionHeader +import com.mbientlab.metawear.app.ui.components.cardListItemColors import com.mbientlab.metawear.app.vm.DeviceViewModel +import com.mbientlab.metawear.model.ModuleInfo +import com.mbientlab.metawear.protocol.Module +/** + * Identity rows from the Device Information service, then every module the + * SDK knows about — present ones with their implementation/revision bytes, + * absent ones kept in the list as "—" so what's on this board is obvious. + */ @Composable -fun DeviceInfoScreen() { +fun DeviceInfoScreen(onBack: () -> Unit) { val vm = appViewModel(::DeviceViewModel) val info by vm.deviceInfo.collectAsState() val battery by vm.battery.collectAsState() val mac by vm.macAddress.collectAsState() val modules by vm.modules.collectAsState() - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(10.dp), - ) { - item { Text("Device Info", style = MaterialTheme.typography.headlineSmall) } - - item { - GlassCard { - LabeledValue("Manufacturer", info?.manufacturer ?: "—") - LabeledValue("Model", info?.model?.displayName ?: "—") - LabeledValue("Model number", info?.modelNumber ?: "—") - LabeledValue("Serial", info?.serialNumber ?: "—") - LabeledValue("Firmware", info?.firmwareRevision ?: "—") - LabeledValue("Hardware", info?.hardwareRevision ?: "—") - LabeledValue("MAC", mac ?: vm.device?.identifier ?: "—") - LabeledValue("Battery", battery?.let { "${it.charge}% · ${it.voltage} mV" } ?: "—") + AppScaffold(title = "Device Info", onBack = onBack) { padding -> + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues( + start = 16.dp, + end = 16.dp, + top = padding.calculateTopPadding() + 4.dp, + bottom = padding.calculateBottomPadding() + 24.dp, + ), + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { + item { SectionHeader("Identity") } + item { + GroupCard { + LabeledValue("Model", info?.modelNumber ?: "—") + HorizontalDivider() + LabeledValue("Manufacturer", info?.manufacturer ?: "—") + HorizontalDivider() + LabeledValue("Serial", info?.serialNumber ?: "—") + HorizontalDivider() + LabeledValue("Firmware", info?.firmwareRevision ?: "—") + HorizontalDivider() + LabeledValue("Hardware", info?.hardwareRevision ?: "—") + HorizontalDivider() + LabeledValue("MAC", mac ?: vm.device?.identifier ?: "—", monospace = true) + HorizontalDivider() + LabeledValue("Battery", battery?.let { "${it.charge}% · ${it.voltage} mV" } ?: "—") + } } - } - item { SectionHeader("Modules") } - item { - GlassCard { - val present = modules.values.filter { it.isPresent }.sortedBy { it.module.value } - if (present.isEmpty()) { - Text("No module table — connect first.", style = MaterialTheme.typography.bodySmall) - } else { - present.forEach { module -> - LabeledValue( - module.module.name.lowercase().replaceFirstChar { it.uppercase() }, - "impl ${module.implementation} · rev ${module.revision}", - ) + item { SectionHeader("Modules") } + item { + GroupCard { + Module.entries.forEachIndexed { index, module -> + if (index > 0) HorizontalDivider() + ModuleRow(module, modules[module]) } } } } } } + +/** One Modules row: name left; "impl 0xNN · rev N" or a muted "—" right. */ +@Composable +private fun ModuleRow(module: Module, info: ModuleInfo?) { + val name = module.name.lowercase().replace('_', ' ').replaceFirstChar { it.uppercase() } + ListItem( + headlineContent = { Text(name) }, + trailingContent = { + if (info != null && info.isPresent) { + Text( + "impl ${hex(info.implementation)} · rev ${info.revision}", + style = MaterialTheme.typography.bodyMedium, + fontFamily = FontFamily.Monospace, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } else { + Text("—", color = MaterialTheme.colorScheme.outline) + } + }, + colors = cardListItemColors(), + ) +} + +private fun hex(value: Int): String = "0x" + value.toString(16).uppercase().padStart(2, '0') diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/firmware/FirmwareScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/firmware/FirmwareScreen.kt index b2c3df4..1c0f70d 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/firmware/FirmwareScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/firmware/FirmwareScreen.kt @@ -1,122 +1,193 @@ package com.mbientlab.metawear.app.ui.firmware import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material3.Button +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.Download +import androidx.compose.material.icons.filled.Sync +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import com.mbientlab.metawear.app.ui.appViewModel -import com.mbientlab.metawear.app.ui.components.ErrorBanner -import com.mbientlab.metawear.app.ui.components.GlassCard +import com.mbientlab.metawear.app.ui.components.ActionRow +import com.mbientlab.metawear.app.ui.components.AppScaffold +import com.mbientlab.metawear.app.ui.components.GroupCard import com.mbientlab.metawear.app.ui.components.LabeledValue -import com.mbientlab.metawear.app.ui.theme.GlassGood -import com.mbientlab.metawear.app.ui.theme.GlassTextDim +import com.mbientlab.metawear.app.ui.components.Notice +import com.mbientlab.metawear.app.ui.components.SectionFooter +import com.mbientlab.metawear.app.ui.components.SectionHeader +import com.mbientlab.metawear.app.ui.components.cardListItemColors +import com.mbientlab.metawear.app.ui.theme.Palette +import com.mbientlab.metawear.app.ui.theme.forText import com.mbientlab.metawear.app.vm.FirmwareUpdateViewModel +import com.mbientlab.metawear.firmware.DFUProgress +/** Stand-alone firmware page (the same section also lives inside Settings). */ @Composable -fun FirmwareScreen() { +fun FirmwareScreen(onBack: () -> Unit) { val vm = appViewModel(::FirmwareUpdateViewModel) val phase by vm.phase.collectAsState() - val context = LocalContext.current - - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(10.dp), - ) { - item { Text("Firmware", style = MaterialTheme.typography.headlineSmall) } - - item { - GlassCard { - LabeledValue("Installed version", vm.currentVersion ?: "—") - } + AppScaffold( + title = "Firmware", + onBack = onBack, + notice = (phase as? FirmwareUpdateViewModel.Phase.Failed)?.let { Notice(it.message) { vm.reset() } }, + ) { padding -> + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues( + start = 16.dp, + end = 16.dp, + top = padding.calculateTopPadding() + 4.dp, + bottom = padding.calculateBottomPadding() + 24.dp, + ), + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { + item { SectionHeader("Firmware") } + item { FirmwareSection(vm) } } + } +} + +/** + * Installed version, a catalog check, and the over-the-air update with a + * live progress readout — every state of [FirmwareUpdateViewModel.Phase] + * mapped onto rows of one card. + */ +@Composable +fun FirmwareSection(vm: FirmwareUpdateViewModel) { + val phase by vm.phase.collectAsState() + val context = LocalContext.current - item { + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { + GroupCard { + LabeledValue("Installed", vm.currentVersion ?: "—", monospace = true) + HorizontalDivider() when (val p = phase) { - FirmwareUpdateViewModel.Phase.Unknown -> { - Button(onClick = { vm.checkForUpdate() }, modifier = Modifier.fillMaxWidth()) { - Text("Check for Update") - } - } + FirmwareUpdateViewModel.Phase.Unknown -> + ActionRow("Check for Updates", Icons.Filled.Sync, onClick = { vm.checkForUpdate() }) - FirmwareUpdateViewModel.Phase.Checking -> { - GlassCard { - Text("Checking the MbientLab catalog…", style = MaterialTheme.typography.bodyMedium) - LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) - } - } + FirmwareUpdateViewModel.Phase.Checking -> ListItem( + headlineContent = { Text("Checking for updates…", color = MaterialTheme.colorScheme.onSurfaceVariant) }, + trailingContent = { CircularProgressIndicator(modifier = Modifier.size(20.dp), strokeWidth = 2.dp) }, + colors = cardListItemColors(), + ) FirmwareUpdateViewModel.Phase.UpToDate -> { - GlassCard { - Text("Firmware is up to date", color = GlassGood, style = MaterialTheme.typography.titleSmall) - } + StatusRow("Up to date") + HorizontalDivider() + ActionRow("Check Again", Icons.Filled.Sync, onClick = { vm.checkForUpdate() }) } is FirmwareUpdateViewModel.Phase.UpdateAvailable -> { - GlassCard { - Text("Update available", style = MaterialTheme.typography.titleSmall) - LabeledValue("New version", p.build.firmwareRev) - LabeledValue("File", p.build.filename) - Button( - onClick = { vm.startUpdate(context) }, - modifier = Modifier.fillMaxWidth(), - ) { Text("Install Update") } - Text( - "Keep the board within range and charged. The board reboots into its " + - "bootloader during the flash.", - style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, - ) - } + LabeledValue("Latest", p.build.firmwareRev, monospace = true) + HorizontalDivider() + ActionRow("Update Firmware", Icons.Filled.Download, onClick = { vm.startUpdate(context) }) } - is FirmwareUpdateViewModel.Phase.Updating -> { - GlassCard { - Text( - p.progress.state.name.lowercase().replace('_', ' ') - .replaceFirstChar { it.uppercase() }, - style = MaterialTheme.typography.titleSmall, - ) - LinearProgressIndicator( - progress = { (p.progress.percentComplete / 100.0).toFloat().coerceIn(0f, 1f) }, - modifier = Modifier.fillMaxWidth(), - ) - Text( - "Part ${p.progress.currentPart}/${p.progress.totalParts}" + - if (p.progress.bytesPerSecond > 0) { - " · ${(p.progress.bytesPerSecond / 1024).toInt()} KB/s" - } else { - "" - }, - style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, - ) - } - } + is FirmwareUpdateViewModel.Phase.Updating -> ProgressRow(p.progress) - FirmwareUpdateViewModel.Phase.Completed -> { - GlassCard { - Text("Update complete", color = GlassGood, style = MaterialTheme.typography.titleSmall) - LabeledValue("Installed version", vm.currentVersion ?: "—") - } - } + FirmwareUpdateViewModel.Phase.Completed -> StatusRow("Updated") is FirmwareUpdateViewModel.Phase.Failed -> { - ErrorBanner(p.message) { vm.reset() } + ListItem( + headlineContent = { Text(p.message) }, + leadingContent = { Icon(Icons.Filled.Warning, contentDescription = null, tint = Palette.warning) }, + colors = cardListItemColors(), + ) + HorizontalDivider() + ActionRow("Try Again", Icons.Filled.Sync, onClick = { vm.checkForUpdate() }) } } } + when (phase) { + is FirmwareUpdateViewModel.Phase.Updating -> SectionFooter( + "Updating firmware. Keep the app open with the board nearby and powered — do not disconnect.", + ) + is FirmwareUpdateViewModel.Phase.UpdateAvailable -> SectionFooter( + "Downloads the latest firmware from MbientLab and installs it over Bluetooth. " + + "The board restarts when finished.", + ) + else -> Unit + } + } +} + +@Composable +private fun StatusRow(text: String) { + ListItem( + headlineContent = { Text("Status") }, + trailingContent = { + Row(horizontalArrangement = Arrangement.spacedBy(6.dp), verticalAlignment = Alignment.CenterVertically) { + Icon(Icons.Filled.CheckCircle, contentDescription = null, tint = Palette.success, modifier = Modifier.size(18.dp)) + Text(text, color = Palette.success.forText()) + } + }, + colors = cardListItemColors(), + ) +} + +/** Active DFU phase plus a determinate bar while bytes are transferring. */ +@Composable +private fun ProgressRow(progress: DFUProgress) { + val label = when (progress.state) { + DFUProgress.State.FETCHING_CATALOG -> "Checking catalog…" + DFUProgress.State.DOWNLOADING_FIRMWARE -> "Downloading firmware…" + DFUProgress.State.BOOTLOADER_HANDOFF -> "Preparing device…" + DFUProgress.State.SCANNING -> "Locating device…" + DFUProgress.State.CONNECTING -> "Connecting…" + DFUProgress.State.STARTING -> "Starting transfer…" + DFUProgress.State.VALIDATING -> "Validating…" + DFUProgress.State.UPLOADING -> "Installing…" + DFUProgress.State.DISCONNECTING -> "Finishing up…" + DFUProgress.State.COMPLETED -> "Complete" + DFUProgress.State.ABORTED -> "Aborted" + } + Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) { + Text(label, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.weight(1f)) + if (progress.totalParts > 1) { + Text( + "Step ${progress.currentPart} of ${progress.totalParts}", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + if (progress.state == DFUProgress.State.UPLOADING) { + Text( + "${progress.percentComplete.toInt()}%", + fontFamily = FontFamily.Monospace, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } else { + CircularProgressIndicator(modifier = Modifier.size(20.dp), strokeWidth = 2.dp) + } + } + if (progress.state == DFUProgress.State.UPLOADING) { + LinearProgressIndicator( + progress = { (progress.percentComplete / 100.0).toFloat().coerceIn(0f, 1f) }, + modifier = Modifier.fillMaxWidth(), + ) + } } } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/GroupLoggingScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/GroupLoggingScreen.kt index 674de8b..ea9c4e8 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/GroupLoggingScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/GroupLoggingScreen.kt @@ -2,16 +2,35 @@ package com.mbientlab.metawear.app.ui.logging import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.DoNotDisturbOn +import androidx.compose.material.icons.filled.FiberManualRecord +import androidx.compose.material.icons.filled.History +import androidx.compose.material.icons.filled.PauseCircle +import androidx.compose.material.icons.filled.RadioButtonUnchecked +import androidx.compose.material.icons.filled.Refresh +import androidx.compose.material.icons.filled.SaveAlt +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material.icons.filled.WarningAmber import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Checkbox +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -20,20 +39,25 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import com.mbientlab.metawear.app.core.SensorKey import com.mbientlab.metawear.app.core.SensorSelection import com.mbientlab.metawear.app.data.LogSessionRecord import com.mbientlab.metawear.app.ui.appContainer -import com.mbientlab.metawear.app.ui.components.GlassCard +import com.mbientlab.metawear.app.ui.components.ActionRow +import com.mbientlab.metawear.app.ui.components.AppScaffold +import com.mbientlab.metawear.app.ui.components.GroupCard +import com.mbientlab.metawear.app.ui.components.RssiChip +import com.mbientlab.metawear.app.ui.components.SectionFooter import com.mbientlab.metawear.app.ui.components.SectionHeader +import com.mbientlab.metawear.app.ui.components.cardListItemColors +import com.mbientlab.metawear.app.ui.components.formatClock import com.mbientlab.metawear.app.ui.stream.SensorConfigSection -import com.mbientlab.metawear.app.ui.theme.GlassError -import com.mbientlab.metawear.app.ui.theme.GlassGood -import com.mbientlab.metawear.app.ui.theme.GlassTextDim -import com.mbientlab.metawear.app.ui.theme.GlassWarn +import com.mbientlab.metawear.app.ui.theme.Palette +import com.mbientlab.metawear.app.ui.theme.forText import com.mbientlab.metawear.app.vm.GroupCaptureCoordinator import kotlinx.coroutines.launch @@ -44,7 +68,7 @@ import kotlinx.coroutines.launch * lives on the app container. */ @Composable -fun GroupLoggingScreen() { +fun GroupLoggingScreen(onBack: () -> Unit, onSessionHistory: () -> Unit) { val container = appContainer() val coordinator = container.groupCapture @@ -54,6 +78,7 @@ fun GroupLoggingScreen() { val demoMode by container.demoModeEnabled.collectAsState() val remembered by container.remembered.devices.collectAsState() val records by container.logSessions.records.collectAsState() + val rssiById by container.scanner.advertisementRssi.collectAsState() var selectedIds by remember { mutableStateOf(setOf()) } var selections by remember { mutableStateOf(listOf(SensorSelection(SensorKey.ACCELEROMETER))) } @@ -78,141 +103,201 @@ fun GroupLoggingScreen() { ) } + fun isPending(status: LogSessionRecord.Status) = + status == LogSessionRecord.Status.RUNNING || status == LogSessionRecord.Status.STOPPED + // Records with a group id are the durable "fleet recording" signal // (they survive app restarts). - val activeGroupRecords = records.filter { - it.groupID != null && - (it.status == LogSessionRecord.Status.RUNNING || it.status == LogSessionRecord.Status.STOPPED) + val activeGroupRecords = records.filter { it.groupID != null && isPending(it.status) } + val activeByBoard = activeGroupRecords.groupBy { it.deviceId } + .entries.sortedBy { entry -> entry.value.minOf { it.startDate } } + val anyRunning = activeGroupRecords.any { it.status == LogSessionRecord.Status.RUNNING } + + // Hide "Last Run" when it would only duplicate the active-group section + // (a fully successful start pass: every board it lists is already shown, + // recording, under "Logging In Progress"). + val lastRunIsRedundant = !isBusy && activeGroupRecords.isNotEmpty() && + boards.all { it.phase == GroupCaptureCoordinator.BoardPhase.Logging } + val showProgress = (isBusy || boards.isNotEmpty()) && !lastRunIsRedundant + val failed = boards.filter { it.phase is GroupCaptureCoordinator.BoardPhase.Failed } + val savedAny = boards.any { + it.phase is GroupCaptureCoordinator.BoardPhase.Saved || it.phase is GroupCaptureCoordinator.BoardPhase.SavedWithIssues } - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(10.dp), - ) { - item { Text("Group Logging", style = MaterialTheme.typography.headlineSmall) } - item { - Text( - "Log the same sensors across several boards at once. Boards are armed one at " + - "a time; each keeps recording on its own until you collect.", - style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, - ) - } + AppScaffold(title = "Group Logging", onBack = onBack) { padding -> + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues( + start = 16.dp, + end = 16.dp, + top = padding.calculateTopPadding() + 4.dp, + bottom = padding.calculateBottomPadding() + 24.dp, + ), + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { + if (showProgress) { + item { SectionHeader(if (isBusy) "Working…" else "Last Run") } + item { + GroupCard { + boards.forEachIndexed { index, board -> + if (index > 0) HorizontalDivider() + ListItem( + headlineContent = { Text(board.name) }, + supportingContent = { Text(phaseText(board.phase)) }, + leadingContent = { PhaseIcon(board.phase) }, + colors = cardListItemColors(), + ) + } + if (!isBusy && savedAny) { + HorizontalDivider() + ActionRow("View Saved Sessions", Icons.Filled.History, onClick = onSessionHistory) + } + if (!isBusy && lastPass == GroupCaptureCoordinator.PassKind.COLLECT && failed.isNotEmpty()) { + HorizontalDivider() + ActionRow( + "Retry Failed Download${if (failed.size == 1) "" else "s"}", + Icons.Filled.Refresh, + onClick = { + val retry = members(failed.map { it.id }) + container.appScope.launch { coordinator.stopAndDownloadAll(retry) } + }, + ) + } + } + } + if (!isBusy) { + item { + SectionFooter( + "Skipped boards keep their data — collect them here later, or connect to one " + + "directly and use its Logging screen.", + ) + } + } + } - item { SectionHeader("Boards") } - if (candidates.isEmpty()) { - item { - GlassCard { - Text( - "No boards yet — connect to a board once (or enable demo mode) so it " + - "appears here.", - style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, + if (activeGroupRecords.isNotEmpty()) { + item { SectionHeader(if (anyRunning) "Logging In Progress" else "Ready To Collect") } + item { + GroupCard { + activeByBoard.forEachIndexed { index, (deviceId, boardRecords) -> + if (index > 0) HorizontalDivider() + val isRunning = boardRecords.any { it.status == LogSessionRecord.Status.RUNNING } + ListItem( + headlineContent = { Text(container.displayNameFor(deviceId) ?: deviceId) }, + supportingContent = { + Text( + if (isRunning) { + "${boardRecords.size} sensor${if (boardRecords.size == 1) "" else "s"} · " + + "since ${boardRecords.minOf { it.startDate }.formatClock()}" + } else { + "Stopped — awaiting download" + }, + ) + }, + leadingContent = { + Icon( + if (isRunning) Icons.Filled.FiberManualRecord else Icons.Filled.PauseCircle, + contentDescription = null, + tint = if (isRunning) Palette.danger else Palette.warning, + ) + }, + colors = cardListItemColors(), + ) + } + HorizontalDivider() + ActionRow( + if (anyRunning) "Stop & Download All" else "Download All", + Icons.Filled.SaveAlt, + onClick = { showStopDialog = true }, + enabled = !isBusy, + ) + } + } + item { + SectionFooter( + "The boards record on their own — you can close the app or leave. " + + "Come back here to collect everything at once.", ) } - } - } - items(candidates, key = { it.first }) { (id, name) -> - GlassCard { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Column(Modifier.weight(1f)) { - Text(name, style = MaterialTheme.typography.titleSmall) - Text(id, style = MaterialTheme.typography.bodySmall, color = GlassTextDim) - if (records.any { - it.deviceId == id && - ( - it.status == LogSessionRecord.Status.RUNNING || - it.status == LogSessionRecord.Status.STOPPED + } else if (!isBusy) { + item { SectionHeader("Boards") } + if (candidates.isEmpty()) { + item { + SectionFooter( + "No boards available — connect to a board once so it is remembered, or enable demo mode.", + ) + } + } else { + item { + GroupCard { + candidates.forEachIndexed { index, (id, name) -> + if (index > 0) HorizontalDivider() + val hasWaiting = records.any { it.deviceId == id && isPending(it.status) } + ListItem( + headlineContent = { Text(name) }, + supportingContent = { + Column { + Text(id, fontFamily = FontFamily.Monospace) + if (hasWaiting) { + Text("Has a session waiting — download it first", color = Palette.warning.forText()) + } + } + }, + leadingContent = { + Checkbox( + checked = id in selectedIds, + onCheckedChange = { checked -> + selectedIds = if (checked) selectedIds + id else selectedIds - id + }, ) + }, + trailingContent = rssiById[id]?.let { rssi -> { RssiChip(rssi) } }, + colors = cardListItemColors(), + ) } - ) { - Text( - "Has a session waiting", - style = MaterialTheme.typography.labelSmall, - color = GlassWarn, - ) } } - Checkbox( - checked = id in selectedIds, - onCheckedChange = { checked -> - selectedIds = if (checked) selectedIds + id else selectedIds - id - }, + } + item { + SectionFooter( + "Every selected board records the same sensors. Boards are set up one at a time and keep " + + "logging on their own — no connection needed while they record.", ) } - } - } - - item { SectionHeader("Shared sensors") } - item { - SensorConfigSection( - modules = emptyMap(), // fleet-wide config: no single module table to gate on - selections = selections, - onSelectionsChange = { selections = it }, - loggingMode = true, - // The multichannel thermometer and the humidity readable vary - // per board generation — keep the shared group config to the - // kinds every board runs identically. - excludeKeys = setOf(SensorKey.TEMPERATURE, SensorKey.HUMIDITY), - ) - } - - item { - Button( - onClick = { - val chosen = members(selectedIds) - container.appScope.launch { coordinator.startAll(chosen, selections) } - }, - enabled = !isBusy && selectedIds.isNotEmpty() && selections.isNotEmpty(), - modifier = Modifier.fillMaxWidth(), - ) { Text("Start Logging All (${selectedIds.size})") } - } - if (activeGroupRecords.isNotEmpty()) { - item { SectionHeader("Recording group") } - item { - GlassCard { - Text( - "${activeGroupRecords.map { it.deviceId }.distinct().size} board(s) have " + - "group sessions pending.", - style = MaterialTheme.typography.bodySmall, + item { + SensorConfigSection( + modules = emptyMap(), // fleet-wide config: no single module table to gate on + selections = selections, + onSelectionsChange = { selections = it }, + loggingMode = true, + // The multichannel thermometer and the humidity readable vary + // per board generation — keep the shared group config to the + // kinds every board runs identically. + excludeKeys = setOf(SensorKey.TEMPERATURE, SensorKey.HUMIDITY), ) - Button( - onClick = { showStopDialog = true }, - enabled = !isBusy, - modifier = Modifier.fillMaxWidth(), - ) { Text("Stop & Download All") } } - } - } - if (boards.isNotEmpty()) { - item { SectionHeader("Progress") } - items(boards, key = { it.id }) { board -> - GlassCard { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Text(board.name, style = MaterialTheme.typography.titleSmall) - PhaseText(board.phase) - } - } - } - val failed = boards.filter { it.phase is GroupCaptureCoordinator.BoardPhase.Failed } - if (failed.isNotEmpty() && !isBusy && lastPass == GroupCaptureCoordinator.PassKind.COLLECT) { item { - TextButton(onClick = { - val retry = members(failed.map { it.id }) - container.appScope.launch { coordinator.stopAndDownloadAll(retry) } - }) { Text("Retry Failed Download(s)") } + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { + Button( + onClick = { + val chosen = members(selectedIds) + container.appScope.launch { coordinator.startAll(chosen, selections) } + }, + enabled = !isBusy && selectedIds.isNotEmpty() && selections.isNotEmpty(), + colors = ButtonDefaults.buttonColors(containerColor = Palette.danger, contentColor = Color.White), + modifier = Modifier.fillMaxWidth(), + ) { + Icon(Icons.Filled.FiberManualRecord, contentDescription = null, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(6.dp)) + Text("Start Logging All") + } + SectionFooter( + if (selectedIds.isEmpty()) "Select at least one board above." + else "${selectedIds.size} board${if (selectedIds.size == 1) "" else "s"} will start logging.", + ) + } } } } @@ -221,11 +306,11 @@ fun GroupLoggingScreen() { if (showStopDialog) { AlertDialog( onDismissRequest = { showStopDialog = false }, - title = { Text("Stop & download all?") }, + title = { Text("Stop logging on all boards and download their data?") }, text = { Text( - "Every board in the group stops recording, then each is downloaded in turn. " + - "Keep the boards nearby.", + "Each board is collected in turn. Boards that are out of range are skipped — " + + "their data stays on the board for later.", ) }, confirmButton = { @@ -233,30 +318,50 @@ fun GroupLoggingScreen() { showStopDialog = false val ids = activeGroupRecords.map { it.deviceId }.distinct() container.appScope.launch { coordinator.stopAndDownloadAll(members(ids)) } - }) { Text("Stop & Download") } + }) { Text("Stop & Download All") } }, dismissButton = { - TextButton(onClick = { showStopDialog = false }) { Text("Cancel") } + OutlinedButton(onClick = { showStopDialog = false }) { Text("Cancel") } }, ) } } @Composable -private fun PhaseText(phase: GroupCaptureCoordinator.BoardPhase) { - val (text, color) = when (phase) { - GroupCaptureCoordinator.BoardPhase.Pending -> "Waiting…" to GlassTextDim - GroupCaptureCoordinator.BoardPhase.Connecting -> "Connecting…" to GlassTextDim - GroupCaptureCoordinator.BoardPhase.Starting -> "Starting…" to GlassTextDim - GroupCaptureCoordinator.BoardPhase.Verifying -> "Verifying…" to GlassTextDim - GroupCaptureCoordinator.BoardPhase.Logging -> "Logging" to GlassGood - GroupCaptureCoordinator.BoardPhase.Stopping -> "Stopping…" to GlassTextDim - GroupCaptureCoordinator.BoardPhase.Downloading -> "Downloading…" to GlassTextDim - is GroupCaptureCoordinator.BoardPhase.Saved -> "Saved ${phase.count} session(s)" to GlassGood +private fun PhaseIcon(phase: GroupCaptureCoordinator.BoardPhase) { + when (phase) { + GroupCaptureCoordinator.BoardPhase.Pending -> + Icon(Icons.Filled.RadioButtonUnchecked, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant) + GroupCaptureCoordinator.BoardPhase.Connecting, + GroupCaptureCoordinator.BoardPhase.Starting, + GroupCaptureCoordinator.BoardPhase.Verifying, + GroupCaptureCoordinator.BoardPhase.Stopping, + GroupCaptureCoordinator.BoardPhase.Downloading, + -> CircularProgressIndicator(modifier = Modifier.padding(2.dp).size(20.dp), strokeWidth = 2.dp) + GroupCaptureCoordinator.BoardPhase.Logging -> + Icon(Icons.Filled.FiberManualRecord, contentDescription = null, tint = Palette.danger) + is GroupCaptureCoordinator.BoardPhase.Saved -> + Icon(Icons.Filled.CheckCircle, contentDescription = null, tint = Palette.success) is GroupCaptureCoordinator.BoardPhase.SavedWithIssues -> - "Saved ${phase.count} — ${phase.message}" to GlassWarn - is GroupCaptureCoordinator.BoardPhase.Skipped -> phase.message to GlassWarn - is GroupCaptureCoordinator.BoardPhase.Failed -> phase.message to GlassError + Icon(Icons.Filled.WarningAmber, contentDescription = null, tint = Palette.warning) + is GroupCaptureCoordinator.BoardPhase.Skipped -> + Icon(Icons.Filled.DoNotDisturbOn, contentDescription = null, tint = Palette.warning) + is GroupCaptureCoordinator.BoardPhase.Failed -> + Icon(Icons.Filled.Warning, contentDescription = null, tint = Palette.danger) } - Text(text, style = MaterialTheme.typography.labelMedium, color = color) +} + +private fun phaseText(phase: GroupCaptureCoordinator.BoardPhase): String = when (phase) { + GroupCaptureCoordinator.BoardPhase.Pending -> "Waiting…" + GroupCaptureCoordinator.BoardPhase.Connecting -> "Connecting…" + GroupCaptureCoordinator.BoardPhase.Starting -> "Starting loggers…" + GroupCaptureCoordinator.BoardPhase.Verifying -> "Confirming data is recording…" + GroupCaptureCoordinator.BoardPhase.Logging -> "Logging" + GroupCaptureCoordinator.BoardPhase.Stopping -> "Stopping…" + GroupCaptureCoordinator.BoardPhase.Downloading -> "Downloading…" + is GroupCaptureCoordinator.BoardPhase.Saved -> "Saved ${phase.count} session${if (phase.count == 1) "" else "s"}" + is GroupCaptureCoordinator.BoardPhase.SavedWithIssues -> + "Saved ${phase.count} session${if (phase.count == 1) "" else "s"} · ${phase.message}" + is GroupCaptureCoordinator.BoardPhase.Skipped -> phase.message + is GroupCaptureCoordinator.BoardPhase.Failed -> phase.message } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/LoggingScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/LoggingScreen.kt index 389ab62..f226ac7 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/LoggingScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/LoggingScreen.kt @@ -1,14 +1,32 @@ package com.mbientlab.metawear.app.ui.logging import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.Download +import androidx.compose.material.icons.filled.FiberManualRecord +import androidx.compose.material.icons.filled.Stop +import androidx.compose.material.icons.filled.Verified +import androidx.compose.material.icons.filled.Warning +import androidx.compose.material.icons.outlined.Circle import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -16,25 +34,42 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import com.mbientlab.metawear.app.core.SensorKey import com.mbientlab.metawear.app.core.SensorSelection import com.mbientlab.metawear.app.data.LogSessionRecord import com.mbientlab.metawear.app.ui.appViewModel -import com.mbientlab.metawear.app.ui.components.ErrorBanner -import com.mbientlab.metawear.app.ui.components.GlassCard -import com.mbientlab.metawear.app.ui.components.LabeledValue +import com.mbientlab.metawear.app.ui.components.AppScaffold +import com.mbientlab.metawear.app.ui.components.BrandCard +import com.mbientlab.metawear.app.ui.components.GroupCard +import com.mbientlab.metawear.app.ui.components.Notice +import com.mbientlab.metawear.app.ui.components.SectionFooter import com.mbientlab.metawear.app.ui.components.SectionHeader +import com.mbientlab.metawear.app.ui.components.cardListItemColors +import com.mbientlab.metawear.app.ui.components.formatDuration +import com.mbientlab.metawear.app.ui.components.formatTime +import com.mbientlab.metawear.app.ui.components.icon +import com.mbientlab.metawear.app.ui.components.sensorIconForLabel import com.mbientlab.metawear.app.ui.stream.SensorConfigSection -import com.mbientlab.metawear.app.ui.theme.GlassGood -import com.mbientlab.metawear.app.ui.theme.GlassTextDim +import com.mbientlab.metawear.app.ui.theme.Palette +import com.mbientlab.metawear.app.ui.theme.forText import com.mbientlab.metawear.app.vm.DeviceViewModel import com.mbientlab.metawear.app.vm.DownloadViewModel import com.mbientlab.metawear.app.vm.LogSessionViewModel +import com.mbientlab.metawear.persistence.SessionSnapshot +/** + * On-board logging: sensor picker (locked while recording), a phase status + * row, the active loggers, and the download flow for stopped sessions. + * The primary action (Start / Stop / Download) lives in the top bar. + */ @Composable -fun LoggingScreen() { +fun LoggingScreen(onBack: () -> Unit) { val deviceVm = appViewModel(::DeviceViewModel) val vm = appViewModel(::LogSessionViewModel) val downloadVm = appViewModel(::DownloadViewModel) @@ -52,118 +87,221 @@ fun LoggingScreen() { } val deviceId = deviceVm.device?.identifier - val pending = records.filter { - it.deviceId == deviceId && it.status == LogSessionRecord.Status.STOPPED - } - - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(10.dp), - ) { - item { Text("Logging", style = MaterialTheme.typography.headlineSmall) } - item { ErrorBanner(lastError) { vm.clearError() } } + val running = records.filter { it.deviceId == deviceId && it.status == LogSessionRecord.Status.RUNNING } + val pending = records.filter { it.deviceId == deviceId && it.status == LogSessionRecord.Status.STOPPED } + val isRunning = phase is LogSessionViewModel.Phase.Running + val notice = lastError?.let { Notice(it) { vm.clearError() } } + ?: (downloadPhase as? DownloadViewModel.Phase.Failed)?.let { Notice(it.message) { downloadVm.reset() } } - when (val p = phase) { - is LogSessionViewModel.Phase.Running -> { - item { - GlassCard { - Text("Recording to flash", style = MaterialTheme.typography.titleSmall, color = GlassGood) - Text( - formatElapsed(elapsed), - style = MaterialTheme.typography.headlineMedium, - ) - Text( - "Started ${p.startedAt}", - style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, - ) - } + AppScaffold( + title = "Logging", + onBack = onBack, + notice = notice, + actions = { + when { + isRunning -> OutlinedButton( + onClick = { vm.stop() }, + enabled = !isBusy, + modifier = Modifier.padding(end = 8.dp), + ) { + Icon(Icons.Filled.Stop, contentDescription = null, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(4.dp)) + Text("Stop") } - item { - Button( - onClick = { vm.stop() }, - enabled = !isBusy, - modifier = Modifier.fillMaxWidth(), - ) { Text("Stop Logging") } + pending.isNotEmpty() && downloadPhase is DownloadViewModel.Phase.Idle -> Button( + onClick = { downloadVm.downloadAll(pending) }, + modifier = Modifier.padding(end = 8.dp), + ) { + Icon(Icons.Filled.Download, contentDescription = null, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(4.dp)) + Text("Download") + } + else -> Button( + onClick = { vm.start(selections) }, + enabled = !isBusy && selections.isNotEmpty() && phase !is LogSessionViewModel.Phase.Running, + colors = ButtonDefaults.buttonColors(containerColor = Palette.danger, contentColor = Color.White), + modifier = Modifier.padding(end = 8.dp), + ) { + Icon(Icons.Filled.FiberManualRecord, contentDescription = null, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(4.dp)) + Text("Start") } } + }, + ) { padding -> + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues( + start = 16.dp, + end = 16.dp, + top = padding.calculateTopPadding() + 4.dp, + bottom = padding.calculateBottomPadding() + 24.dp, + ), + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { + item { + // While recording, mirror the running loggers into the locked + // picker so the user sees what is actually being captured. + SensorConfigSection( + modules = modules, + selections = if (isRunning && running.isNotEmpty()) running.map { it.selection } else selections, + onSelectionsChange = { selections = it }, + loggingMode = true, // hides stream-only altitude + locked = isRunning, + ) + } - else -> { - item { - SensorConfigSection( - modules = modules, - selections = selections, - onSelectionsChange = { selections = it }, - loggingMode = true, // hides stream-only altitude + item { + val (icon, tint, text) = when (val p = phase) { + LogSessionViewModel.Phase.Idle -> Triple( + Icons.Outlined.Circle, + MaterialTheme.colorScheme.onSurfaceVariant, + "Idle · tap Start to begin", + ) + is LogSessionViewModel.Phase.Running -> Triple( + Icons.Filled.FiberManualRecord, + Palette.danger, + "Logging · ${formatDuration(elapsed)} · started ${p.startedAt.formatTime()}", + ) + LogSessionViewModel.Phase.Stopped -> Triple( + Icons.Filled.CheckCircle, + Palette.success, + "Stopped · ${formatDuration(elapsed)} captured", ) } - item { - Button( - onClick = { vm.start(selections) }, - enabled = !isBusy && selections.isNotEmpty(), - modifier = Modifier.fillMaxWidth(), - ) { Text("Start Logging") } + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { + BrandCard { + Row(horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically) { + Icon(icon, contentDescription = null, tint = tint) + Text(text, style = MaterialTheme.typography.bodyLarge, fontFamily = FontFamily.Monospace) + } + } + SectionFooter("The board keeps logging even if you close the app.") } } - } - if (pending.isNotEmpty()) { - item { SectionHeader("Pending download") } - items(pending, key = { it.id }) { record -> - GlassCard { - LabeledValue(record.selection.displayLabel, record.status.name.lowercase()) - } - } - item { - when (val dp = downloadPhase) { - is DownloadViewModel.Phase.Downloading -> { - GlassCard { - Text("Downloading…", style = MaterialTheme.typography.titleSmall) - LinearProgressIndicator( - progress = { dp.progress.toFloat() }, - modifier = Modifier.fillMaxWidth(), - ) - Text( - "${dp.entries} entries" + (dp.total?.let { " of $it" } ?: ""), - style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, + if (isRunning && running.isNotEmpty()) { + item { SectionHeader("Active Loggers") } + item { + GroupCard { + running.forEachIndexed { index, record -> + if (index > 0) HorizontalDivider() + ListItem( + headlineContent = { Text(record.selection.displayLabel) }, + supportingContent = { Text(record.startDate.formatTime()) }, + leadingContent = { + Icon(record.selection.key.icon, contentDescription = null, tint = Palette.accent) + }, + colors = cardListItemColors(), ) } } + } + } - is DownloadViewModel.Phase.Ready -> { - GlassCard { - Text( - "Saved ${dp.snapshots.size} session(s) to history", - color = GlassGood, - style = MaterialTheme.typography.titleSmall, - ) - dp.warning?.let { - Text(it, style = MaterialTheme.typography.bodySmall, color = GlassTextDim) + if (pending.isNotEmpty() || downloadPhase !is DownloadViewModel.Phase.Idle) { + item { SectionHeader("Download") } + if (pending.isNotEmpty()) { + item { + GroupCard { + pending.forEachIndexed { index, record -> + if (index > 0) HorizontalDivider() + ListItem( + headlineContent = { Text(record.selection.displayLabel) }, + supportingContent = { Text("Stopped — awaiting download") }, + leadingContent = { + Icon(record.selection.key.icon, contentDescription = null, tint = Palette.accent) + }, + colors = cardListItemColors(), + ) } } } + } + item { DownloadStateCard(downloadPhase, onDismiss = { downloadVm.reset() }) } + } + } + } +} - is DownloadViewModel.Phase.Failed -> { - ErrorBanner(dp.message) { downloadVm.reset() } - } - - DownloadViewModel.Phase.Idle -> { - Button( - onClick = { downloadVm.downloadAll(pending) }, - modifier = Modifier.fillMaxWidth(), - ) { Text("Download ${pending.size} session(s)") } - } +/** Progress / complete / failed card for the flash readout. */ +@Composable +private fun DownloadStateCard(phase: DownloadViewModel.Phase, onDismiss: () -> Unit) { + when (phase) { + DownloadViewModel.Phase.Idle -> Unit + is DownloadViewModel.Phase.Downloading -> BrandCard(verticalSpacing = 12.dp) { + Row(horizontalArrangement = Arrangement.spacedBy(10.dp), verticalAlignment = Alignment.CenterVertically) { + Icon(Icons.Filled.Download, contentDescription = null, tint = Palette.accent) + Column { + Text("Downloading", style = MaterialTheme.typography.titleSmall) + Text( + if (phase.total != null && phase.total > 0) "${phase.entries} / ${phase.total} entries" + else "Reading log length…", + style = MaterialTheme.typography.bodySmall, + fontFamily = FontFamily.Monospace, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + } + LinearProgressIndicator( + progress = { phase.progress.toFloat().coerceIn(0f, 1f) }, + modifier = Modifier.fillMaxWidth(), + ) + Text( + "${(phase.progress * 100).toInt()}%", + style = MaterialTheme.typography.headlineSmall, + fontFamily = FontFamily.Monospace, + modifier = Modifier.align(Alignment.End), + ) + } + is DownloadViewModel.Phase.Ready -> Column(verticalArrangement = Arrangement.spacedBy(10.dp)) { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(horizontal = 4.dp), + ) { + Icon(Icons.Filled.Verified, contentDescription = null, tint = Palette.success) + Text("Download complete", style = MaterialTheme.typography.titleSmall) + } + phase.warning?.let { warning -> + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(horizontal = 4.dp), + ) { + Icon(Icons.Filled.Warning, contentDescription = null, tint = Palette.warning, modifier = Modifier.size(16.dp)) + Text(warning, style = MaterialTheme.typography.bodySmall, color = Palette.warning.forText()) } } + GroupCard { + phase.snapshots.forEachIndexed { index, snapshot -> + if (index > 0) HorizontalDivider() + SessionDownloadRow(snapshot) + } + } + SectionFooter("Saved to Session History — open it from the device hub to chart, replay, or export.") + } + is DownloadViewModel.Phase.Failed -> BrandCard { + Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) { + Icon(Icons.Filled.Warning, contentDescription = null, tint = Palette.danger) + Text("Download failed", style = MaterialTheme.typography.titleSmall) + } + Text(phase.message, style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant) + OutlinedButton(onClick = onDismiss) { Text("Dismiss") } } } } -private fun formatElapsed(totalSeconds: Int): String { - val minutes = totalSeconds / 60 - val seconds = totalSeconds % 60 - return "%d:%02d".format(minutes, seconds) +/** One downloaded-session row: sensor icon, label, sample count and start time. */ +@Composable +private fun SessionDownloadRow(snapshot: SessionSnapshot) { + val icon: ImageVector = sensorIconForLabel(snapshot.label) + ListItem( + headlineContent = { Text(snapshot.label ?: snapshot.sensorKind.replaceFirstChar { it.uppercase() }) }, + supportingContent = { + Text("${snapshot.sampleCount} samples · ${snapshot.startDate.formatTime()}", fontFamily = FontFamily.Monospace) + }, + leadingContent = { Icon(icon, contentDescription = null, tint = Palette.accent) }, + colors = cardListItemColors(), + ) } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/scan/BrandScanBackground.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/scan/BrandScanBackground.kt new file mode 100644 index 0000000..4f2c9a3 --- /dev/null +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/scan/BrandScanBackground.kt @@ -0,0 +1,72 @@ +package com.mbientlab.metawear.app.ui.scan + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.drawText +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.rememberTextMeasurer +import androidx.compose.ui.unit.dp +import com.mbientlab.metawear.app.ui.theme.Palette + +/** + * Decorative background for the connection screen: the brand's solid + * #FE9500 field, broken up with static white line-art so it doesn't read as + * a wall of orange — an oversized "m" watermark rising from the bottom + * corner and a set of concentric scan ripples radiating from the + * bottom-leading edge, a nod to what this screen does all day. No motion. + * + * In light mode a 25 % white veil lifts the orange toward cream so the + * white device cards don't jump too harshly off it; dark cards sit + * comfortably on the full orange, so no veil there. + */ +@Composable +fun BrandScanBackground(modifier: Modifier = Modifier) { + val isLight = !isSystemInDarkTheme() + val textMeasurer = rememberTextMeasurer() + Canvas(modifier = modifier.fillMaxSize()) { + val w = size.width + val h = size.height + + drawRect(Palette.accent) + if (isLight) drawRect(Color.White.copy(alpha = 0.25f)) + + // Scan ripples centred just off the bottom-leading corner. + for (ring in 1..5) { + drawCircle( + color = Color.White.copy(alpha = 0.12f), + radius = ring * w * 0.45f / 2f, + center = Offset(0f, h), + style = Stroke(width = 1.5.dp.toPx()), + ) + } + + // Oversized "m" peeking up from the bottom-trailing edge — the + // device lists live in the top half, so the mark fills the quiet + // space below them. + val fontSizeSp = (w * 0.95f).toSp() + val layout = textMeasurer.measure( + text = "m", + style = TextStyle( + fontSize = fontSizeSp, + fontWeight = FontWeight.SemiBold, + fontFamily = FontFamily.SansSerif, + color = Color.White.copy(alpha = 0.14f), + ), + ) + drawText( + textLayoutResult = layout, + topLeft = Offset( + x = w * 0.74f - layout.size.width / 2f, + y = h * 0.92f - layout.size.height / 2f, + ), + ) + } +} diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/scan/ScanScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/scan/ScanScreen.kt index b0cdf4a..45e816f 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/scan/ScanScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/scan/ScanScreen.kt @@ -5,16 +5,39 @@ import android.content.pm.PackageManager import android.os.Build import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.clickable +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.material3.Button +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight +import androidx.compose.material.icons.filled.AutoAwesome +import androidx.compose.material.icons.filled.BluetoothDisabled +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.FiberManualRecord +import androidx.compose.material.icons.filled.History +import androidx.compose.material.icons.filled.Layers +import androidx.compose.material.icons.filled.Sensors +import androidx.compose.material.icons.filled.SettingsInputAntenna +import androidx.compose.material.icons.filled.WifiTethering +import androidx.compose.material.icons.outlined.StopCircle +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Switch import androidx.compose.material3.Text @@ -27,18 +50,27 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat +import com.mbientlab.metawear.DeviceState +import com.mbientlab.metawear.app.data.LogSessionRecord import com.mbientlab.metawear.app.data.RememberedDevice +import com.mbientlab.metawear.app.ui.appContainer import com.mbientlab.metawear.app.ui.appViewModel -import com.mbientlab.metawear.app.ui.components.ErrorBanner -import com.mbientlab.metawear.app.ui.components.GlassCard -import com.mbientlab.metawear.app.ui.components.RssiPill +import com.mbientlab.metawear.app.ui.components.AppScaffold +import com.mbientlab.metawear.app.ui.components.BrandCard +import com.mbientlab.metawear.app.ui.components.DeviceConnectionStatus +import com.mbientlab.metawear.app.ui.components.DeviceStatusChip +import com.mbientlab.metawear.app.ui.components.Notice import com.mbientlab.metawear.app.ui.components.SectionHeader -import com.mbientlab.metawear.app.ui.theme.GlassTextDim -import com.mbientlab.metawear.app.ui.theme.GlassWarn +import com.mbientlab.metawear.app.ui.components.cardListItemColors +import com.mbientlab.metawear.app.ui.theme.Palette import com.mbientlab.metawear.app.vm.ScannerViewModel +import kotlinx.coroutines.flow.MutableStateFlow /** Runtime permissions needed to scan/connect on this API level. */ private fun requiredBlePermissions(): Array = @@ -48,16 +80,30 @@ private fun requiredBlePermissions(): Array = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION) } +/** + * Connection screen: remembered boards, nearby advertisers, the demo board, + * and a link into session history — all on the brand orange field. + */ @Composable -fun ScanScreen(onDeviceSelected: () -> Unit, onGroupLogging: () -> Unit = {}) { +fun ScanScreen( + onDeviceSelected: () -> Unit, + onGroupLogging: () -> Unit = {}, + onSessionHistory: () -> Unit = {}, +) { val vm = appViewModel(::ScannerViewModel) + val container = appContainer() val context = LocalContext.current val isScanning by vm.isScanning.collectAsState() - val nearby by vm.nearbyDevices.collectAsState() + val nearbyAll by vm.nearbyDevices.collectAsState() val remembered by vm.remembered.collectAsState() val demoMode by vm.demoModeEnabled.collectAsState() val lastError by vm.lastError.collectAsState() + val records by container.logSessions.records.collectAsState() + val activeId by container.activeDeviceId.collectAsState() + val activeState by remember(activeId) { + activeId?.let { container.device(it).state } ?: MutableStateFlow(DeviceState.Disconnected) + }.collectAsState() fun hasPermissions(): Boolean = requiredBlePermissions().all { ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED @@ -71,137 +117,284 @@ fun ScanScreen(onDeviceSelected: () -> Unit, onGroupLogging: () -> Unit = {}) { if (permissionsGranted) vm.startScan() } - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(10.dp), - ) { - item { - Text("MetaWear", style = MaterialTheme.typography.headlineMedium) - Text( - "Scan for nearby MetaMotion boards or run in demo mode.", - style = MaterialTheme.typography.bodyMedium, - color = GlassTextDim, - ) + fun toggleScan() { + when { + isScanning -> vm.stopScan() + hasPermissions() -> { + permissionsGranted = true + vm.startScan() + } + else -> permissionLauncher.launch(requiredBlePermissions()) } + } + + fun connect(identifier: String) { + vm.stopScan() + vm.select(identifier) + onDeviceSelected() + } + + val bluetoothAvailable = vm.isBluetoothAvailable() + val demoRows = nearbyAll.filter { it.isDemo } + val nearby = nearbyAll.filter { row -> !row.isDemo && remembered.none { it.mac == row.identifier } } + val hasActiveGroup = records.any { + it.groupID != null && + (it.status == LogSessionRecord.Status.RUNNING || it.status == LogSessionRecord.Status.STOPPED) + } + + fun hasPendingLog(mac: String) = records.any { + it.deviceId == mac && + (it.status == LogSessionRecord.Status.RUNNING || it.status == LogSessionRecord.Status.STOPPED) + } - item { ErrorBanner(lastError) { vm.clearError() } } + fun status(device: RememberedDevice): DeviceConnectionStatus { + val liveRssi = nearbyAll.firstOrNull { it.identifier == device.mac }?.rssi + return when { + activeId == device.mac && activeState == DeviceState.Connecting -> DeviceConnectionStatus.Connecting + activeId == device.mac && activeState != DeviceState.Disconnected -> DeviceConnectionStatus.Connected(liveRssi) + nearbyAll.any { it.identifier == device.mac } -> DeviceConnectionStatus.Available(liveRssi) + else -> DeviceConnectionStatus.Offline + } + } - item { - GlassCard { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, + Box(Modifier.fillMaxSize()) { + BrandScanBackground() + AppScaffold( + title = "MetaWear", + containerColor = Color.Transparent, + notice = lastError?.let { Notice(it) { vm.clearError() } }, + topBarContentColor = Color.White, + actions = { + TextButton( + onClick = ::toggleScan, + colors = ButtonDefaults.textButtonColors(contentColor = Color.White), ) { - Column(Modifier.weight(1f)) { - Text("Demo mode", style = MaterialTheme.typography.titleSmall) - Text( - "Simulated MetaMotion S — no hardware needed", - style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, - ) - } - Switch(checked = demoMode, onCheckedChange = { vm.setDemoMode(it) }) + Icon( + if (isScanning) Icons.Outlined.StopCircle else Icons.Filled.SettingsInputAntenna, + contentDescription = null, + modifier = Modifier.size(18.dp), + ) + Spacer(Modifier.width(6.dp)) + Text(if (isScanning) "Stop" else "Scan") } - if (!vm.isBluetoothAvailable() && !demoMode) { - Text( - "Bluetooth is off or unavailable on this device — enable demo mode to explore the app.", - style = MaterialTheme.typography.bodySmall, - color = GlassWarn, + IconButton(onClick = onGroupLogging) { + Icon( + Icons.Filled.Layers, + contentDescription = "Group logging", + tint = if (hasActiveGroup) Palette.danger else Color.White, ) } - } - } - - item { - Button( - onClick = { - if (isScanning) { - vm.stopScan() - } else if (hasPermissions()) { - permissionsGranted = true - vm.startScan() - } else { - permissionLauncher.launch(requiredBlePermissions()) - } - }, - modifier = Modifier.fillMaxWidth(), + }, + ) { padding -> + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues( + start = 16.dp, + end = 16.dp, + top = padding.calculateTopPadding() + 4.dp, + bottom = padding.calculateBottomPadding() + 24.dp, + ), + verticalArrangement = Arrangement.spacedBy(10.dp), ) { - Text(if (isScanning) "Stop Scanning" else "Start Scanning") - } - } + item { SectionHeader("Remembered", color = Color.White) } + if (remembered.isEmpty()) { + item { ScanNote("No remembered devices yet.") } + } else { + items(remembered, key = { "remembered-${it.mac}" }) { device -> + RememberedDeviceRow( + device = device, + status = status(device), + hasPendingLog = hasPendingLog(device.mac), + onTap = { connect(device.mac) }, + onForget = { vm.forget(device.mac) }, + ) + } + } - item { - TextButton(onClick = onGroupLogging, modifier = Modifier.fillMaxWidth()) { - Text("Group Logging — record several boards at once") - } - } + item { SectionHeader("Nearby", color = Color.White) } + when { + !bluetoothAvailable -> item { + ScanNote( + "Bluetooth is turned off or unavailable — turn it on to scan, or use demo mode.", + icon = Icons.Filled.BluetoothDisabled, + tint = Palette.warning, + ) + } + nearby.isEmpty() -> item { + ScanNote(if (isScanning) "Scanning…" else "Tap Scan to look for devices.") + } + else -> items(nearby, key = { "nearby-${it.identifier}" }) { device -> + val isConnecting = activeId == device.identifier && activeState == DeviceState.Connecting + ScanRow( + icon = Icons.Filled.WifiTethering, + iconTint = Palette.info, + title = device.name.ifEmpty { "MetaWear" }, + subtitle = device.identifier, + trailing = { + DeviceStatusChip( + if (isConnecting) DeviceConnectionStatus.Connecting + else DeviceConnectionStatus.Available(device.rssi), + ) + }, + onTap = { connect(device.identifier) }, + ) + } + } - if (nearby.isNotEmpty()) { - item { SectionHeader("Nearby") } - items(nearby, key = { it.identifier }) { device -> - GlassCard( - modifier = Modifier.clickable { - vm.stopScan() - vm.select(device.identifier) - onDeviceSelected() - }, - ) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, + item { SectionHeader("Demo", color = Color.White) } + item { + BrandCard( + containerColor = MaterialTheme.colorScheme.surfaceContainerLowest, + contentPadding = PaddingValues(0.dp), + verticalSpacing = 0.dp, ) { - Column(Modifier.weight(1f)) { - Text(device.name, style = MaterialTheme.typography.titleSmall) - Text( - if (device.isDemo) "Demo device" else device.identifier, - style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, + ListItem( + headlineContent = { Text("Demo mode") }, + supportingContent = { Text("Synthetic sensors — no hardware needed") }, + trailingContent = { + Switch(checked = demoMode, onCheckedChange = { vm.setDemoMode(it) }) + }, + colors = cardListItemColors(), + ) + demoRows.forEach { demo -> + HorizontalDivider() + val isConnecting = activeId == demo.identifier && activeState == DeviceState.Connecting + ListItem( + headlineContent = { Text(demo.name) }, + supportingContent = { Text("Tap to connect to the simulated board") }, + leadingContent = { + Icon(Icons.Filled.AutoAwesome, contentDescription = null, tint = Palette.accent) + }, + trailingContent = { + if (isConnecting) { + CircularProgressIndicator(modifier = Modifier.size(20.dp), strokeWidth = 2.dp) + } else { + Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null) + } + }, + colors = cardListItemColors(), + modifier = Modifier.combinedClickable(onClick = { connect(demo.identifier) }), ) } - RssiPill(device.rssi) } } - } - } - if (remembered.isNotEmpty()) { - item { SectionHeader("My Devices") } - items(remembered, key = { it.mac }) { device -> - RememberedRow( - device = device, - onConnect = { - vm.stopScan() - vm.select(device.mac) - onDeviceSelected() - }, - onForget = { vm.forget(device.mac) }, - ) + item { + BrandCard( + containerColor = MaterialTheme.colorScheme.surfaceContainerLowest, + contentPadding = PaddingValues(0.dp), + onClick = onSessionHistory, + ) { + ListItem( + headlineContent = { Text("Session History") }, + supportingContent = { Text("Saved streams and downloaded logs") }, + leadingContent = { + Icon(Icons.Filled.History, contentDescription = null, tint = Palette.accent) + }, + trailingContent = { + Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null) + }, + colors = cardListItemColors(), + ) + } + } } } } } +/** Short explanatory line on the orange field, in white. */ +@Composable +private fun ScanNote(text: String, icon: ImageVector? = null, tint: Color = Color.White) { + Row( + modifier = Modifier.padding(horizontal = 4.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + if (icon != null) Icon(icon, contentDescription = null, tint = tint, modifier = Modifier.size(18.dp)) + Text(text, style = MaterialTheme.typography.bodySmall, color = Color.White.copy(alpha = 0.9f)) + } +} + +/** One device row on a light card: icon, name, identifier, trailing status. */ +@OptIn(ExperimentalFoundationApi::class) @Composable -private fun RememberedRow( +private fun ScanRow( + icon: ImageVector, + iconTint: Color, + title: String, + subtitle: String?, + trailing: @Composable () -> Unit, + onTap: () -> Unit, + onLongPress: (() -> Unit)? = null, + titleBadge: (@Composable () -> Unit)? = null, + dimmed: Boolean = false, +) { + BrandCard( + containerColor = MaterialTheme.colorScheme.surfaceContainerLowest.copy(alpha = if (dimmed) 0.75f else 1f), + contentPadding = PaddingValues(0.dp), + ) { + ListItem( + headlineContent = { + Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) { + Text(title) + titleBadge?.invoke() + } + }, + supportingContent = subtitle?.let { + { Text(it, fontFamily = FontFamily.Monospace, style = MaterialTheme.typography.bodySmall) } + }, + leadingContent = { Icon(icon, contentDescription = null, tint = iconTint) }, + trailingContent = trailing, + colors = cardListItemColors(), + modifier = Modifier.combinedClickable(onClick = onTap, onLongClick = onLongPress), + ) + } +} + +@Composable +private fun RememberedDeviceRow( device: RememberedDevice, - onConnect: () -> Unit, + status: DeviceConnectionStatus, + hasPendingLog: Boolean, + onTap: () -> Unit, onForget: () -> Unit, ) { - GlassCard(modifier = Modifier.clickable(onClick = onConnect)) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Column(Modifier.weight(1f)) { - Text(device.name, style = MaterialTheme.typography.titleSmall) - Text(device.mac, style = MaterialTheme.typography.bodySmall, color = GlassTextDim) - } - TextButton(onClick = onForget) { Text("Forget") } + var menuOpen by remember { mutableStateOf(false) } + Box { + ScanRow( + icon = Icons.Filled.Sensors, + iconTint = Palette.accent, + title = device.name.ifEmpty { "MetaWear" }, + subtitle = device.mac, + trailing = { DeviceStatusChip(status) }, + onTap = onTap, + onLongPress = { menuOpen = true }, + dimmed = status == DeviceConnectionStatus.Offline, + titleBadge = if (hasPendingLog) { + { + // Red, not amber: the board is actively recording — the + // same colour the state chip uses for "Logging". + Icon( + Icons.Filled.FiberManualRecord, + contentDescription = "Logging session waiting", + tint = Palette.danger, + modifier = Modifier.size(12.dp), + ) + } + } else { + null + }, + ) + DropdownMenu(expanded = menuOpen, onDismissRequest = { menuOpen = false }) { + DropdownMenuItem( + text = { Text("Forget device") }, + leadingIcon = { Icon(Icons.Filled.Delete, contentDescription = null) }, + onClick = { + menuOpen = false + onForget() + }, + ) } } } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/sessions/SessionDetailScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/sessions/SessionDetailScreen.kt new file mode 100644 index 0000000..33a23c9 --- /dev/null +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/sessions/SessionDetailScreen.kt @@ -0,0 +1,168 @@ +package com.mbientlab.metawear.app.ui.sessions + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Build +import androidx.compose.material.icons.filled.Sensors +import androidx.compose.material.icons.filled.Share +import androidx.compose.material.icons.filled.Tag +import androidx.compose.material.icons.outlined.Schedule +import androidx.compose.material3.Button +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import com.mbientlab.metawear.app.core.AnyChartSample +import com.mbientlab.metawear.app.core.ReplayTimeline +import com.mbientlab.metawear.app.core.SessionAxisStyle +import com.mbientlab.metawear.app.export.CsvShare +import com.mbientlab.metawear.app.export.ExportFilename +import com.mbientlab.metawear.app.ui.appViewModel +import com.mbientlab.metawear.app.ui.components.AppScaffold +import com.mbientlab.metawear.app.ui.components.BrandCard +import com.mbientlab.metawear.app.ui.components.Notice +import com.mbientlab.metawear.app.ui.components.SensorChartCard +import com.mbientlab.metawear.app.ui.components.formatTime +import com.mbientlab.metawear.app.ui.components.sensorIconForLabel +import com.mbientlab.metawear.app.ui.theme.Palette +import com.mbientlab.metawear.app.vm.SessionHistoryViewModel +import com.mbientlab.metawear.persistence.SessionSnapshot +import java.text.NumberFormat +import kotlinx.coroutines.launch + +/** + * One saved session: stats card, the 3D replay for quaternion captures, the + * chart preview (most recent 600 samples), and CSV export. + */ +@Composable +fun SessionDetailScreen(sessionId: String, onBack: () -> Unit) { + val vm = appViewModel(::SessionHistoryViewModel) + val sessions by vm.sessions.collectAsState() + val lastError by vm.lastError.collectAsState() + val session = sessions.firstOrNull { it.id == sessionId } + val context = LocalContext.current + val scope = rememberCoroutineScope() + + var preview by remember { mutableStateOf?>(null) } + var replay by remember { mutableStateOf, ReplayTimeline>?>(null) } + + LaunchedEffect(session?.id) { + val snapshot = session ?: return@LaunchedEffect + preview = vm.loadPreview(snapshot) + if (snapshot.sensorKind == "quaternion" && snapshot.sampleCount >= 2) { + replay = vm.loadReplay(snapshot)?.let { (samples, ticks) -> samples to ReplayTimeline(ticks) } + } + } + + AppScaffold( + title = session?.let { it.label ?: it.sensorKind.replaceFirstChar { c -> c.uppercase() } } ?: "Session", + onBack = onBack, + notice = lastError?.let { Notice(it) { vm.clearError() } }, + ) { padding -> + if (session == null) { + Box(Modifier.fillMaxSize().padding(padding), contentAlignment = Alignment.Center) { + CircularProgressIndicator() + } + return@AppScaffold + } + val style = SessionAxisStyle.forSession( + sensorKind = session.sensorKind, + label = session.label, + channelCount = preview?.firstOrNull()?.channelCount ?: SessionAxisStyle.channelCountFor(session.sensorKind), + ) + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues( + start = 16.dp, + end = 16.dp, + top = padding.calculateTopPadding() + 4.dp, + bottom = padding.calculateBottomPadding() + 24.dp, + ), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + item { StatsCard(session) } + replay?.let { (samples, timeline) -> + item { BrandCard { SessionReplayView(samples = samples, timeline = timeline) } } + } + preview?.let { samples -> + if (samples.isNotEmpty()) { + item { + SensorChartCard( + title = session.label?.substringBefore(" · ") + ?: session.sensorKind.replaceFirstChar { it.uppercase() }, + icon = sensorIconForLabel(session.label), + samples = samples, + latest = samples.last(), + style = style, + ) + } + } + } + item { + Button( + onClick = { + scope.launch { + val csv = vm.exportCsv(session) ?: return@launch + val filename = ExportFilename.make( + // Capture-time name attributes the file; un-renamed + // boards get their serial appended instead. + deviceName = session.deviceName ?: "MetaWear-${session.deviceSerial}", + sensorTag = session.sensorKind, + timestamp = session.startDate, + discriminator = session.id.take(4), + ) + CsvShare.share(context, filename, csv) + } + }, + modifier = Modifier.fillMaxWidth(), + ) { + Icon(Icons.Filled.Share, contentDescription = null, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(8.dp)) + Text("Export CSV") + } + } + } + } +} + +@Composable +private fun StatsCard(session: SessionSnapshot) { + BrandCard(verticalSpacing = 6.dp) { + StatLine(Icons.Filled.Tag, "${NumberFormat.getIntegerInstance().format(session.sampleCount)} samples") + StatLine(Icons.Outlined.Schedule, "${session.startDate.formatTime()} – ${session.endDate.formatTime()}") + StatLine(Icons.Filled.Sensors, session.deviceName?.let { "$it · ${session.deviceModel}" } ?: session.deviceModel) + StatLine(Icons.Filled.Build, "Firmware ${session.deviceFirmware}") + session.groupID?.let { StatLine(Icons.Filled.Tag, "Group ${it.take(8)}") } + } +} + +@Composable +private fun StatLine(icon: ImageVector, text: String) { + Row(horizontalArrangement = Arrangement.spacedBy(10.dp), verticalAlignment = Alignment.CenterVertically) { + Icon(icon, contentDescription = null, tint = Palette.accent, modifier = Modifier.size(18.dp)) + Text(text, style = MaterialTheme.typography.bodyMedium) + } +} diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/sessions/SessionReplayView.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/sessions/SessionReplayView.kt index 21d05fa..f4fb7ee 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/sessions/SessionReplayView.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/sessions/SessionReplayView.kt @@ -4,10 +4,15 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Pause +import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.material3.FilledIconButton +import androidx.compose.material3.FilledTonalButton +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Slider import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -17,11 +22,11 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import com.mbientlab.metawear.app.core.AnyChartSample import com.mbientlab.metawear.app.core.ReplayTimeline import com.mbientlab.metawear.app.ui.components.TaredQuaternionCube -import com.mbientlab.metawear.app.ui.theme.GlassTextDim import kotlinx.coroutines.delay /** @@ -59,41 +64,54 @@ fun SessionReplayView( Column(verticalArrangement = Arrangement.spacedBy(4.dp)) { TaredQuaternionCube(latest = current) - Slider( - value = position.toFloat(), - onValueChange = { value -> - if (!isScrubbing) { - isScrubbing = true - wasPlayingBeforeScrub = isPlaying - isPlaying = false - } - position = value.toDouble() - }, - onValueChangeFinished = { - isScrubbing = false - if (wasPlayingBeforeScrub) isPlaying = true - }, - valueRange = 0f..maxOf(timeline.duration, 0.001).toFloat(), + Row( modifier = Modifier.fillMaxWidth(), - ) + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + FilledIconButton(onClick = { isPlaying = !isPlaying }) { + Icon( + if (isPlaying) Icons.Filled.Pause else Icons.Filled.PlayArrow, + contentDescription = if (isPlaying) "Pause" else "Play", + ) + } + Slider( + value = position.toFloat(), + onValueChange = { value -> + if (!isScrubbing) { + isScrubbing = true + wasPlayingBeforeScrub = isPlaying + isPlaying = false + } + position = value.toDouble() + }, + onValueChangeFinished = { + isScrubbing = false + if (wasPlayingBeforeScrub) isPlaying = true + }, + valueRange = 0f..maxOf(timeline.duration, 0.001).toFloat(), + modifier = Modifier.weight(1f), + ) + FilledTonalButton(onClick = { speed = if (speed >= 4.0) 1.0 else speed * 2 }) { + Text("${speed.toInt()}×", fontFamily = FontFamily.Monospace) + } + } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, ) { - Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) { - TextButton(onClick = { isPlaying = !isPlaying }) { - Text(if (isPlaying) "Pause" else "Play") - } - TextButton(onClick = { speed = if (speed >= 4.0) 1.0 else speed * 2 }) { - Text("${speed.toInt()}×") - } - } Text( - "${formatTime(position)} / ${formatTime(timeline.duration)}", + formatTime(position), + style = MaterialTheme.typography.labelMedium, + fontFamily = FontFamily.Monospace, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Text( + formatTime(timeline.duration), style = MaterialTheme.typography.labelMedium, - color = GlassTextDim, + fontFamily = FontFamily.Monospace, + color = MaterialTheme.colorScheme.onSurfaceVariant, ) } } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/sessions/SessionsScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/sessions/SessionsScreen.kt index fde10c2..57d33bf 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/sessions/SessionsScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/sessions/SessionsScreen.kt @@ -1,96 +1,121 @@ package com.mbientlab.metawear.app.ui.sessions import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.outlined.Schedule +import androidx.compose.material3.Icon +import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SwipeToDismissBox import androidx.compose.material3.SwipeToDismissBoxValue import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.material3.rememberSwipeToDismissBoxState import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import com.mbientlab.metawear.app.core.AnyChartSample -import com.mbientlab.metawear.app.core.ReplayTimeline -import com.mbientlab.metawear.app.core.SessionAxisStyle -import com.mbientlab.metawear.app.export.CsvShare -import com.mbientlab.metawear.app.export.ExportFilename import com.mbientlab.metawear.app.ui.appViewModel -import com.mbientlab.metawear.app.ui.components.ErrorBanner -import com.mbientlab.metawear.app.ui.components.GlassCard -import com.mbientlab.metawear.app.ui.components.LabeledValue -import com.mbientlab.metawear.app.ui.components.LineChart +import com.mbientlab.metawear.app.ui.components.AppScaffold +import com.mbientlab.metawear.app.ui.components.BrandCard +import com.mbientlab.metawear.app.ui.components.BrandCardShape +import com.mbientlab.metawear.app.ui.components.Notice import com.mbientlab.metawear.app.ui.components.SectionHeader -import com.mbientlab.metawear.app.ui.theme.ChannelColors -import com.mbientlab.metawear.app.ui.theme.FourChannelColors -import com.mbientlab.metawear.app.ui.theme.GlassError -import com.mbientlab.metawear.app.ui.theme.GlassTextDim +import com.mbientlab.metawear.app.ui.components.cardListItemColors +import com.mbientlab.metawear.app.ui.components.formatDateTime +import com.mbientlab.metawear.app.ui.components.sensorIconForLabel +import com.mbientlab.metawear.app.ui.theme.Palette import com.mbientlab.metawear.app.vm.SessionHistoryViewModel import com.mbientlab.metawear.persistence.SessionSnapshot -import kotlinx.coroutines.launch +import java.text.NumberFormat /** - * Session history grouped by board, with swipe-to-delete, chart previews with - * per-sample-type styling, quaternion 3D replay, and CSV export. + * Session history grouped by board (sections keyed on board identity), one + * row per saved session with swipe-to-delete; tapping a row opens the + * detail screen with the chart preview, quaternion replay, and CSV export. */ @Composable -fun SessionsScreen() { +fun SessionsScreen(onBack: () -> Unit, onOpenSession: (String) -> Unit) { val vm = appViewModel(::SessionHistoryViewModel) val sections by vm.sections.collectAsState() val lastError by vm.lastError.collectAsState() - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(10.dp), - ) { - item { Text("Sessions", style = MaterialTheme.typography.headlineSmall) } - item { ErrorBanner(lastError) { vm.clearError() } } - + AppScaffold( + title = "Session History", + onBack = onBack, + notice = lastError?.let { Notice(it) { vm.clearError() } }, + ) { padding -> if (sections.isEmpty()) { - item { - GlassCard { - Text( - "No saved sessions yet. Stream (Stop & Save) or download a log to create one.", - style = MaterialTheme.typography.bodyMedium, - color = GlassTextDim, - ) - } + Column( + modifier = Modifier + .fillMaxSize() + .padding(padding) + .padding(32.dp), + verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.CenterVertically), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + Icons.Outlined.Schedule, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.size(48.dp), + ) + Text("No sessions yet", style = MaterialTheme.typography.titleMedium) + Text( + "Downloaded log sessions and stopped live streams will appear here.", + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Center, + ) } + return@AppScaffold } - - sections.forEach { section -> - item(key = "header-${section.id}") { SectionHeader(section.title) } - items(section.sessions, key = { it.id }) { session -> - DismissableSessionCard(session, vm) + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues( + start = 16.dp, + end = 16.dp, + top = padding.calculateTopPadding() + 4.dp, + bottom = padding.calculateBottomPadding() + 24.dp, + ), + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { + sections.forEach { section -> + item(key = "header-${section.id}") { SectionHeader(section.title) } + items(section.sessions, key = { it.id }) { session -> + DismissableSessionRow( + session = session, + onOpen = { onOpenSession(session.id) }, + onDelete = { vm.deleteSession(session.id) }, + ) + } } } } } @Composable -private fun DismissableSessionCard(session: SessionSnapshot, vm: SessionHistoryViewModel) { +private fun DismissableSessionRow(session: SessionSnapshot, onOpen: () -> Unit, onDelete: () -> Unit) { val dismissState = rememberSwipeToDismissBoxState( confirmValueChange = { value -> if (value == SwipeToDismissBoxValue.EndToStart) { - vm.deleteSession(session.id) + onDelete() true } else { false @@ -104,86 +129,39 @@ private fun DismissableSessionCard(session: SessionSnapshot, vm: SessionHistoryV Box( modifier = Modifier .fillMaxSize() - .background(GlassError.copy(alpha = 0.4f)), + .background(Palette.danger, BrandCardShape), contentAlignment = Alignment.CenterEnd, ) { - Text("Delete", modifier = Modifier.padding(end = 24.dp)) + Icon( + Icons.Filled.Delete, + contentDescription = "Delete", + tint = MaterialTheme.colorScheme.onError, + modifier = Modifier.padding(end = 24.dp), + ) } }, ) { - SessionCard(session, vm) - } -} - -@Composable -private fun SessionCard(session: SessionSnapshot, vm: SessionHistoryViewModel) { - val context = LocalContext.current - val scope = rememberCoroutineScope() - - var preview by remember { mutableStateOf?>(null) } - var replay by remember { mutableStateOf, ReplayTimeline>?>(null) } - - val isQuaternion = session.sensorKind == "quaternion" - val style = SessionAxisStyle.forSession( - sensorKind = session.sensorKind, - label = session.label, - channelCount = SessionAxisStyle.channelCountFor(session.sensorKind), - ) - - GlassCard { - Text(session.label ?: session.sensorKind, style = MaterialTheme.typography.titleSmall) - session.deviceName?.let { LabeledValue("Board", it) } - LabeledValue("Samples", session.sampleCount.toString()) - LabeledValue("Start", session.startDate.toString()) - LabeledValue("Device", "${session.deviceModel} · fw ${session.deviceFirmware}") - session.groupID?.let { LabeledValue("Group", it.take(8)) } - - replay?.let { (samples, timeline) -> - SessionReplayView(samples = samples, timeline = timeline) - } - - preview?.let { samples -> - LineChart( - samples = samples, - channelCount = style.chartChannels, - colors = if (style.chartChannels == 4) FourChannelColors else ChannelColors, - yRange = style.yRange, - ) - } - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(4.dp), - ) { - if (isQuaternion && session.sampleCount >= 2 && replay == null) { - TextButton(onClick = { - scope.launch { - vm.loadReplay(session)?.let { (samples, ticks) -> - replay = samples to ReplayTimeline(ticks) - } + BrandCard(contentPadding = PaddingValues(0.dp)) { + ListItem( + headlineContent = { Text(session.label ?: session.sensorKind.replaceFirstChar { it.uppercase() }) }, + supportingContent = { + Column { + Text(session.startDate.formatDateTime()) + Text( + "${NumberFormat.getIntegerInstance().format(session.sampleCount)} samples", + fontFamily = FontFamily.Monospace, + ) } - }) { Text("Replay") } - } - if (preview == null) { - TextButton(onClick = { - scope.launch { preview = vm.loadPreview(session) } - }) { Text("Preview") } - } - TextButton(onClick = { - scope.launch { - val csv = vm.exportCsv(session) ?: return@launch - val filename = ExportFilename.make( - // Capture-time name attributes the file; un-renamed - // boards get their serial appended instead. - deviceName = session.deviceName ?: "MetaWear-${session.deviceSerial}", - sensorTag = session.sensorKind, - timestamp = session.startDate, - discriminator = session.id.take(4), - ) - CsvShare.share(context, filename, csv) - } - }) { Text("Export CSV") } - TextButton(onClick = { vm.deleteSession(session.id) }) { Text("Delete") } + }, + leadingContent = { + Icon(sensorIconForLabel(session.label), contentDescription = null, tint = Palette.accent) + }, + trailingContent = { Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null) }, + colors = cardListItemColors(), + modifier = Modifier + .fillMaxWidth() + .clickable(onClick = onOpen), + ) } } } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt index aea97eb..5e4587f 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt @@ -1,45 +1,77 @@ package com.mbientlab.metawear.app.ui.settings +import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.EventBusy +import androidx.compose.material.icons.filled.FlashlightOff +import androidx.compose.material.icons.filled.Memory +import androidx.compose.material.icons.filled.Refresh +import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Button +import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.FilterChip +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp +import com.mbientlab.metawear.app.demo.DemoBleTransport +import com.mbientlab.metawear.app.ui.appContainer import com.mbientlab.metawear.app.ui.appViewModel -import com.mbientlab.metawear.app.ui.components.ErrorBanner -import com.mbientlab.metawear.app.ui.components.GlassCard +import com.mbientlab.metawear.app.ui.components.ActionRow +import com.mbientlab.metawear.app.ui.components.AppScaffold +import com.mbientlab.metawear.app.ui.components.BrandCard +import com.mbientlab.metawear.app.ui.components.GroupCard +import com.mbientlab.metawear.app.ui.components.Notice +import com.mbientlab.metawear.app.ui.components.SectionFooter import com.mbientlab.metawear.app.ui.components.SectionHeader -import com.mbientlab.metawear.app.ui.theme.GlassError -import com.mbientlab.metawear.app.ui.theme.GlassGood -import com.mbientlab.metawear.app.ui.theme.GlassTextDim +import com.mbientlab.metawear.app.ui.firmware.FirmwareSection +import com.mbientlab.metawear.app.ui.theme.Palette +import com.mbientlab.metawear.app.vm.FirmwareUpdateViewModel import com.mbientlab.metawear.app.vm.SettingsViewModel import com.mbientlab.metawear.sensor.Settings +/** + * Device settings: rename, firmware, advertising, maintenance, factory + * reset. Every destructive action confirms through a centered dialog. + */ @Composable -fun SettingsScreen(onFactoryReset: () -> Unit) { +fun SettingsScreen(onFactoryReset: () -> Unit, onBack: () -> Unit) { val vm = appViewModel(::SettingsViewModel) + val container = appContainer() val statusMessage by vm.statusMessage.collectAsState() val lastError by vm.lastError.collectAsState() val didFactoryReset by vm.didFactoryReset.collectAsState() + // Firmware update is a real-hardware operation — hidden for the demo + // board, whose synthetic firmware revision the catalog can't resolve. + val isDemo = container.activeDeviceId.collectAsState().value == DemoBleTransport.DEVICE_IDENTIFIER + val firmwareVm = if (isDemo) null else appViewModel(::FirmwareUpdateViewModel) + var name by remember { mutableStateOf("") } var intervalMs by remember { mutableStateOf("417") } var timeoutSec by remember { mutableStateOf("0") } @@ -49,216 +81,245 @@ fun SettingsScreen(onFactoryReset: () -> Unit) { var showClearEventsDialog by remember { mutableStateOf(false) } var showRestartDialog by remember { mutableStateOf(false) } - androidx.compose.runtime.LaunchedEffect(didFactoryReset) { + LaunchedEffect(didFactoryReset) { // Board rebooted; return to the device hub, which offers Reconnect. if (didFactoryReset) onFactoryReset() } - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(10.dp), - ) { - item { Text("Settings", style = MaterialTheme.typography.headlineSmall) } - item { ErrorBanner(lastError) { vm.clearMessages() } } - statusMessage?.let { message -> + val notice = lastError?.let { Notice(it) { vm.clearMessages() } } + ?: statusMessage?.let { Notice(it) { vm.clearMessages() } } + + AppScaffold(title = "Settings", onBack = onBack, notice = notice) { padding -> + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues( + start = 16.dp, + end = 16.dp, + top = padding.calculateTopPadding() + 4.dp, + bottom = padding.calculateBottomPadding() + 24.dp, + ), + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { + item { SectionHeader("Rename") } item { - GlassCard { Text(message, color = GlassGood, style = MaterialTheme.typography.bodySmall) } + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { + BrandCard(verticalSpacing = 10.dp) { + OutlinedTextField( + value = name, + onValueChange = { name = it }, + label = { Text("Device name") }, + singleLine = true, + isError = name.isNotEmpty() && !vm.isNameValid(name), + modifier = Modifier.fillMaxWidth(), + ) + FilledTonalButton( + onClick = { vm.rename(name) }, + enabled = vm.isNameValid(name), + ) { + Icon( + Icons.Filled.Check, + contentDescription = null, + modifier = Modifier.padding(end = 6.dp), + ) + Text("Save") + } + } + SectionFooter( + "Up to 26 characters: letters, numbers, spaces, _ and -. The name updates in the app " + + "immediately and on the board's next advertisement.", + ) + } } - } - item { SectionHeader("Device name") } - item { - GlassCard { - OutlinedTextField( - value = name, - onValueChange = { name = it }, - label = { Text("Advertising name (max 26 ASCII chars)") }, - isError = name.isNotEmpty() && !vm.isNameValid(name), - modifier = Modifier.fillMaxWidth(), - ) - Button( - onClick = { vm.rename(name) }, - enabled = vm.isNameValid(name), - modifier = Modifier.fillMaxWidth(), - ) { Text("Save Name") } + if (firmwareVm != null) { + item { SectionHeader("Firmware") } + item { FirmwareSection(firmwareVm) } } - } - item { SectionHeader("Advertising") } - item { - GlassCard { - OutlinedTextField( - value = intervalMs, - onValueChange = { intervalMs = it.filter(Char::isDigit).take(5) }, - label = { Text("Interval (ms, 20–10240)") }, - modifier = Modifier.fillMaxWidth(), - ) - OutlinedTextField( - value = timeoutSec, - onValueChange = { timeoutSec = it.filter(Char::isDigit).take(3) }, - label = { Text("Timeout (s, 0 = forever)") }, - modifier = Modifier.fillMaxWidth(), - ) - Text("TX power", style = MaterialTheme.typography.labelMedium, color = GlassTextDim) - Row( - modifier = Modifier - .fillMaxWidth() - .horizontalScroll(rememberScrollState()), - horizontalArrangement = Arrangement.spacedBy(6.dp), - ) { - Settings.TxPower.entries.forEach { power -> - FilterChip( - selected = txPower == power, + item { SectionHeader("Advertising") } + item { + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { + BrandCard(verticalSpacing = 10.dp) { + Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) { + OutlinedTextField( + value = intervalMs, + onValueChange = { intervalMs = it.filter(Char::isDigit).take(5) }, + label = { Text("Interval (ms)") }, + singleLine = true, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + modifier = Modifier.weight(1f), + ) + OutlinedTextField( + value = timeoutSec, + onValueChange = { timeoutSec = it.filter(Char::isDigit).take(3) }, + label = { Text("Timeout (s)") }, + singleLine = true, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + modifier = Modifier.weight(1f), + ) + } + Row( + modifier = Modifier + .fillMaxWidth() + .horizontalScroll(rememberScrollState()), + horizontalArrangement = Arrangement.spacedBy(6.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + "TX power", + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Settings.TxPower.entries.forEach { power -> + FilterChip( + selected = txPower == power, + onClick = { + txPower = power + vm.setTxPower(power) + }, + label = { Text("${power.raw} dBm") }, + ) + } + } + FilledTonalButton( onClick = { - txPower = power - vm.setTxPower(power) + vm.setAdvertising( + intervalMs = intervalMs.toIntOrNull() ?: 417, + timeoutSec = timeoutSec.toIntOrNull() ?: 0, + ) }, - label = { Text("${power.raw} dBm") }, - ) + ) { Text("Apply") } } + SectionFooter("Interval 20–10240 ms; timeout 0 advertises forever. TX power applies immediately.") } - Button( - onClick = { - vm.setAdvertising( - intervalMs = intervalMs.toIntOrNull() ?: 417, - timeoutSec = timeoutSec.toIntOrNull() ?: 0, - ) - }, - modifier = Modifier.fillMaxWidth(), - ) { Text("Apply Advertising") } } - } - item { SectionHeader("Maintenance") } - item { - GlassCard { - Button( - onClick = { vm.resetLed() }, - modifier = Modifier.fillMaxWidth(), - ) { Text("Reset LED") } - Button( - onClick = { showClearMacrosDialog = true }, - modifier = Modifier.fillMaxWidth(), - ) { Text("Clear Macros…") } - Button( - onClick = { showClearEventsDialog = true }, - modifier = Modifier.fillMaxWidth(), - ) { Text("Clear Events & Timers…") } - Button( - onClick = { showRestartDialog = true }, - modifier = Modifier.fillMaxWidth(), - ) { Text("Restart Board…") } - Text( - "Reset LED stops and clears the light immediately — if it relights after a " + - "disconnect, an on-board event is re-arming it; use Clear Events & Timers. " + - "Restart reboots the board without erasing logs or macros.", - style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, - ) + item { SectionHeader("Maintenance") } + item { + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { + GroupCard { + ActionRow("Reset LED", Icons.Filled.FlashlightOff, onClick = { vm.resetLed() }) + HorizontalDivider() + ActionRow( + "Clear Macros", + Icons.Filled.Memory, + tint = Palette.danger, + onClick = { showClearMacrosDialog = true }, + ) + HorizontalDivider() + ActionRow( + "Clear Events & Timers", + Icons.Filled.EventBusy, + tint = Palette.danger, + onClick = { showClearEventsDialog = true }, + ) + HorizontalDivider() + ActionRow("Restart Board", Icons.Filled.Refresh, onClick = { showRestartDialog = true }) + } + SectionFooter( + "Reset LED stops and clears the light immediately — if it relights after a disconnect, " + + "an on-board event is re-arming it; use Clear Events & Timers. Restart reboots the " + + "board without erasing logs or macros.", + ) + } } - } - item { SectionHeader("Danger zone") } - item { - GlassCard { - Text( - "Factory reset erases all on-board log entries, loggers, events, " + - "data processors, and macros, then reboots the board.", - style = MaterialTheme.typography.bodySmall, - color = GlassTextDim, - ) - Button( - onClick = { showResetDialog = true }, - colors = androidx.compose.material3.ButtonDefaults.buttonColors(containerColor = GlassError), - modifier = Modifier.fillMaxWidth(), - ) { Text("Factory Reset…") } + item { + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { + GroupCard { + ActionRow( + "Factory Reset", + Icons.Filled.Warning, + tint = Palette.danger, + onClick = { showResetDialog = true }, + ) + } + SectionFooter("Erases all logs, processors, events, macros, and timers on the board, then reboots it.") + } } } } if (showClearMacrosDialog) { - AlertDialog( - onDismissRequest = { showClearMacrosDialog = false }, - title = { Text("Clear macros?") }, - text = { Text("Removes every on-boot macro recorded on the board.") }, - confirmButton = { - TextButton(onClick = { - showClearMacrosDialog = false - vm.clearMacros() - }) { Text("Clear Macros", color = GlassError) } - }, - dismissButton = { - TextButton(onClick = { showClearMacrosDialog = false }) { Text("Cancel") } + ConfirmDialog( + title = "Clear all macros on this board?", + text = "Removes every on-boot macro — including the identity broadcast this app configures. " + + "The app re-applies its broadcast automatically on the next connect.", + confirmLabel = "Clear Macros", + destructive = true, + onConfirm = { + showClearMacrosDialog = false + vm.clearMacros() }, + onDismiss = { showClearMacrosDialog = false }, ) } if (showClearEventsDialog) { - AlertDialog( - onDismissRequest = { showClearEventsDialog = false }, - title = { Text("Clear events & timers?") }, - text = { - Text( - "Removes every on-board event binding and timer — from ALL apps, including " + - "this one's recording heartbeat. Use this if the LED keeps relighting " + - "after disconnects.", - ) - }, - confirmButton = { - TextButton(onClick = { - showClearEventsDialog = false - vm.clearEvents() - }) { Text("Clear Events & Timers", color = GlassError) } - }, - dismissButton = { - TextButton(onClick = { showClearEventsDialog = false }) { Text("Cancel") } + ConfirmDialog( + title = "Clear all events and timers?", + text = "Removes every on-board event binding and timer — from ALL apps, including this one's " + + "recording heartbeat. Use this if the LED keeps relighting after disconnects.", + confirmLabel = "Clear Events & Timers", + destructive = true, + onConfirm = { + showClearEventsDialog = false + vm.clearEvents() }, + onDismiss = { showClearEventsDialog = false }, ) } if (showRestartDialog) { - AlertDialog( - onDismissRequest = { showRestartDialog = false }, - title = { Text("Restart the board?") }, - text = { - Text( - "Reboots the board without erasing anything — logs and macros survive. " + - "The board will disconnect.", - ) - }, - confirmButton = { - TextButton(onClick = { - showRestartDialog = false - vm.restart() - }) { Text("Restart") } - }, - dismissButton = { - TextButton(onClick = { showRestartDialog = false }) { Text("Cancel") } + ConfirmDialog( + title = "Restart this MetaWear?", + text = "Reboots the board without erasing anything — logs and macros survive. The board will disconnect.", + confirmLabel = "Restart", + destructive = false, + onConfirm = { + showRestartDialog = false + vm.restart() }, + onDismiss = { showRestartDialog = false }, ) } if (showResetDialog) { - AlertDialog( - onDismissRequest = { showResetDialog = false }, - title = { Text("Factory reset?") }, - text = { - Text( - "All data on the board (logs, loggers, macros) will be erased and the " + - "board will reboot. This cannot be undone.", - ) - }, - confirmButton = { - TextButton(onClick = { - showResetDialog = false - vm.factoryReset() - }) { Text("Reset", color = GlassError) } - }, - dismissButton = { - TextButton(onClick = { showResetDialog = false }) { Text("Cancel") } + ConfirmDialog( + title = "Factory reset this MetaWear?", + text = "This will erase all on-device state. The board will reboot and disconnect.", + confirmLabel = "Reset", + destructive = true, + onConfirm = { + showResetDialog = false + vm.factoryReset() }, + onDismiss = { showResetDialog = false }, ) } } + +/** Centered confirmation dialog; the confirm action reads red when destructive. */ +@Composable +private fun ConfirmDialog( + title: String, + text: String, + confirmLabel: String, + destructive: Boolean, + onConfirm: () -> Unit, + onDismiss: () -> Unit, +) { + AlertDialog( + onDismissRequest = onDismiss, + title = { Text(title) }, + text = { Text(text) }, + confirmButton = { + TextButton(onClick = onConfirm) { + Text(confirmLabel, color = if (destructive) Palette.danger else LocalContentColor.current) + } + }, + dismissButton = { + TextButton(onClick = onDismiss) { Text("Cancel") } + }, + ) +} diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/stream/LiveStreamScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/stream/LiveStreamScreen.kt index 5408b3a..a8ac6e1 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/stream/LiveStreamScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/stream/LiveStreamScreen.kt @@ -1,15 +1,25 @@ package com.mbientlab.metawear.app.ui.stream import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Pause +import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.material.icons.filled.Share +import androidx.compose.material.icons.filled.Stop import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState @@ -18,6 +28,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import com.mbientlab.metawear.app.core.SensorKey @@ -26,176 +37,169 @@ import com.mbientlab.metawear.app.export.CsvShare import com.mbientlab.metawear.app.export.ExportFilename import com.mbientlab.metawear.app.export.LiveBufferCsvExporter import com.mbientlab.metawear.app.ui.appViewModel -import com.mbientlab.metawear.app.ui.components.ErrorBanner +import com.mbientlab.metawear.app.ui.components.AppScaffold +import com.mbientlab.metawear.app.ui.components.BrandCard import com.mbientlab.metawear.app.ui.components.FusionCalibrationBadge -import com.mbientlab.metawear.app.ui.components.GlassCard -import com.mbientlab.metawear.app.ui.components.LineChart +import com.mbientlab.metawear.app.ui.components.Notice +import com.mbientlab.metawear.app.ui.components.SensorChartCard +import com.mbientlab.metawear.app.ui.components.SessionStatsBar import com.mbientlab.metawear.app.ui.components.TaredQuaternionCube -import com.mbientlab.metawear.app.ui.theme.ChannelColors -import com.mbientlab.metawear.app.ui.theme.FourChannelColors -import com.mbientlab.metawear.app.ui.theme.GlassTextDim +import com.mbientlab.metawear.app.ui.components.icon +import com.mbientlab.metawear.app.ui.theme.Palette import com.mbientlab.metawear.app.vm.Channel import com.mbientlab.metawear.app.vm.DeviceViewModel import com.mbientlab.metawear.app.vm.StreamSessionViewModel import java.util.Locale import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +/** + * Configure → stream in one screen: the sensor picker with a Start action + * until streaming begins, then the stats bar, calibration card, and one + * chart card per channel with Export / Pause / Stop in the top bar. + */ @Composable -fun LiveStreamScreen() { +fun LiveStreamScreen(onBack: () -> Unit) { val deviceVm = appViewModel(::DeviceViewModel) val vm = appViewModel(::StreamSessionViewModel) + val context = LocalContext.current val modules by deviceVm.modules.collectAsState() val channels by vm.channels.collectAsState() val isStreaming by vm.isStreaming.collectAsState() val isPaused by vm.isPaused.collectAsState() val isBusy by vm.isBusy.collectAsState() + val startedAt by vm.startedAt.collectAsState() val lastError by vm.lastError.collectAsState() - val calibration by vm.calibration.collectAsState() var selections by remember { mutableStateOf(listOf(SensorSelection(SensorKey.ACCELEROMETER))) } - LazyColumn( - modifier = Modifier - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(10.dp), - ) { - item { Text("Live Stream", style = MaterialTheme.typography.headlineSmall) } - item { ErrorBanner(lastError) { vm.clearError() } } + fun exportAll() { + val timestamp = Clock.System.now() + val files = channels.map { channel -> + val csv = LiveBufferCsvExporter.export(channel.captureBuffer(), channel.selection) + val filename = ExportFilename.make( + deviceName = deviceVm.displayName, + sensorTag = channel.selection.key.shortTag, + timestamp = timestamp, + ) + filename to csv + } + CsvShare.shareAll(context, files) + } - if (!isStreaming) { - item { - SensorConfigSection( - modules = modules, - selections = selections, - onSelectionsChange = { selections = it }, - ) - } - item { + AppScaffold( + title = if (isStreaming) "Live Stream" else "Configure Stream", + onBack = onBack, + notice = lastError?.let { Notice(it) { vm.clearError() } }, + actions = { + if (!isStreaming) { Button( onClick = { vm.start(selections) }, enabled = !isBusy && selections.isNotEmpty(), - modifier = Modifier.fillMaxWidth(), - ) { Text("Start Streaming") } - } - } else { - item { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp), + colors = ButtonDefaults.buttonColors(containerColor = Palette.success, contentColor = Color.White), + modifier = Modifier.padding(end = 8.dp), ) { - OutlinedButton( - onClick = { vm.togglePause() }, - enabled = !isBusy, - modifier = Modifier.weight(1f), - ) { Text(if (isPaused) "Resume" else "Pause") } - Button( - onClick = { vm.stop() }, - enabled = !isBusy, - modifier = Modifier.weight(1f), - ) { Text("Stop & Save") } + Icon(Icons.Filled.PlayArrow, contentDescription = null, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(4.dp)) + Text("Start") + } + } else { + IconButton(onClick = ::exportAll, enabled = channels.isNotEmpty()) { + Icon(Icons.Filled.Share, contentDescription = "Export CSV") + } + IconButton(onClick = { vm.togglePause() }, enabled = !isBusy) { + Icon( + if (isPaused) Icons.Filled.PlayArrow else Icons.Filled.Pause, + contentDescription = if (isPaused) "Resume" else "Pause", + ) + } + IconButton(onClick = { vm.stop() }, enabled = !isBusy) { + Icon(Icons.Filled.Stop, contentDescription = "Stop", tint = Palette.danger) } } - - calibration?.let { item { FusionCalibrationBadge(it) } } - - items(channels, key = { it.id }) { channel -> - ChannelCard(channel) + }, + ) { padding -> + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues( + start = 12.dp, + end = 12.dp, + top = padding.calculateTopPadding() + 4.dp, + bottom = padding.calculateBottomPadding() + 24.dp, + ), + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { + if (!isStreaming) { + item { + SensorConfigSection( + modules = modules, + selections = selections, + onSelectionsChange = { selections = it }, + ) + } + } else { + startedAt?.let { started -> + item { StatsBar(started, channels) } + } + calibration?.let { item { FusionCalibrationBadge(it) } } + items(channels, key = { it.id }) { channel -> + ChannelCard(channel) + } } } } } +/** Stats strip whose sample total is summed live across every channel. */ +@Composable +private fun StatsBar(startedAt: Instant, channels: List) { + var total = 0 + channels.forEach { channel -> + val ui by channel.ui.collectAsState() + total += ui.totalSamples + } + SessionStatsBar(startedAt = startedAt, totalSamples = total) +} + @Composable private fun ChannelCard(channel: Channel) { val ui by channel.ui.collectAsState() - val style = channel.selection.key.axisStyle - val colors = if (style.chartChannels == 4) FourChannelColors else ChannelColors - val context = LocalContext.current + val key = channel.selection.key + val style = key.axisStyle - GlassCard { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - ) { - Text(channel.selection.displayLabel, style = MaterialTheme.typography.titleSmall) - if (!channel.selection.key.isPolled) { - Text( - String.format(Locale.US, "%.1f Hz", ui.effectiveHz), - style = MaterialTheme.typography.labelMedium, - color = MaterialTheme.colorScheme.primary, - ) - } + Column(verticalArrangement = Arrangement.spacedBy(10.dp)) { + // Live 3D orientation for the quaternion output — tared to a reference + // pose, with a Zero button to re-zero on demand. + if (key == SensorKey.FUSION_QUATERNION) { + BrandCard { TaredQuaternionCube(latest = ui.latest) } } - val latest = ui.latest - if (channel.selection.key.isPolled) { - // Readout tile: one big current value plus the (trivially reused) - // chart of recent readings. - Text( - latest?.let { String.format(Locale.US, "%.2f %s", it.f0, style.unit) } ?: "waiting…", - style = MaterialTheme.typography.headlineMedium, - color = MaterialTheme.colorScheme.primary, - ) - if (ui.displayBuffer.size >= 2) { - LineChart( - samples = ui.displayBuffer, - channelCount = 1, - colors = colors, - yRange = style.yRange, + SensorChartCard( + title = key.title, + icon = key.icon, + samples = ui.displayBuffer, + latest = ui.latest, + style = style, + rateLabel = if (key.isPolled) { + "every " + SensorSelection.formatPollInterval(channel.selection.effectivePollIntervalMs) + } else if (ui.effectiveHz > 0) { + String.format(Locale.US, "%.1f Hz", ui.effectiveHz) + } else { + null + }, + footer = { + Text( + "${ui.totalSamples} samples" + + (if (!key.isPolled) " · plotting 1/${channel.displayStride}" else "") + + (style.unit.takeIf { it.isNotEmpty() }?.let { " · $it" } ?: ""), + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, ) - } - Text( - "${ui.totalSamples} readings · every " + - SensorSelection.formatPollInterval(channel.selection.effectivePollIntervalMs), - style = MaterialTheme.typography.labelSmall, - color = GlassTextDim, - ) - } else { - // Live 3D orientation cube for the quaternion output — tared to - // a reference pose, with a Zero button to re-zero on demand. - if (channel.selection.key == SensorKey.FUSION_QUATERNION) { - TaredQuaternionCube(latest = latest) - } - LineChart( - samples = ui.displayBuffer, - channelCount = style.chartChannels, - colors = colors, - yRange = style.yRange, - ) - - // Live per-axis readout. - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(12.dp), - ) { - style.labels.take(style.chartChannels).forEachIndexed { index, label -> - Text( - "$label ${latest?.let { String.format(Locale.US, "%+.3f", it.channel(index)) } ?: "—"}", - style = MaterialTheme.typography.bodySmall, - color = colors.getOrElse(index) { GlassTextDim }, - ) - } - } - Text( - "${ui.totalSamples} samples · plotting 1/${channel.displayStride}" + - (style.unit.takeIf { it.isNotEmpty() }?.let { " · $it" } ?: ""), - style = MaterialTheme.typography.labelSmall, - color = GlassTextDim, - ) - } - androidx.compose.material3.TextButton(onClick = { - val csv = LiveBufferCsvExporter.export(channel.captureBuffer(), channel.selection) - val filename = ExportFilename.make( - deviceName = "MetaWear", - sensorTag = channel.selection.key.shortTag, - timestamp = Clock.System.now(), - ) - CsvShare.share(context, filename, csv) - }) { Text("Export buffer CSV") } + }, + ) } } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/stream/SensorConfigSection.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/stream/SensorConfigSection.kt index 2c3b317..813d766 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/stream/SensorConfigSection.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/stream/SensorConfigSection.kt @@ -1,16 +1,17 @@ package com.mbientlab.metawear.app.ui.stream +import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.material3.Checkbox import androidx.compose.material3.FilterChip +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -18,10 +19,12 @@ import androidx.compose.ui.unit.dp import com.mbientlab.metawear.app.core.BandwidthAdvisor import com.mbientlab.metawear.app.core.SensorKey import com.mbientlab.metawear.app.core.SensorSelection -import com.mbientlab.metawear.app.ui.components.GlassCard +import com.mbientlab.metawear.app.ui.components.BandwidthCard +import com.mbientlab.metawear.app.ui.components.BrandCard import com.mbientlab.metawear.app.ui.components.SectionHeader -import com.mbientlab.metawear.app.ui.theme.GlassTextDim -import com.mbientlab.metawear.app.ui.theme.GlassWarn +import com.mbientlab.metawear.app.ui.components.formatHz +import com.mbientlab.metawear.app.ui.components.icon +import com.mbientlab.metawear.app.ui.theme.Palette import com.mbientlab.metawear.model.ModuleInfo import com.mbientlab.metawear.protocol.Module @@ -48,10 +51,12 @@ fun availableSensors(modules: Map): List { /** * Multi-select sensor picker with per-sensor rate/range chips (streamed) or * polling-interval chips (polled readables), plus the bandwidth advisory. - * Shared by the live-stream and logging screens. + * Shared by the live-stream, logging, and group-logging screens. * * @param loggingMode hides stream-only signals (altitude) that can't be * captured to flash. + * @param locked disables every control — used while a log session is + * recording so the configuration can't be edited mid-session. */ @Composable fun SensorConfigSection( @@ -61,6 +66,7 @@ fun SensorConfigSection( loggingMode: Boolean = false, /** Kinds hidden outright (e.g. group logging excludes temperature/humidity). */ excludeKeys: Set = emptySet(), + locked: Boolean = false, ) { val available = availableSensors(modules) .filter { !loggingMode || it.canLog } @@ -72,30 +78,21 @@ fun SensorConfigSection( if (motion.isNotEmpty()) { SectionHeader("Motion & Fusion") motion.forEach { key -> - SensorCard(key, selections, onSelectionsChange) + SensorCard(key, selections, onSelectionsChange, locked) } } if (environmental.isNotEmpty()) { SectionHeader("Environmental") environmental.forEach { key -> - SensorCard(key, selections, onSelectionsChange) + SensorCard(key, selections, onSelectionsChange, locked) } } - if (BandwidthAdvisor.isOverCeiling(selections)) { - GlassCard { - Text( - "Combined rate ${BandwidthAdvisor.aggregateHz(selections).toInt()} Hz exceeds the " + - "~${BandwidthAdvisor.BLE_SAFE_CEILING_HZ.toInt()} Hz BLE ceiling — samples may drop.", - style = MaterialTheme.typography.bodySmall, - color = GlassWarn, - ) - TextButton(onClick = { onSelectionsChange(BandwidthAdvisor.halved(selections)) }) { - Text("Halve all rates") - } - } - } + BandwidthCard( + aggregateHz = BandwidthAdvisor.aggregateHz(selections), + onHalve = { onSelectionsChange(BandwidthAdvisor.halved(selections)) }, + ) } } @@ -104,23 +101,32 @@ private fun SensorCard( key: SensorKey, selections: List, onSelectionsChange: (List) -> Unit, + locked: Boolean, ) { val selection = selections.firstOrNull { it.key == key } - GlassCard { + val onToggle: (Boolean) -> Unit = { checked -> + onSelectionsChange( + if (checked) selections + SensorSelection(key) + else selections.filterNot { it.key == key }, + ) + } + BrandCard(onClick = if (locked) null else ({ onToggle(selection == null) })) { Row( modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, + horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically, ) { - Text(key.title, style = MaterialTheme.typography.titleSmall) + Icon( + key.icon, + contentDescription = null, + tint = if (selection != null) Palette.accent else MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.size(22.dp), + ) + Text(key.title, style = MaterialTheme.typography.titleSmall, modifier = Modifier.weight(1f)) Checkbox( checked = selection != null, - onCheckedChange = { checked -> - onSelectionsChange( - if (checked) selections + SensorSelection(key) - else selections.filterNot { it.key == key }, - ) - }, + onCheckedChange = onToggle, + enabled = !locked, ) } if (selection != null) { @@ -128,11 +134,11 @@ private fun SensorCard( onSelectionsChange(selections.map { if (it.key == key) updated else it }) if (key.isPolled) { - IntervalRow(selection, ::update) + IntervalRow(selection, ::update, locked) } else { - RateRow(selection, ::update) + RateRow(selection, ::update, locked) if (key.rangeOptions.isNotEmpty()) { - RangeRow(selection, ::update) + RangeRow(selection, ::update, locked) } } } @@ -140,26 +146,28 @@ private fun SensorCard( } @Composable -private fun RateRow(selection: SensorSelection, onChange: (SensorSelection) -> Unit) { +private fun RateRow(selection: SensorSelection, onChange: (SensorSelection) -> Unit, locked: Boolean) { ChipRow("Rate") { selection.key.rateOptionsHz.forEach { hz -> FilterChip( selected = selection.hz == hz, onClick = { onChange(selection.copy(hz = hz)) }, label = { Text(formatHz(hz)) }, + enabled = !locked, ) } } } @Composable -private fun RangeRow(selection: SensorSelection, onChange: (SensorSelection) -> Unit) { +private fun RangeRow(selection: SensorSelection, onChange: (SensorSelection) -> Unit, locked: Boolean) { ChipRow("Range") { selection.key.rangeOptions.forEach { range -> FilterChip( selected = selection.range == range, onClick = { onChange(selection.copy(range = range)) }, label = { Text("±${range.toInt()} ${selection.key.rangeUnit}") }, + enabled = !locked, ) } } @@ -167,13 +175,14 @@ private fun RangeRow(selection: SensorSelection, onChange: (SensorSelection) -> /** Polling-interval chips (1 s … 5 m) for the environmental readables. */ @Composable -private fun IntervalRow(selection: SensorSelection, onChange: (SensorSelection) -> Unit) { +private fun IntervalRow(selection: SensorSelection, onChange: (SensorSelection) -> Unit, locked: Boolean) { ChipRow("Every") { selection.key.pollIntervalOptionsMs.forEach { intervalMs -> FilterChip( selected = selection.effectivePollIntervalMs == intervalMs, onClick = { onChange(selection.withPollInterval(intervalMs)) }, label = { Text(SensorSelection.formatPollInterval(intervalMs)) }, + enabled = !locked, ) } } @@ -188,10 +197,11 @@ private fun ChipRow(label: String, content: @Composable () -> Unit) { horizontalArrangement = Arrangement.spacedBy(6.dp), verticalAlignment = Alignment.CenterVertically, ) { - Text(label, style = MaterialTheme.typography.labelMedium, color = GlassTextDim) + Text( + label, + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) content() } } - -private fun formatHz(hz: Double): String = - if (hz == hz.toLong().toDouble()) "${hz.toLong()} Hz" else "$hz Hz" diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/theme/Palette.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/theme/Palette.kt new file mode 100644 index 0000000..921ed6e --- /dev/null +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/theme/Palette.kt @@ -0,0 +1,47 @@ +package com.mbientlab.metawear.app.ui.theme + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.lerp +import androidx.compose.ui.graphics.luminance + +/** + * Brand palette. The accent is the MetaWear orange (#FE9500) sampled flat + * from the original connect screen; the semantic colors are deliberately + * soft so they read apart from it — the warning gold is pushed toward + * yellow, the danger red toward coral, and the info blue is dusty rather + * than saturated. + */ +object Palette { + /** Brand orange — the primary color in both light and dark schemes. */ + val accent = Color(0xFFFE9500) + + /** Soft teal-green: connected, calibrated, saved. */ + val success = Color(red = 0.30f, green = 0.70f, blue = 0.55f) + + /** Gold: connecting, idle, over-budget, bootloader. */ + val warning = Color(red = 0.93f, green = 0.74f, blue = 0.20f) + + /** Softened coral: disconnected, logging (recording), destructive. */ + val danger = Color(red = 0.90f, green = 0.40f, blue = 0.45f) + + /** Dusty blue: signal strength, streaming, medium calibration. */ + val info = Color(red = 0.40f, green = 0.60f, blue = 0.80f) + + /** Mid gray: offline / unknown. */ + val neutral = Color(red = 0.55f, green = 0.55f, blue = 0.55f) +} + +/** True when the active color scheme is the dark one. */ +@Composable +fun isDarkScheme(): Boolean = MaterialTheme.colorScheme.background.luminance() < 0.5f + +/** + * The palette color as it should be used for small TEXT: unchanged on dark + * surfaces, pulled a third of the way toward black on light ones so gold, + * teal, and orange labels stay legible on cream. Containers and icons keep + * the pure tint — only type needs the extra contrast. + */ +@Composable +fun Color.forText(): Color = if (isDarkScheme()) this else lerp(this, Color.Black, 0.35f) diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/theme/Theme.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/theme/Theme.kt index 3b622e5..43634ef 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/theme/Theme.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/theme/Theme.kt @@ -2,25 +2,21 @@ package com.mbientlab.metawear.app.ui.theme import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Typography import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight -// Dark-first "glass" palette: deep blue-black backgrounds, translucent card -// surfaces, cool cyan accent. (The app is dark-themed in both system modes.) - -val GlassBackground = Color(0xFF0B0F14) -val GlassSurface = Color(0xFF141B24) -val GlassSurfaceHigh = Color(0xFF1C2530) -val GlassAccent = Color(0xFF4FC3F7) -val GlassAccentDim = Color(0xFF2A5A70) -val GlassText = Color(0xFFE4EAF1) -val GlassTextDim = Color(0xFF8A97A6) -val GlassWarn = Color(0xFFFFB74D) -val GlassError = Color(0xFFEF6E6E) -val GlassGood = Color(0xFF69D18C) - -// Chart channel colors (x/red, y/green, z/blue, w/extra purple). +// Brand-first Material 3 theme: the MetaWear orange is the primary color in +// BOTH light and dark schemes (dynamic color is deliberately not applied so +// the brand always wins), the semantic colors come from [Palette], and the +// surfaces are warm-tinted neutrals so cards and lists sit comfortably next +// to the orange without competing with it. + +// Chart channel colors (x/red, y/green, z/blue, w/extra purple). These are +// DATA colors, not brand — kept distinct from the palette on purpose. val ChannelColors = listOf( Color(0xFFEF6E6E), // red Color(0xFF69D18C), // green @@ -36,26 +32,100 @@ val FourChannelColors = listOf( Color(0xFF5D9BF7), // z / yaw ) +private val LightScheme = lightColorScheme( + primary = Palette.accent, + // Dark brown on the orange (the launcher icon's "m" on its field) — far + // more legible than white at button sizes and just as on-brand. + onPrimary = Color(0xFF3D2200), + primaryContainer = Color(0xFFFFDDB8), + onPrimaryContainer = Color(0xFF4A2A00), + inversePrimary = Color(0xFFFFB866), + secondary = Color(0xFF7A5A3A), + onSecondary = Color.White, + secondaryContainer = Color(0xFFFFE6CC), + onSecondaryContainer = Color(0xFF3F2A12), + tertiary = Palette.info, + onTertiary = Color.White, + tertiaryContainer = Color(0xFFD9E6F5), + onTertiaryContainer = Color(0xFF1B3550), + error = Palette.danger, + onError = Color.White, + errorContainer = Color(0xFFFFDAD9), + onErrorContainer = Color(0xFF5C1A21), + background = Color(0xFFFFF8F1), + onBackground = Color(0xFF211A14), + surface = Color(0xFFFFF8F1), + onSurface = Color(0xFF211A14), + surfaceVariant = Color(0xFFF3E6D8), + onSurfaceVariant = Color(0xFF52463C), + surfaceContainerLowest = Color(0xFFFFFFFF), + surfaceContainerLow = Color(0xFFFDF2E8), + surfaceContainer = Color(0xFFF9EDE0), + surfaceContainerHigh = Color(0xFFF3E7DA), + surfaceContainerHighest = Color(0xFFEDE1D4), + outline = Color(0xFF85766A), + outlineVariant = Color(0xFFD8C9BB), + inverseSurface = Color(0xFF362F29), + inverseOnSurface = Color(0xFFFBEFE5), + scrim = Color.Black, +) + private val DarkScheme = darkColorScheme( - primary = GlassAccent, - onPrimary = Color(0xFF06222E), - secondary = GlassAccentDim, - onSecondary = GlassText, - background = GlassBackground, - onBackground = GlassText, - surface = GlassSurface, - onSurface = GlassText, - surfaceVariant = GlassSurfaceHigh, - onSurfaceVariant = GlassTextDim, - error = GlassError, + primary = Palette.accent, + onPrimary = Color(0xFF3D2200), + primaryContainer = Color(0xFF6A3E00), + onPrimaryContainer = Color(0xFFFFDDB8), + inversePrimary = Color(0xFF9A5A00), + secondary = Color(0xFFE6C3A0), + onSecondary = Color(0xFF432D14), + secondaryContainer = Color(0xFF5C4328), + onSecondaryContainer = Color(0xFFFFE6CC), + tertiary = Palette.info, + onTertiary = Color(0xFF0E2438), + tertiaryContainer = Color(0xFF2E4A66), + onTertiaryContainer = Color(0xFFD9E6F5), + error = Palette.danger, + onError = Color(0xFF3F0A12), + errorContainer = Color(0xFF7A2E36), + onErrorContainer = Color(0xFFFFDAD9), + background = Color(0xFF15110D), + onBackground = Color(0xFFEDE1D6), + surface = Color(0xFF15110D), + onSurface = Color(0xFFEDE1D6), + surfaceVariant = Color(0xFF3B322A), + onSurfaceVariant = Color(0xFFCDBEB0), + surfaceContainerLowest = Color(0xFF0F0C09), + surfaceContainerLow = Color(0xFF1C1712), + surfaceContainer = Color(0xFF221C16), + surfaceContainerHigh = Color(0xFF2C251E), + surfaceContainerHighest = Color(0xFF362E26), + outline = Color(0xFF968678), + outlineVariant = Color(0xFF4C4138), + inverseSurface = Color(0xFFEDE1D6), + inverseOnSurface = Color(0xFF362F29), + scrim = Color.Black, ) +/** Default Material type scale with semibold headline/title weights. */ +private val AppTypography: Typography = Typography().let { base -> + base.copy( + headlineLarge = base.headlineLarge.copy(fontWeight = FontWeight.SemiBold), + headlineMedium = base.headlineMedium.copy(fontWeight = FontWeight.SemiBold), + headlineSmall = base.headlineSmall.copy(fontWeight = FontWeight.SemiBold), + titleLarge = base.titleLarge.copy(fontWeight = FontWeight.SemiBold), + titleMedium = base.titleMedium.copy(fontWeight = FontWeight.SemiBold), + titleSmall = base.titleSmall.copy(fontWeight = FontWeight.SemiBold), + ) +} + @Composable -fun MetaWearTheme(content: @Composable () -> Unit) { - // Dark in both modes — the glass look is dark-first by design. - isSystemInDarkTheme() +fun MetaWearTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit, +) { MaterialTheme( - colorScheme = DarkScheme, + colorScheme = if (darkTheme) DarkScheme else LightScheme, + typography = AppTypography, content = content, ) } diff --git a/app/src/main/res/drawable/ic_launcher_monochrome.xml b/app/src/main/res/drawable/ic_launcher_monochrome.xml new file mode 100644 index 0000000..e398243 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_monochrome.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml new file mode 100644 index 0000000..f81e273 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi/ic_launcher.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml new file mode 100644 index 0000000..f81e273 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml @@ -0,0 +1,10 @@ + + + + + + + diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..54b9f1786fb5c182eadbb194bc2915b87a5731c0 GIT binary patch literal 8703 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 AlmGw# literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..68f270b9c025412a953aebdeb6d704ac40b5454d GIT binary patch literal 17499 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..efbbaefccfdc01e1fb17cc200fe479850cc2f9d9 GIT binary patch literal 58616 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%0AjnjL1aG)^w#PA5$t*Q$*_ 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%0AB z{=8QkJ-stiTxId0)9YDZW}`zCJL`PMOy-4-5uNwyj8)P{!)QrE^gn>3@$_|yREZ#A%uHO!^RNth}b1BUGT`% z^DKE6lp|8v?y`J(H|rZxhY6b>)5X4w$K7d>C!m5PeUZWzW&4Z$VxN@(1*$8X!Au(m@4p_?; z2~ib^#i?uc3l5Mav+{{yW?pu#O=q`vc8;=7qDlj_*+5KW?up+jbL1*sotzyAxmS6_ zKv`sP35nQf`S^z%YVX3LHZ23;pcu~8($(6x$NOvzneZ_pI!R{qlOynug~_JSmx{`U z)cM-xs2k|TD=I-CTJl1|29CrZtsc%1?tYnbzcjh%U7_t@O=n*A2Mn7PZ@p>_9Qjd&CPKkbsxjKj z0Dvr$quKU-2CXdEvHM~wy8}RvTq)c1!jcZ|nPm-4Q0m~qDS}cSXbij(#xat~vKw%StA zLRH>I1d1h!Igv@OC|dU$YIF^Z_ngn3Vu(Hy;^B~*g4_6MQAZZb+H3l+?989S-b5Mr zX%wXu5PnjMwG1BIU;5CputpH^a?-=j-ks<6Uf##wkVjgaw6;i5?Gfx@#Il5>Ry1-+ zyz&)N#tijqo{9F$CPqMa++5aTP@p1}kY5UrpB(Ovc33amn(Xb2X2U@43TUI!7&Xhb zHD_ec=n&Eb2-P1`8s;=S$eWL6$Xi(y@Wj~g5c8_2;%&-As<~B$NZM=ey$lis6%FX}={JH?6&GWOvPlnbHHf%x@^e7l2h} zG%~cPlohZjMZHNI?=ei=VTbkjkjZPTY7k)n9@s0iC~Uz7GaNsfWKI*T%{{<_b2oU{ zIojmGZ-0f+Ae98_Ibk-(3)Q!%l1`$2l~0E`$Z4tKYt3C^y0mX=`iEI`WDBW&x z>}_cTeK@i^e#nG>6JsaFZ}?VzLfy8yONJL}oP1FY+?MfLq}ZS!dvDM9ngJ6USL`)M zPy8uDL9ju;D!vF5L?bRn!&*PgV&|)q2d;G%yl8@oO=&ND>)>1e@$`jnGBCKjyuH2i z*2VIZ%kvMmmLFeUo-#I4qSnroq801}NRfDBv5-au0FX$agt^g9Z9>BV^IVO+Jj<1* zo((944_6l3DC`pqz8pN^)4`MdzEQg271S^&Uj$9bxI?yc<(3{f2)W1uYQX8k1H%ch zN4ylQZD3`MZ1Lc`5)XXZ%aCS%&Ks<(zw6isVilvL=2)zI(}Y&Co-9{^$u$JHRH+gb z2m>p8SeU5Vnx94dVW`4*)ZtNKVnSs4$^C#EPSq7*4?IpBKRkAbiS<2$K}d zG5f{k>GFazVs@r{gUx5?tYQM$NOTMmP8cW-w~`S|q6P#M2s%iaJT4cb+0Fg8{=xM2 zw}Lq^Bc7VRbGh?|8+hj*UM_xdxnkik2ZpC_Ev10ikwQZ7USWw8=?`>_hIuQ|2NPWQ zHXMe+>tF>XR}B%6mBzJaj<+~!V8Q#P>;$B6K}MG$6|l8x>y|gj8TJr&XsDJ;CyfWk zKNn~6Fxul4TO}dCx@M;)>>8xju|Z?UhkLg`k(suWZSF zY@pCWHIA8QeU%9=U^heA%Vx^6@j#>$7b>dd!xMo!f8$z`A_rmDNUKBC%U;Rz6?2-! zF=qy~LYDuUP;54CJHI*~`X8Fe z9LgG?S~U>I3*j#GrXjhwZ; zw@74w;LpvXAcm?&6nQ)4%1kcU;?Nu(O2|VAsHz`tZk7 zSW3^ zL?WxdAeE>`bmDyHr{{OyJ-`2p)5F`-+uxks`Of(8c^>cy;}(SJc=yib-kX#8%iELp zxAHbd0p15YV?t#<@)qabH6zrR}iFnB;0jBvqABkpK1$crBF=wkePW$|E`ES}!Y z2&|DUP}vo}5`8puar}Vgp9%Y%clKmjbX^zT0a>!7s!Smih61nKByRwNq$Cy_yO2*G z@-Q?lc+C&xyLPI$)S|h5d*#=H>bnPvZ`{f#V|KT_(71U9AUq8}oD`_Kt08u@C?YX5 zV$kKZ08Y zTQV;>qkQM^#@W43)B?mT$eU3-UOFFn45zxqPN|QmKB`ewASWfz1!Os_@$8td=#2Mo z_%Z;?d&^j3*>Eh8xF?V=aWK^MQDp*<+`Jc;`N?#k!|W(|yFM$`R+xBlMYH~)~)*#7IkJHP+)(_j8?XP`g)mN{Gf;5t6wk+&a5BUQ4hU16XSacJTq5UKJZ$Ehqc8_TT)yqi_Dh(S##P zbl413Tx{3YX)pr=6md%nDS|l!9I}cSXz`ku5%cpEw#duDf&gCpu9jyX|K$Q(Bw+5! zjkA6jof-ICZ6^3!XrvRaaZ z8eX7gMCdWMYV%n_YkoE{7T)374U>dM(yWarM6Ldd-J7o-{l-7qd-?mcU916R?H)ZH zzct;vedp|plk9!P_wiJfa!ocSr|C_N71dK%CSBc8RW@zwYsL=T4UQcx~ z+I{}Dqj&z{?u}Pz>Z|j|?#pEL>g+Mq%$Di-IVTY^A%I992&yiaaXwLp`c z6|DLE7XZ+6wLrSTGVs9LT%P~gd5A3+IuI3XZkxtL-m=>dN*8d>q;EVuAMG)@^Pyp@ zq5QTYgQ^Pyw-KYRj#jc+(5`{>XQ)W5@k17wLw@17%9A?yssILX^K$L({_EdA`qm%s z+;~|;C}nZ?;`INyTz0uV$VSJS1 zDjxq_YKdU(!biO7gXKXgWuJ+8S&p_Q7xvo}4{M?!Ag_agm<$njH(JuS7BDM(p;0Ew zPJnn73;5ze9A!D;ksn8}zTjnge&U+~CnSiL?1AucU-1Bp9DIyxqLwWz(C?U}80uECU7zim!$Er6xJ2G%+cm-zg=DGn9+^v-~8xqiMgDwt?Mf zNIU~5-o018f8*c#$8^UTdY^C2-&>vio2?7ZAt5u4neJuA=bx@mpF3DQIy-+jJAZhL zXR`S^8ZKQti}_3UU&UzGo4_apg01>BM~;skPp@q;#@ZgwA2F7v;;|G!^}AM5#cUE; z(ob%B58pN|n}Bzb8yG=)#3=hJTrDIL&aujpAV$F=DfQp7GzAR~{2C4jy|oSC5#=Md zydOh>g#$t7C|aisx>}TK4`dQirfW=6OcN7B{&|6bMhIHWlvc!`!Z?Nv6;~7`nC$gW z?oCDom7kkHiU-!!Cs{KfC*r(~tgg@$i??UZ)m}#hcN;Sx_XU;ab|D7N-+B6HYD& z=S}*STcC~Nn(#UnRIr7$;j3aF_R$sMFSl5qbNI;OGskbnun@G!V0PIB-Yiw&HIj6@ z++?PVCJAI}xv9W>_P)?~r=PL{gVpxnCeMIY+pciSF0ha`d<1h){%25VG zSz#JU?sMVxDnE6%2H1${6msxV5#rXiO~>F#ZzThK^Jdn`FaPEFXaDx#m21;mEX~Yn ze%BB+xlt|~AAfT+{TB1V-Rolv1y<(|f4M>L>!KT~dln?pA%Y-P4Y;iX<|SEwF5`%V z4M$S+g_Xr`;?JU8-_wty&wN^DaE)nPtoiX)$#i$`rN2Ao%Q+_xG$4%%V`I8g1Hq7{ zX3#kdI3x(e#HnFHu@xsnu6xtn8n-{imbCzi`N3iS0gwfA9ctXGX?bB3W!`vmGTl3W z`~}@$^Av(G7n27@2?)bieY(JF(Tp{~31D3bwzv3S%=l{>O z^DnQ=f+zdDq)fe9&(x_Hg;0RpCdJgK!sL?9q^}IB(08M-zw!bhXwB;5kG}cG)7{bO zXYZYV{_gz2M||Q6Q{jL(Erw3KdRu@sQwxeM909RO&eZrs!9>n4D6;%lyy{IsqCMjZ z7@61=tO!Pq^V zb8KbvDCSX|Zm6~>50FOoUOc@1oomOR9@7dg7kqG#;WmRIR%@3yXY6!)#I^?B*ZIxS z-rv65&RRW-Yq6^CqQ3B@Ehd1+PR0bGe%Hr8|n2gOX1Mh5AB9T0z(>t&neM zJF2m+JJ}W~vORH4N=Qs%q1xAct)a&U&+Wf@fK4nO@in>k&hGw|b)gj4YI>(;g|S^k zHlzPlrF+|wY1Krv4#66)Z4~R+lrJg0#t3Kn{5K|hH{9;2zu4M2x1kVM9`=87x%(VEPnoeEfAD9=AO4x&bimfDeRr~J%zxupgr&R{ zJlZ>A^x|qV|V((cb1PoT-^I7m#a@jWS^|7;ytFPAN}d* zlb`P1e2q0;+@T14G6B(Ia)a@&q!T8t8cv66(p}76c$%zs?~o$*dKrSq|w>Ey;MyU)Fe5zOy@xOn_27Ma6_ z5I1$CIOwMg8da=W(qgqL&AJcjAi+R=Z|CrZ9nju-op}PTE)U5reEluQK1EU&s+^?q zLfnK`w%?e(@b==#=kUObr?rmQgmctody+D1O^*0=;1wFo=53NnbCf_@>$U-4$fvB2 zkP>Syl?z*7z$IOQ29;IWjsZ51TYeB5tdy<4&H`mwJB0$t&t?S%rR{!en{6Z6x56g3 z(@%b~wK{QdXiF^x*BFC+)b~%0?>m}?mPpG_OBe^r-6Kui%$85j!|3AK+gDJk_t#Azer zB#?-gJqI^`^B-S(=MQ*|72kt>J33Sg^0`(erK!T$UI`FAR2z03q9=YGBVSI3LaPu> zO0Kj&UxWx1hlVsEdcWzx&hGxs^_O|X#q;<1=Rb8TsxTB+&sqbh!jFSR zGgW{oWgzMYTg=*0GQ9u>UDa8JIb?))5wsv+@Kj3O_Dp|a2LjDaKxrk?t`^@2L)MViaYxRo^0=ac6by}&gBkc43a3!NTbCFZZmdTE zDuaq#Jo@Cp5B|x?r|)U+J8n#OR>u!LW{Aa%;EFVyx8}%=S*oX*=2vn8pL8MN2158h z;U&6br;!B5Tyvf#EP~C zjYKZ1*g-og*TQKn=j>y^ES9Ga7ssFTo>%-If)_KO5>YOsbcLAAiyWut_deicFuT|3 zK@_gp0j7o_yqIG)_N(#7rjxitSK$GqFcluy)Wc3)-?gS(g5<3;S;gurL?`I&(4(_2-aq3D z3#b60947Ic-6PZeTzGU9Nv-wtnCefyB%>Bk7)VUo5Cp;9vh|FhqQlO#e%)TA0EuEj z@6y4$yB2)HO1&07P&|u8G@(w!e*sWG#Z+ZJdu?FPCJ*;4pW)=>8~4WpJ`euVjm0`y z^kHVnUh(wER{AD7*^yaI7se}ueJ)tRV)66~mKt`hzr=L7S(yFVE+>PLJz@vAJv%*y zmLH`8RXp&yjc5$G3%&;%YgiL$wCblQs|UFQ2m^Njw85_F6#|w^m!oM^#x@?<}Jodl)k@1}HQ@Oa@0q)63F<63?G*b5^pa{aoV| zGVCm(Sn&AglAy>G>sJEa1Zt z95RJrWR9%1E2tl0dVBtwT@pcwDpa?(X+ymjh4h8?O+yk9Zjertg-r!AG#`%2?PST_ z5U49ZTv_`aOw`1S#Af0q%^t6O*Z*vs4qh3t@c_)jyLi{`>GJWX>@=gP6o8oNr>p`= z)~)d-khw}z_wa;zbxfq=)IQ{i(d6ib@h$^#m)3aVfOXLJ(Iw#lf5|GL6PdHe z%;7j9Y}k)jCL-h7gk#SQKAIk22as+< z6q;0q@N0fD_JXBg365tk!DJ#zu^ln!@dRDB9NR(A4Hr*YrGo(lbW1WrYM%ITK4sbz zMP)9Ru&J2Q11m7sUmi_=<#SPjrbZ-Vogg-G-6I#Dr6{tkCFch|T~G>;!lEXtP1|3o zASn}43SUrYWtFr+@D3BulBq?%?#JSIob$^SyDPAM?9JCaL8~Kt^a;JHH-OP+`-Tg5 zZ9U79k`9XDM?yh>2->tw9f#*oZ+5!Q~TOfL->+Xy-bvfM#gibTNyt!p6FFPVj1 z==`ty^1Iy;k~M&(kmZXXER`x;hXGq6sV(tVpat5?7+Q4_VHhfGphONfnrvYBPWK6Y z7wf%crY0tk4MUo2RKmJUw9z;L16e2sB6-;AbeO~<(b<7FqFks_kcpwFtChg}>^o zfz||CvS1W+Ta6-(7=E)TL+0xA@!9Hq9u2Ta$-V;n5DXEVJPLQUwef(bVA~t3+4>G9 zd29n41kx^SvTmuOccX#WcH>*S3I?{I7-?n8wW6x>0TtqzZz#qKK1EE7?WlRu3!p=q z(^AqT;0Ko}lYB<5TdDw*?q#Kv|6UTxZ`lNObqVdqfivYa>*Cqp^qXl*&gSH*ppB5r zuz~YiA1==Ms0X*~qM*1ixPdipHj0uf^2jFv9PM(2S5CSuQ#|05J-?N z={MAaw#7NbOtC=#%1q$Ai3HXQq^q$hDbp|5y`-zv&~;uK!)UZRKR&zr-p;ixy@}d1HpntVk(i3^RF6g1jE8)?IXv3#y;xC3%2q>QCx295JhslH!;b zY`}B~ZJ4{djoMp0g%K&HAR<8uxX=b+y97)t99_S*|Hd15X)Pe=Q0uUPnJ#k4dq7T3 z309NryCH3R`^MoUBkf?6c%(s_(k+I;=SG-7a4GzCOO|5V_w0^ed+oWZ5cu`D^ZlE< z*I&{NXg&5Hu3XS2eWE}wY!zcQ``38nipyb~03*&8i3V-7B^@omKWUX^hps2bA#Vdf z^p{_Kez~A79dv|YXe0=pK{h{o*XbD@?cjr5al0H(Cc6jMfMc?v6*?&7`7*&bS)(hf z&rjV@%57k}Nz-k^MaW+R+B1bnZt$;@DIYMi)v&RWbH-L@qurW~E%ms}g^zCY3Llen zcBx^n_(7gs(Id0yQ<(T9o(goNB2GLY;~odI=s4|CxdmRsgW`$L-%A-p`qXMq6IXG}!C1W>k9o>3?m(O+87DyvTc|?7wn|(ABzQlXj zFxXdeK*M#+WpVtF2dSYNNs0u1-tn23yz<8#4GA8w;IdZv$$cntP~cnNxFdc71OM^P z{wsg`=ilmcrPg9v4wzxd=OG+O1&9(!-;YBE_i8jfTMtT{6iP(3T zG={f#)Si3_>6DOiW)$OTdV+MXOT{*mQGMA(ci<@mO7U@1IrAM^c{JCOIsLc@L8z#DvkT z>`V?{m>jSPl`8;cO*_`(VPa)*kz(;&lyY}T-kEMo;(3qqv3*27t22G|W zFxO-cm#rNOsn(*hv?NP5$PyqB9fmA`Q>qHqjR;81I*$CLNni)?%FHMWmx!YhqTIM? z5Rm|fj6SSIoq(*bwrQX=dT#&;vBo({@C^^#6BQgTi41lL6LIjdNaMByNU&Kaz?_S< zcm3dv-(ow{QAY zYU#l~nq%~N*+45ykX1A=aHuN^X%Z~*}T7mpF zW?AixI{-);VJYB>t{hngy2T!q3>Rqwl>*@qR9yO)n=f5^`|s^O_l9LVU{e9RoX^9{#ZNE5mrYG3~^uwEd(A0bI^tu zWFfhQQrpr9<&~0T6}O=hTDA)29MPTaEXBi&s|Cp&cs3Vi!RJ=4>)-v4b`GBNmU}fw zqw}+b>JZP(EYcf6EGhqWB?|PhJL~C2-ozIS+fBv(s*XQEM@s#)3FQOZ5J0gohs$ z%WH5vy>x_%Rb0$3*niHpUmPB4@QqdaXr>(4C`te!Ylk>&tPu_!jLL)S1~?x3DV<_$ zg$U5-RSiNp1biio&e3Y51(j8#UQl%L+Pq3rEsUB|iTqXzZkZ`k0=8C|u2{44EEs(= zPaSbcP$Mzs1w!PMDPr=4K*@JDtp>G7#8#hx`YXKhO3tBMBBYB1S)f8LV^FR%s$wb% z77;iknXC8!;*J3G4=0#brP z5@fPlY!ZqM#T1pK;guZH6Za>17Oww@i=q&RW&AuH!RPY1p8LENaOz?fH0*Zik=g&k z`=ESMeZgUn_Qe>4_$lSki<;xbLd6N{@FN$d$VJ7aN+}1Lwy2b6fslR{nHYoG38-X< zYC`x(dl)*@rQB`*7x+bqI-bpEJpi%}K z_62%<8g@4C6aaXDV;1eu2#12&wp}M+uI9|ibt3AHH#YKu8@`ad;CC5T3f!tSSml-k zyoc2wpAt07y0aO;`3e^)Fk<IlwSq{8J3)%6JHDw#6D`fCMb+OF9NRaeZONqrls{ zDVSh#5WqYsj-7E!!KNd@V8yqzs}rID+n69A<|(TlcCQalYpy2W{5AViwQvaS*(nUW^dN@Sdku;|u4^MZcOM=5kuqe-m4 z^s2S%cePD=*5UzDF=f8c0xMWVqpEZv#!uG>XkwJ|s79;Z`v zlz;_oAY`BsQ<6&r5<&-UgaL|zRh(qW6{;dmo{mHSYJ|5SaDGfJnGQYtm)KjN-WX=3Lq6FU9ocSy3h8-=>wP_?YK&eLg#0n(-odh-{ra1 z?)4WZ`#1GWz)=Uht%o1TV4QZSxx`|2j$U9ULDt3m9R8bAM+5R89V7j>cuivEAPasM z4Ps`PJF}l1_DHjCs+}$9j;-b_B;hHrbsLXP8V9L8*+E%wQF!ZI#Q3C zWAquzYju)gu9V2aFY+4;rgmTylTpzYhcwG-R4U#W*Mh&y2sH~Y7>CbF*i#(clO{j(x&u+c6JUw9! z4+a*}_!J={hSf)B%-DCXadgw{^mCToX<>9*^t{EqgDsh9{8%eUBRS<0Dj`Ca{Ms8Z z`GG03kpqPOqgXB}T}4Hu;K|EPE|R6KQ-~a?(Y95M_kMHgJ9%zGnn~a;C&ba4OlG6qy`38`j@@EOZP4W*26I?`UF6jAaLFhNq40&+sFe64 zfH=cbqS2R#lPW-30f*iEz2bM#ZVXkId9$B+dq8m9r|Ay;@N}GC%gl3czv>e z=m&;)hB!MI?d&X1d8D&FIXK$A{@nD&^Y}w-5i0}_R20AH2DcPCn*%>43KkPd)GVvo zO0pqEB4hC8U0iZY2VDphl_2+$7Kj@3^+D40BU%X(g0^mHS|E0v0sXkql^w{8D;xpi z?bu*PpU6}H^9LVkj<;Uly>=%<6eeNRUM(VGyPQ7gS!!-c=E*D%{dg=9a(YL48|)?~ ztdhf}rAdh+tf{d|W5eIhlt(+U_>8b?%*PW8`aU)QJ0Mf8R6pKJ|E(YhYpXCz-&-x_vs<@EvweLjn=n8B?DVsb zmdB6xU!rcer<|>hM%5W_LsYeqEKojia_k-Xx#n__i_$U zOH$V+2R`{BM4H1`(=AqMQJK)@nBOMR(NzJP#oPOwAdN$E!GfM>@%S^o%5lPugKM|3 z$en91@Vrq;=|*shGgtd$6CXLA@tPm|0JxJ)DiXB->RuIQS6Pl>EYiOI;((1Ca&d&B z@|mH6xFYBTSr_P30DIFBvND1-5j#yX*B}mjQQE(kZgAp`ktkaF*V$*G2N&OP2 z%9lUv1EaUQ%YPFWv` zVKAn2uNS6-p^Jv`S;?egwNwxm{xI*|U}X^>4Fg7Mhf0ACoLW$InXhI1bcMm#!pDGg`CN0~G;h&mNq;!c$0WvC1CeZ$hu#r&33gU#swB`- zf=+4ZF2v|3_~tsB8B@WUU~`#zbXj#6jjuYHpFf#DzPoe(mrPS8htDxmwVN=g|P+>u_(v!4}L`^u||kG48U09W3a-xUJ&Ppbz%n$kSNZ)#3?X! z&$8gLQIVs`MAQ`F1nBjU|&zq5De9mYC(ctI_liU(=QA!QAySaFxndv_O4A2D~i`5V7;`1*Io(>-46I{xT`ryu>&mZLqPUanvt zS|llu5W6(shix=3e(U5UAUw5U2x99!ta+jUH0}6yT@`CuB#AM&>7rn7`SJC|1K&Ak0UQ8YHR)p-@czxo9OLk>jIum@9#43+JU;4n zu1>P568n(2LhwIjDa4Ihkae%Qm>}aB8w4Un0*OQgW&u@QRxT3Ds>ZU^oT$7%0#=s_YEcM5JeOLK8Up>l2px7(`-}9;?qFkHsJ! z7-E`67(pKK{x!EdGd?MTA^L?t208dwy=I=(g3YzS47NhZ6A;mfy!b>h&k^uFY00qC zhGpSFfh!VdF9uWnVHg0zrsQHILgZSPO2W@|m{wNU24)dM5mFt5)`U#A#DL$(_`mzZ zmu$Qqjlv1-v*{l5!H27pPq*iv@J=A@iN#TJQ;5=5WZV%6-X>noUYeSiW7I*GWlrR3LO#B znAvU0@{CW!?cdKAPrmRo6E=jL`X52qXow+Vd{xcLgP*1*Xcx?!ta}EIj2Z`_lv0*k zlB-KSZo0}3aMCw3NjG0$c|du+PY;G+brPz>?xR3~P?Lp> zL?^j=3;IaawM7j|8;!TaIa{FLPY z0{A;WvcZ2+BB)hGt?K}&5LUUzyHl2F_+l!mx5*Z;IIJ9ztz{-t*UQ1?dp9`c^h^9G z5Oc+Y(d-U8-k<#9|HVl-t|}y@SK_kM1&1jKY+M6v2@;Ebns1cl=!NtFVb3oV$YsM# zE`lI3ezCB)D1%v%!s>mV-8p*B*uyo8Xa~p@rl}IJ4Tq9UWl&XGK93+cg@OS)4+&=n zyNA#Fw#V)sWic#XbmA2g8)Pv}gVxo7kMRkw=eb4$PI|6b1~U~ck3qu96ztym(rzjY zk$&HP^HadsB4tQe*OIwvpyS~e4jhf7+~y@3>IonOvg)IAuvUGCGKc4rdn}74DUyad zx7>sqE0ccX;MQz%vUm1$?*23FFc3mju$}SDQ!&Zr>uMYZ_62+NeJoomz)?y`vdOEn zU$HR%;N|cAefG04rwn3~Z7W43L|11I`C8b?gR>mi;8I*vLq*P*(=ehs`{Dy;16Ggl z5-Jwk)gKc138%(c%nlExRS94UZB<8^b14)MifECF6E`W!j8^HGaaTOIak&L=Eg&Xe&{gnR@6}gy{mYM<-bf=(CrR|-W`JF$d zFBV=Lt`u^`$`c`<&y^nIdU2?Xo_)YW(Av0Wt(an(0c7N(h@?IB8~f|Pn@tcbmByuo z6e|pBp=$s@cq$Vv5)nZpLj>AaLYOFs%BkxW9>)LEfB#>P_BptOm(6IUF~>rZ8rbGg z^J{PK-1`3b+FKk*ymN4qhoroQv#J+@9M8o)G`h_RM+GIBO1Fj+&;`mZksMOg;wpfX z-D}_Y_YYtF9^ZP{o;@!=lOr7fgCVv;T5BthCEx`Jw^e#^rPf!hIGcHLaPwg2;DB$9 z;E7qE+c~^7z4;0spaU)2(COW@3mYEn^-5_Cqw}3yZ{)RWxn=Ux%u)TNnv$dMgSWc+ zD|SH=jOc!|0}M|lrnpH5g_@~*d1#C{dscs( zGV5PscKRNzO2gH?gH+CnFaQzP~0&XB|c}XcM z!BPa)*UcggVEjM)m;ae-2h*FQeKs;Z&%*uc^gb)A!Tevx?P+; zJ^lRslaGH)W5qJo<`vnm$Xh(H`l6ZU#!LQNUydj%8QEn76*BIo)92AGD{7Oy?b$8O zamrI^zF>H7dHRSU0n@)MMDT(vT@tV?t{v>(;7n3BrQf?a<=g_sQk2+XHp7w|4ie8X zr>Bl~qlZw-EesdWc4rl$>x#CtXeq^IRFUPM)|DO@AOX@8%^HLvHGgTPYhgu-EnzoC zgvhH^@pIzEQgM^`gE4E}9Lm9-Wrlirh)=PU_3R40sVrm6PLPRVZ50XgO)x=vFy2IJ zNjd@(n`){a%u`7IE7;eC@L!b}>IJ7jdWVMA#2`s>f&3aC#{caf(2?<$^c3OwSozt* zdyD(;@~O<#<=Ogt8|XC3Bk+bz2fO zxjJ?7J5CF328FhAqXx40161>kM7Ty{meA!n!?1Uvsk_|G@hPUVvtARPbE2z+B9wz` zdk5h`n7`s-XeXc+IjKdo*do5xKqMq3p$)FWFbNdAB1_>3DRZk#(dhVFVn{TIj{Mj2 z@UQ>p-=fNQuiu$I|N7#=ub%$mU$chFg7hhm>;qvTp`nZr+ugDSt zjkkeVF~@DDjSB3-kN2u$BcMK$`74)1P2`?Mquu@K&6nt2&pv>$y~`DpNpSj1=}F5vIII8jP4wv&TFyrnfC{_Nz_cb88;P8M_UKwwpT zqwKZc@WjmpUo&NOx9~&b8J2!W|e(NCvrI8P>H_Nfaq4GY&%w=2{L4M)xI&7pTLJ#c`n5a?oCEaOjhzr zBk6hsPC>3!*7`6OnKi}|i+7``05Zvtxa?d^j@~+W>mP71*Vgik#i7;luP)Bmid)~0 zZN7xXUYiZds4m+cub6V@ebS+L#O9fx00$u7<>GeSI{09@t4`XN2cMZJRdFtF6Qm#Y>w;Ydmo zFe~X|jtdM1lakVM1#5Uh8*X#E#@&&G^CXipWDk*miLFB8AC>8@zQBfnQ)J|^Il~6&N3@(L)Y@;l(k;$x@I0?j- zXUpyV1NILvVdQ9y(ZS23J?0_|3O{08$a8Ve3TG%ur51Jh0EgFZ94a{MlISq10h%bC z@U2hsjxf}y%#-g0p3Q1_l2KR#Q5i(bt$0f=MTE&HzRYV#2UHf7It>7$1U9R!o;t-o zTtQj5wv)9*xO;1cF`S(}{~DX0+1oeT zyEU5KTFzdcfBLVs7LOnVL5o|MsN_w^#p)tOy;WMJ28L)Ag$+^oKXPrP7aBScU1mgU zDKwXj8my93qA(Freu92Y*Jc2cmQbWEs1h0c8ABF&XS{>MV%7*)8jwq_i3pHLSQP_x zDDzSVP++m-8b(boK6>T=P;3sjy_EP1bFdV~L_s@XZj^d@SGLS{iUm?Bf#b4KOjJI3 zv-)HO#c`M^%hjyW(;VwGob|vwkd}u(*%Mju!;MT9-`@DtA=xIYuiPe588)_|xCIIP zlx}+#Fplup%g5nvqFXLlPCq?AIe+jmE1J{iUYp%|g{nIK=*L^<_jhZ{Ghi8Ji48SPE;j;!?aWnIo`LB zK3#I*!nq0=b~~(6`IIjbWds$|Bs0i^!Mg4)ypibE6B-YAmjfC+YVIhD?~6Kdy@S32h>=a286 z-u)>|W1YO7B_raA{VkU4v|6p$cF;Dx%Bt4T>kL@n*MX9fFo0pmi;)GCBrzy~zrE>( zYa}bp2%;D*IEIlUZqr2?8L~UMDq{(dX1qvs@qStz!^%?=8j^Uh0926%DR2umZlVLn zqQ?Nk!oW5ciGHdOkT@PiJZ1~(7^>{%W##5La9 z4APW}T2Qdvgyn}T$}$dWMc~R$V=F0XF$z{yp1O~HXY3Vtbs7t#Y?ey^;#R^tue0 z@+`Z?-{Wh%$^UvD^oUhnd|C`#un;Xu=}D`Y2w-kRZgQ!TTwwzhNYbf*R#s5z!y*-; z=m5v~h*hEFDrBc9Kh%e>qGe&+SNjN-smsV1xZFJJ4)>s!mJ$-!2nUuctiSsIa zATL#ST8*C&m(xfiPzhcd`~2cQ~C!z}u|tSOJRRjZ22ngydIZwSIxtbRA{hfLB40fUqJ^{Pe; zm z)r}c5i|)sqP9rlG6i7`;AynG0!umI1Rw>)!O zOA6Iu75h_Y41#bZ+I<)gKYJo~lWSQWRf$?$u1nQD2zji;OqsHf zNuM&oG$BT(l&!3q2K7XBI$(y7|LvXhUlpQ7Ma2+7;()K!F=;|Yo7!T`QRLw@7g1>I zcus%)2ghh1yH87K{1kdpsuWkECQ53xn$I~Pnt4pv6A26}n(PZ2SJwy%BN{_^0fn_2 z`6Qi0e^V6D25iKCCdXc)s45z4UXnc&NwO>^qwuACGKyluPRXDxSQXuPFJ^VUKoqLx zxf|OJSAHa4O2}=?ibU&#ih-)7cahc*(6TCv^Nr*@QPdWPv@o=Zka%GpK#Ziw25ie* z^H7@w!RxFfs;3CI{Lko^U;oS$o2dAY(G>GZ&P5BPgVaBLbx2lhNBo3#McQaQT(&G- zRQL&_l^CT!BgF*BU#wisXZbDNRT%c*FWsR*Bm@~)H=pE?MP@@)x+=^)w|>1sR?vjv zoCI0#iC2FI@E$>h@^KZ4yDt8aQB=Y$Y2aUTI4}_4t9Ni;!dPhFR;tvQza#MsxOY=V7dnJT^swnjMcz@(D6?hsFg>$1jlCyr4_L8S7Cj`NfUUQrET zYg{d=$K*>xA^}?^QX~qBAvBLjPEjvxsmj2cswHLjaK83+&6*u9V9qyX=B7QXpW zFA&L<)xeG$-VA1F=@HPHqiWq?x>uUqfv^lX8rBmQB_fDq+z>rh)VQ&567U8Sz(AUv z)+(6v17G=t$`tW5(OOm@g{4%=L%%{J7D3P|FtXh<#g&_I9Gg^=u|koptu)9xtSvZM z19o8vOawjr#9HVyOoppsI5*Pd7Ak4rjlOqdlcF>jxPZ zP0#@flOCWwP1HVJ#tbSsH<5XJn^R1-ru)p7-RolzL;i@E(#jd-zs3C*R?HNedBs)L z5Ba2pPSWKqwHr9s_6k)%DvfK0H`<7rC;Jj`(V*y>+KQ^AkcXd?iJnQR42T*J-@V8t z9g7z85|3^L8@Lq@3CR?Iu)(D{L)H+fR1Cmwq>!6buM|QqNmUDl&1)}x<+l|so%WnB z@!*jo4#fz;jV0*MO2ux>Yz~725~c5oh&x`rD;9_ktV*Cn_88sBfG|Q-Mgvv!*4Bzo z5J0!~dp1aM!jw&5tG59=9;;5beniK<55B<`RjOarZP~^k^!zrc`}E^DS|}3G+c6Le~p7K zrYc>C+^5TE+#*M|U=3Ak*$8I@qH8UQB^BTCh(e_~WeYzzI+g}^*+ZYrDd9xh}kw>B&69kmSq!prd*IXGTn`K*YfRbslHcEuR z<7DXvsZWsAegqB?>uxgJJ(%#Zr4wFp$pkEn0XftMXu^p2)`RbVRly ziq8R2M=D4R-TaiGs54rs%S#J7%63^ zE@CJpb>#_auv`_{Os_be#v=Sz3t_ASb#ctMIN9wt@P)TU&FGtzgGVB~JjyO^s+r^Sx3}_=E9t?;CwWG^oO$A-zs`qB6k7#b(Pqkq z6;>HC9j_KkaZknz12QJv1O*8V@JbA32e^Pma+xqY#5>yXrUmp83^5W76^xH}i9D3L zR%9tpHzfl6a_okh0&gBq2d`N@;x9MFuLNTdCKgELU4bC8^>nR9&5d-y6`JH&O`=tr zqzy;{X5Ns(=qwyaH0jok2oic?&@l?+J~;rPMZObB4_Mn|J)5EZtD?EmSw#EXs2XlU z%slX@ok(k9;Vm3wg_dw(3o$G%aLVPUaxJHzafG~1!&4I~#N||mt>fLv!S&hASJ-{C zbHK}oTPL6WeD&Z5TU$>dNbqfUF4Nudx!;9s)S;qyoWfDjz?D3>+Moai^}GWW_oF82B>6pT-s!R>~+<)o`ZpafD7N4__*BA{vhzY?@_ zg6+AcgBwIiFKkspe4$Q>{HCBl&*Duf2K$AYD-MS~nIo-?;Z^Au_)UGMQ)sB=TZZ&! zlnR}-gya=@vtoB>oRs(y*a&#%sVcA|q@W~#;0gnLc$#u}H=Z-^&iHo63vV91{U2bC z2u!V9@D2LoPcBY=#YsJKyR*mhX0~!mO9WsEQRNh+w58r&<_dSR8g}b9v~z)#NFwJJ z1zIF9l~HMulAJ^db-*HnEbEk1_P~W1gu*PVh9au4_&j!+>xDh+QLC3|45kyTwn%r>Mrkepa zmm&kAVkpSS7?KNb@IS+Y>vIxybb?{=ISra7<1ZV3Of<7L=n51bz*4Lg>Mls)0CH~w5K-y_uGF>sxM)3IJ^9($-J5)kk#BwT!piF8TFE6s%L!cLRB5!f&iLl9WK+9U2R%z9VO|aJSLsALVsj<#UobR+ zl`@bbRRBODUW4bZTBdy>pCje1XAfdzt)3VBe1Fa7&la>Xu$TZPtBB8vDr1u_ID;H) z`R(jUFI@RsiG%c&-gAY7n*S0uS_^804T*dSc>zC5vIrHPhE3drt31RIY3H2zwad3v zkNLO~6)J-yPR6@O&+}mrPk*bD+MQqFfe& zV6q&dabD4A@9=WPw=Ma;ydxF3;=}95KmXt7U;KD_Fk>ny6){i;B9JUM_z#X(U!G+x z1D(Z}o43|TF~1hUvH=K)9JUnGdu;$N{-P3mp@(#WXh5P6PX$vNa4V$@r6tiuC`v>j z{?OF4C+9l4J?1F9G{lxl;+$&C*O2rLnLGb~g`N3sBu8?GN%p~RUPI1(Nuy&SX#v{{ zuMHSB{D+^$ui+=~GZ_o70Rvtb5U>Vht)$U0ntO&v^Jq4kq`&VMSv{2HUDt4`voa&& z%E-v0vND|&7pLu4q#%35EDpg1BcMT@hCyI4bZq5UUI|om_!l9uauyj%?nqHN0d73U zZz^oEM|MFZl`Hg&2#qUoe~Jg}ZG|+-{9-|WX-Ly0RXLAzdyct*&oeLU$IL`xeYN$ zR5*291w>S<7&50y<0Fma84Ir*(P$vdDH75t2Csz(ivSsgN|K~Hcw>CVo(PnYz^yC= zOeZG;Nws!id5A)5%)AwDkock@7EFp zrn$qx^vSv7J9+h(t;x@i57G})e5K7LfB6>yx+vRO zRqiv8?biq&-sLSQzUp6R! zOTX53w)plw^q>#c-tur~WqhA+{Dy>71y za#^G-vDpeX$r~m=bEf;wN2YK9;@mXPsvu3tVInruK{XD6>x!>q8iYi0fvfIfU0kI< zlS%<(C(RLMGL&dRiy)?jsg}Jm|0y2&wxA_DeQJbnp3ODE5VYn$_x5RvQ5)|G>29%KK{Hf=Xk8o2%rI}t=w9q|7X(}=l z6cPsWQ@6SYWh0WDe~bqZ(F^ut^^3zFE{>mGUYy7oieRwD&R~Ze&G6&O>h<2n`)-Le z+V*@e989@cDO0*N>A=Ppovw;0gDmem;>kNi`+j$G5K3#mN?50MH^jN()|a;NKHIg%FN0GJT*SSCGn!hEgAHu3R-=VxTIqimNKOXa)fOy8`qhvd47ogPAiZj5`d zu!%hRy{K+P+ojGUHv$#rt}Nb7mATSGfQ(V1Wc1dh1sb{WGAbUhU?~iMl}@fA1ds6{ zVJQE#Swmh)`Y8`Y^=Hrj?Q-_kEeXR)F$*{WB(*4}ujaG2%agB{Mh{L8PL7^_!N=~f z1xJ=vDj%ReMhs~cvDABxz0D(tg^gIE*oj(JTzy zfzFSlJaYST#?hxEEz%vBSjWNM&pqC)&}+Hmd@GF^Jk%VmcA2`*=My;xROy@pW~L)- zgucM$(B~(dN4q%6UUCJn;v-RKoE_VRLaI%Q5)TG|6bG8DtB?>jK=zO@DS7KoQo;Wp zJS_j$KmW~WV}z#{Z)NO4L7G^OQaYKP?6V#}@7z)vCba)P`wm0vyy(%skV0Kwu~Bg5 zi_PZfSzqxAa$6-0jWkOo6=V0+%^~Lpgu>7HA(N}IkKPx9g zL=RRe+B5@@w)j)S@uw=h*f4Knu>{Fu7*3t+BKcKscC}@`@+cTls3UR~EYiRXlFC&@ zb$6i3mafJZ^`G-lFlwxSvVVp*9AD!wt+XsZ!7CgTPY-8<jHsTlFF+;Gj!H$(H+M;`w(#;{$ELrG2_C4+5k}c6#MauC7utXn#Wpml;90sc0)Ueb- zimdoW^61m2NM6w?K7ki;qEH?D>4&4uJQB97qtcc+Kgn6hg(rhB>D>H3Jm9@}H+IIh z9;40SaGkz$A;h2C{PA7p$>el)>=;cJgF6ru>QANck?QEXfzhp7<6HOGQ5b;-N^q<19-Y(;4q3TLBi*O_G`_a? z#c$Z?+#Z-H;EqBLB5!GD9cWh#DfIFOq_ApO*U@P`<)nOxD!(z`?dD{8#_}gon0@k$7A7;=uxt2Wu_aDEQ;O zDQ9brcXkFlA2aYgKcut&14nH#K81*2Rf-jqUb|!{qEfQ`@#?h?K3UzlHJ=`37wdC6 z)Ef^zTD$$;{c%`OqDK0D^b zZswBsdX$XCNTdMEqHBVrSwkm4$Ez}jB1Bb?%EHtnu3i-4%EbdIEh_b}fHY-08*VC` zO*P3%9dK?r0Wx88;LeVQc{}2Ibq+|=B5_=fJhH%VFl1@Q;MzUba11wZ<0CO#o9ytx z_(plNV=;#_tn_YjC=0f$>0omlmd&mY%v}xbQ*sCmmPbdQ7t^R!v7o5;Wuz#iN>Cbj zq+^Hzh#HP{K-%i)r@t_x_xcIeGa_ z_S(`Q(+@sY-w z^3Lgy7NN60Om4f65B`r6)Z{J$?Xqn0GH`(hFqn(4D|DQE$GCFNiX`?RfI9p;nMTg& zR&k1F>&)UJ4Hi?qja$GIDD6h$O~cAAp1Kw~tw%Iq?Ex{rhtd*R@5{7VY@M0M~*_#}5-UM{f_l{H+4; zXWg><d=t{Uu9d1{>Ss+xI5leQm$riqe=1of5Xlhc=Asdr}hWA(3(vKdvt;Lu=0f zM16@bPnTfWFC~=y7MK{xQbnYt7J9{nt_EsGt%+df(tY)dYs5J4VHC41Kq_-)tU#tX zE@Uz{o~%FMKoEw*945ulmMDZ3Bp!!5JH_(Y(~NTuSfVlKH38j0+9O6AX^3>L)o9>I zD99Wf9#L!j>Xew%kiLnrq~jg4Zmta1Ucd};aHQ?x4qeKm*HjB#Yo;1ifI`$2WYAee ztHh?Dw0@4}YrDbWVCvYk(krm)hMBNkQ)|3;yRxVTOvU;{)!yr;oOI9Ud zf1wmpLcGR9UZ96|vQy9(ZF)~S;IcMXp=yaS3*w7hIFfZP{Xg*(HZW%gyVKnl1g0;4 zWVOiZ&P{wCa}DPwZtBm`ko3nnDfjfvv)SG&2%(6R*U#1~F)VJjs%t#{scl_uT zxQwqf@7il*E8CLCFhg3B)sM`{Y5g}lS&TL6$`$cU61Ld50wUrh3E+BR4sqo@M>FLS z^g<#eLq047cwMuRj`$x0oHoJ~S__A+{1P$cZ5yZ-xn;OgKr!a~}1} zj)v&2fR$6-&{Q;RX0B9G%gE-K?pgnA*+cJVeQeR!PWD*jl8#PqKwmS19>*xYQ~(<} zSx7;0##P#j(e#!O)~j~Pvl`DUfYDs{oMIv;{T4COXkc3Qs_jlRqbeTQ zkC}gQ;b_-_lLw*Y9o>VMY^_Gbg_d!cB$P=h6&;LBFkj$F>GQMs;dfsR*4EBWj)r+n*9&Esg%1I|RTKST!z*Lm=EcCa_wXXXW3V1-aN--+zGgT%>odfmUWmyspAa5s)i-iP^9qPw z4>KcC2BtFO&<&Og4L9!$bEq_JtA<6&DgCqHVz!x6ZO=Gk^^#LIS&czw#_lEvbAS?p zNU&W5iVXsVz(=++rtX)_OXn>9;IO{^!Op$@<%UHr_qY-YcBtawfR!I!c~!^}lukK$ zDy1ro#dpZ1!9~Hg%9ls+RSBeQOfFdhiN*PEaS=r3n4k&E96#+C%Weig#RFS9I5NXq z|AYq~0ExmaGiiEJux(6$6!tBEA#>EE0A>JPjhhhe@L+yCk;&zy+1~5`V^a%SAL(IUI6K4ZavUMcmssB6j0Oxl5d+)Sy2b#Z;y>(|y&jcB zu~iUd$>dgyc+N|v^|L;&VW95f2@pmNG==&kz&u%(p5|rW<0G9}R5TQhABD6it+?ty z+M&kH81+`X5pUT9tRRc625zc2(o1#%H60g<#tBq-$ZBQGlxo3wZ!p>MU?W^xeii8= zoI5w^QSMWqWS_bpTJ{K`7- zzG#<&md3&^1w!K3!QX&!bjOSx!`_UPQIRH# z9_$UH1utTUuCVDHJt}aPjME$j(yGDpCG;O27w7yfGsijgwLinv4jk z-r6XEs0Fa}5t;TMP!CW`{2x^HhrE-p0Z;pibn?L2C31zBJW_tCBs@Smb+p9i2}To* zchvH*LKg6 z8CUW!wZu0dOzF}hG4{tz`g$4_G1N`#2j7-Q6)WVK9a1hd8*1<;pNKV&+Tb3a$K0o? zr_pyr0&ke&fLhVU`_!4`;o3@Orr-=+(s^)FDZY#eCK^zLiTqk**OY*<_$U>qC7Je( zzKsGEk!GHIr;E(D*4F9hT?_ zV0cpuS4YL>sy7{S##kw3UuP6P)3@_OB$BFXnGL|sWRfUM2Jrw=JPl%g)uCi!l@OeshPM*`_O>lPmeCsjq&_Ij^~to zrGO03gd=`%6Y@$6LG)tmDRE!$12&ls>?G?nt#cU*hZnEXNmBCJ>DH^g=LR4expJmPj>Yn^?9^Z1t3TE$I9~N=HDw^#glL+RDbiVvYJ^ zjklKTFT*82F}rXiIsfF|OhEFVKySRW_VBNJqix$7XOUUHI$&nh`9gTMZBStURE{&a zB9v?9)p!xd^!3EK8~Ha$GD=!7!UdQ}Srtt^i!P{yNE~V5iWEP#ij1J82j*!=!x63# zf9wnUSza(MbK+^=$#d(WM#IpaPJ(q`tAf8wo7+K>xIBd&6vcBVS5c1@66;FS4Aon= z%2a2a7!%RL4sg129VFuwfqA!die0*&YGyYZ4>LZFg9Tq7)jLz12UdU^#vEx2K*fcL z;7|C)3d>v)hW+RC zt|+OVrDMFe1cXvjuCNWJ!a^z>vPjWwA;brzod@URt=Znwi`fyPTH1qE`nWjh9!@cp zBl4m>Dj68dxrj=Z#7v{4Z&{xQO(N-3X2&1vY)_0Mqcwqhc-l1L8o+D9IvCC zw=W(TU7;2z_WtqjoO$Mwdl4iTpd?#Pwm4{9-=llcyL8Z9hCF-os`3t^ije8V6EIkh zbq`eudsQ9<_k#D7mjNxq$96#?KEUx&=?+cd)EOZb2{7PZS=+)pH(tLwS_gxa3nx8x zgmTCa2ExFT+ToAwikW!vb14*RzBrwnzIn``!_$M!L6byBLe_vtQOL8ZBclkZ+I)Oy zpNS$M;_Tqr*@1JeR%|i>9wj#h!iWSd)4ZxK2IMf8m@`Zaj8RE}N)cl4#8Lset>Lc6 zl8C$sWc=jZnGE~Rnp|>dF2bSZ6e*taoL)rrt%*;CG~FEV&y9b%_T2hb|57-3OCICqHy5q-Qng>}>0Puh2AeT41Vl6J((dwWSE<^ktSO zJI$hw)l@n_6iQJHtJ;S;lyfBUlNVi1NviKy;dTGY;1Qh6kKOPI?vSb|7r4kF28|W6x`lQAE_DMG9{7`auKw!q+I@88_Z!E% lR=kY*T;Qv5+b2M%{{ecOSjX<8+q?h(002ovPDHLkV1icwZ*Twr literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..f768b702d5e51cd629b4cf45625cf8bd2c2727d2 GIT binary patch literal 12676 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�r8rvIR8kL}Dw0U!=Ps9JuSA_8YuO=5E{$Kll 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�jkM*}+-4p~W^TJihJ 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π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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..1d2c8d56183d94916c496d8471dcdf3a6e59aa91 GIT binary patch literal 43693 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

jEgva{AeRahZ*5SxI8sJUir8XQ13 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

qLL)988%DC!-M-17-2; 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 JPDHLkV1i%;E_wg} literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..eea77a536772eff4f6d5ccf4020a9b8b17234fbd GIT binary patch literal 39183 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 Au^A_(pH? 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..93209a8 --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,5 @@ + + + + #FE9500 + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d62b650..7ca45dd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -60,6 +60,9 @@ androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = androidx-compose-ui = { module = "androidx.compose.ui:ui" } androidx-compose-foundation = { module = "androidx.compose.foundation:foundation" } androidx-compose-material3 = { module = "androidx.compose.material3:material3" } +# Full Material Symbols set — the app's screen iconography (sensor, logging, +# maintenance glyphs) goes well beyond the core icon subset material3 bundles. +androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" } androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" } From 5ec4fecee95b069b7c1e040c33f312411fa621d7 Mon Sep 17 00:00:00 2001 From: lkasso Date: Mon, 17 Aug 2026 08:19:45 -0700 Subject: [PATCH 3/9] App: remove demo mode from the UI; declare INTERNET permission Demo mode is gone from every user-facing surface: no toggle, no simulated boards on the scan screen, no demo candidates in group logging, no settings hints; Bluetooth-off shows a plain empty state. DemoBleTransport moves to the app's test source set, where it remains the fixture that drives the real device stack for 23 JVM end-to-end tests (logging round-trips, group capture, process-restart recovery). README and HARDWARE.md no longer advertise an app demo mode. The app manifest never declared android.permission.INTERNET, so the firmware server's catalog fetch was refused on-device. Declared, with the reason. Co-Authored-By: Claude Fable 5 --- HARDWARE.md | 5 +- README.md | 38 +++++++------- app/build.gradle.kts | 6 +-- app/src/main/AndroidManifest.xml | 6 ++- .../mbientlab/metawear/app/AppContainer.kt | 39 ++------------ .../app/ui/logging/GroupLoggingScreen.kt | 17 ++---- .../metawear/app/ui/scan/ScanScreen.kt | 52 ++----------------- .../app/ui/settings/SettingsScreen.kt | 14 ++--- .../metawear/app/vm/DeviceViewModel.kt | 4 -- .../metawear/app/vm/ScannerViewModel.kt | 26 +++------- .../metawear/app/DemoBleTransportTest.kt | 4 +- .../metawear/app/demo/DemoBleTransport.kt | 19 +++---- settings.gradle.kts | 4 +- 13 files changed, 65 insertions(+), 169 deletions(-) rename app/src/{main => test}/kotlin/com/mbientlab/metawear/app/demo/DemoBleTransport.kt (97%) diff --git a/HARDWARE.md b/HARDWARE.md index 7369542..9fe832d 100644 --- a/HARDWARE.md +++ b/HARDWARE.md @@ -80,8 +80,9 @@ Then on the phone: - **Logging**: start a log, wait ~30 s, stop, download — progress bar completes, session appears in history, CSV export shares a file. - **Settings**: rename the device; rescan to see the new name. -4. No board handy? Toggle **demo mode** on the scan screen — the entire app - runs against a synthesized board. +4. No board handy? The app itself needs hardware, but the JVM suite drives + the same device stack against `DemoBleTransport` (a test-only emulator): + `./gradlew :app:testDebugUnitTest`. ## Troubleshooting diff --git a/README.md b/README.md index 9dfa0a2..7a36ae0 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The SDK is structured **KMP-ready, Android-only**: the pure protocol/parsing lay | [Requirements](#requirements) | Toolchain and Android versions | | [Supported boards](#supported-boards) | Which MetaMotion boards the SDK targets and how the model is detected | | [What ships in this repository](#what-ships-in-this-repository) | The five Gradle modules and what each one is for | -| [The MetaWear App](#the-metawear-app) | The full Jetpack Compose app — what it does, running it (incl. demo mode), and how it's built | +| [The MetaWear App](#the-metawear-app) | The full Jetpack Compose app — what it does, running it, and how it's built | | [Development workflow](#development-workflow) | Repository layout, common Gradle commands, toolchain notes | | [Quick Start](#quick-start) | Adding the SDK, permissions, scanning, connecting, streaming, and sending simple commands | | [Architecture](#architecture) | Understanding the scanner/device/protocol/transport layering | @@ -31,7 +31,7 @@ The SDK is structured **KMP-ready, Android-only**: the pure protocol/parsing lay | [Data modes](#data-modes) | Streaming vs. logging, and the flash entry layout | | [BLE packet format](#ble-packet-format) | Wire format, characteristics, module IDs | | [Testing](#testing) | Running JVM unit tests, the mock transport, and the instrumented hardware suites | -| [Demo mode](#demo-mode) | The hardware-free MetaMotion S emulator | +| [Demo transport (test fixture)](#demo-transport-test-fixture) | The protocol-level MetaMotion S emulator that drives the app's JVM tests | | [Design notes](#design-notes) | Kotlin/coroutines gotchas the codebase relies on | | [What's not yet implemented](#whats-not-yet-implemented) | Known gaps | @@ -98,9 +98,10 @@ metawear-android/ └── app/ ← Jetpack Compose MetaWear app: scan, live streaming with ring-buffer decimation, on-device logging + download, group (fleet) logging, session history with CSV export, - LED/haptic controls, device settings, firmware updates, - and a hardware-free demo mode (protocol-level - MetaMotion S emulator). + LED/haptic controls, device settings, and firmware + updates. Its JVM tests drive the real device stack + against DemoBleTransport, a protocol-level + MetaMotion S emulator (test source set only). ``` The modules are intentionally split so an app can take just protocol + core without pulling in Room or the DFU library. `:metawear-protocol` is the foundation everything else builds on, written test-first to lock wire-format correctness before any BLE code. The full vertical slice — scan → connect → `startStream(accelerometer)` → `Flow>` — runs end-to-end against `MockBleTransport` on the JVM, and against real hardware via `:metawear-core`'s instrumented suites. @@ -126,13 +127,11 @@ The repo carries **1274 JVM tests** across the four testable modules (1042 proto | **Controls** | LED color / pattern presets with play / stop, haptic motor strength and pulse-width sliders, buzzer pulse | | **Settings** | Validated advertising rename, advertising interval / timeout, TX power, a Maintenance section (reset LED, clear macros, clear events + timers, restart-without-erase), and a confirm-dialog factory reset | | **Firmware** | Catalog update check plus a Nordic-DFU update flow with state / progress UI | -| **Demo mode** | A toggle on the scan screen runs the entire app against a protocol-level MetaMotion S emulator — no hardware needed | ### Running it 1. Open the repo in Android Studio (JDK 21). The launch configuration is the **app** module. -2. **With hardware** — run on a physical Android phone (Android 8.0+, Android 12+ recommended) and connect a MetaMotion board over Bluetooth. Grant the Bluetooth permission prompts on first scan. -3. **Without hardware** — toggle **Demo mode** on the scan screen (the app also suggests it when Bluetooth is off). A "Simulated MetaWear" board appears and every screen works: synthetic live streams, a recordable / downloadable log session, battery / RSSI, and a three-board simulated fleet for group logging. See [Demo mode](#demo-mode). +2. Run on a physical Android phone (Android 8.0+, Android 12+ recommended) and connect a MetaMotion board over Bluetooth. Grant the Bluetooth permission prompts on first scan. The app needs real hardware — with Bluetooth off, the scan screen shows a plain "turn it on to scan" note; the hardware-free path is the JVM test suite (see [Demo transport (test fixture)](#demo-transport-test-fixture)). Or install from the command line: @@ -143,7 +142,7 @@ JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home" \ ### How it's built -- **Jetpack Compose + Material 3**, single-activity (`MainActivity`) with Navigation Compose string routes (`"scan"`, `"group"`, `"device"`, `"info"`, `"stream"`, `"logging"`, `"sessions"`, `"controls"`, `"settings"`, `"firmware"`). No DI framework — `AppContainer` (owned by `MetaWearApplication`) is the process-wide root state: the shared `MetaWearScanner`, `PersistenceStore`, remembered-device store, log-session registry, demo-mode flag, and the `GroupCaptureCoordinator`. Each screen is driven by a focused ViewModel in `vm/`. +- **Jetpack Compose + Material 3**, single-activity (`MainActivity`) with Navigation Compose string routes (`"scan"`, `"group"`, `"device"`, `"info"`, `"stream"`, `"logging"`, `"sessions"`, `"controls"`, `"settings"`, `"firmware"`). No DI framework — `AppContainer` (owned by `MetaWearApplication`) is the process-wide root state: the shared `MetaWearScanner`, `PersistenceStore`, remembered-device store, log-session registry, and the `GroupCaptureCoordinator`. Each screen is driven by a focused ViewModel in `vm/`. - **High-rate streaming pipeline** — samples ingest into plain ring buffers on a background coroutine (a 600-sample full-resolution capture ring plus a 180-sample 1-in-N decimated display ring, `Channel.kt` / `RingBuffer.kt`), and a ~33 ms ticker snapshots into Compose state (`StreamSessionViewModel`), so nothing touches UI state at sensor rate and charts stay smooth at 200 Hz. - **3D orientation** — the quaternion output renders a dependency-free Canvas wireframe cube (`QuaternionCube`, `TaredQuaternionCube`) that is **tared**: it shows rotation since a reference pose (auto-set from the first valid sample, re-zeroed by the Zero button) through the IMU's 90° mounting correction, because the raw quaternion's absolute frame isn't stable session-to-session. While a fusion output streams, a calibration badge polls `SensorFusionCalibrationState` every 2 s; the bar is MEDIUM, since HIGH is a live score the magnetometer legitimately loses indoors. - **Logging** — polled environmental sensors log through the SDK's timer → event → logger chain (`PolledLogger`); pending session records including the board-allocated polled-logger handles are persisted, so a fresh process can `recoverLoggers` and finish the download. Group capture arms boards sequentially (connect → clear → start → verify entries land → disconnect) and detects foreign logs with a pure decision table (`ForeignLog.kt`). @@ -156,12 +155,11 @@ JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home" \ | `MetaWearApplication.kt`, `MainActivity.kt`, `AppContainer.kt` | Entry point, single activity, root app state | | `core/` | Pure-Kotlin logic: `RingBuffer`, `EffectiveHz`, `BandwidthAdvisor`, `CalibrationReadiness`, `QuaternionCube`, `QuaternionFrame`, `ReplayTimeline`, `SensorSelection`, `SessionHistoryGrouping` | | `data/` | Device-facing adapters: `ConfiguredSensor` (+ `openStream` / `startLoggingOn` / `decodeAndSave`), `LogDownloader`, `LogSessionRegistry`, `ForeignLog.kt`, `PolledPressure`, `RecordingHeartbeat`, `RememberedDeviceStore` | -| `demo/` | `DemoBleTransport` — the hardware-free MetaMotion S emulator | | `export/` | `LiveBufferCsvExporter`, `ExportFilename`, `CsvShare` | | `vm/` | ViewModels (`ScannerViewModel`, `DeviceViewModel`, `StreamSessionViewModel`, `LogSessionViewModel`, `DownloadViewModel`, `SessionHistoryViewModel`, `ControlsViewModel`, `SettingsViewModel`, `FirmwareUpdateViewModel`), `Channel`, `GroupCaptureCoordinator` | | `ui/` | `AppNavHost`, one folder per screen (`scan`, `device`, `stream`, `logging`, `sessions`, `controls`, `settings`, `firmware`), shared `components/` (`LineChart`, `TaredQuaternionCube`, `FusionCalibrationBadge`, glass cards / badges), `theme/` | -`app/src/test` holds 120 JVM tests, including end-to-end demo-pipeline walks through the real `MetaWearDevice` against `DemoBleTransport` and full group-capture runs against the real persistence store. +`app/src/test` holds 120 JVM tests, including end-to-end walks through the real `MetaWearDevice` against `DemoBleTransport` (a test-only MetaMotion S emulator under `app/src/test/kotlin/com/mbientlab/metawear/app/demo/`) and full group-capture runs against the real persistence store. --- @@ -191,7 +189,7 @@ JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home" \ ./gradlew :metawear-persistence:testDebugUnitTest # persistence tests (JVM) ./gradlew :metawear-persistence:connectedAndroidTest # Room round-trips (needs a phone) ./gradlew :metawear-firmware:testDebugUnitTest # firmware tests (JVM) -./gradlew :app:testDebugUnitTest # app logic + demo-emulator tests (JVM) +./gradlew :app:testDebugUnitTest # app logic + demo-transport tests (JVM) ./gradlew :app:assembleDebug # Compose app APK ./gradlew :metawear-core:connectedAndroidTest # hardware suites (needs a phone + board) @@ -371,7 +369,7 @@ device.send(Haptic.motor(dutyCycle = 80, pulseWidth = 500)) │ (shared scan) │ (per device, Nordic BLE) │ ├────────────────────────┴─────────────────────────────┤ │ MockBleTransport (JVM unit tests) │ -│ DemoBleTransport (app demo mode, JVM tests) │ +│ DemoBleTransport (app JVM tests, test-only) │ │ connectedAndroidTest (hardware suites) │ └──────────────────────────────────────────────────────┘ ``` @@ -525,7 +523,7 @@ The shared scan source is separate from the per-peripheral connection transport: | `AndroidBleScanSource` | `:metawear-core` | Unfiltered BLE scan → `Flow` (`identifier` = MAC, `name`, `rssi`, `manufacturerData`, `serviceUUIDs`). Scan-only; connection calls throw. | | `NordicBleTransport` | `:metawear-core` | One per device. GATT connect / write / read / notify / RSSI on the Nordic Kotlin BLE Library. | | `MockBleTransport` | `:metawear-protocol` | Public in-memory transport for unit tests. Inject notifications with `inject(notification, characteristic)`; inspect `writtenCommands`. No hardware required. | -| `DemoBleTransport` | `:app` | Protocol-level MetaMotion S emulator behind the same seam (see [Demo mode](#demo-mode)). | +| `DemoBleTransport` | `:app` (test only) | Protocol-level MetaMotion S emulator behind the same seam (see [Demo transport (test fixture)](#demo-transport-test-fixture)). | `NordicBleTransport` connect flow and transport notes: @@ -1686,7 +1684,7 @@ Four JVM test source sets ship with the repo — **1274 tests** in total: - **`:metawear-protocol`** — 1042 tests across 43 files. The full SDK surface, run against `MockBleTransport`, including reference byte vectors from the MetaWear C++ SDK's Python test suite. No hardware required. - **`:metawear-persistence`** — 46 tests (`PersistableConformanceTest`, `PersistenceStoreTest`, `SessionExportTest`, `AttributionStampTest`) against an in-memory fake DAO — no hardware, no on-disk side effects. - **`:metawear-firmware`** — 66 tests (`BootloaderInterlockTest`, `DFUProgressTest`, `FirmwareBuildTest`, `FirmwareCatalogTest`, `FirmwareExceptionTest`, `FirmwareServerTest`, `MetaWearVersionTest`). -- **`:app`** — 120 tests: ring buffer / decimation / effective-Hz math, quaternion cube and tare frame, CSV exporters, session grouping, foreign-log decisions, group-capture coordination, and end-to-end demo-emulator walks through the real `MetaWearDevice`. +- **`:app`** — 120 tests: ring buffer / decimation / effective-Hz math, quaternion cube and tare frame, CSV exporters, session grouping, foreign-log decisions, group-capture coordination, and end-to-end walks through the real `MetaWearDevice` against the test-only `DemoBleTransport`. `:metawear-protocol` coverage by file (ordered roughly by dependency): @@ -1782,13 +1780,13 @@ See [`HARDWARE.md`](HARDWARE.md) for the full step-by-step procedure (phone setu --- -## Demo mode +## Demo transport (test fixture) -`DemoBleTransport` (`app/src/main/kotlin/com/mbientlab/metawear/app/demo/`) is a protocol-level MetaMotion S emulator behind the same `BleTransport` seam the real transport implements. It emulates a connected MetaMotion S on firmware 1.7.3: module discovery, Device Information / battery / MAC / temperature / humidity / pressure / illuminance / log reads, synthetic waveforms on every sensor including the packed registers, all seven fusion outputs, altitude and ambient light, and full logging round trips — streamed sensors, the polled timer / event / logger chain, and logger recovery across a simulated process restart. +`DemoBleTransport` (`app/src/test/kotlin/com/mbientlab/metawear/app/demo/`) is a protocol-level MetaMotion S emulator behind the same `BleTransport` seam the real transport implements. It lives in the app's **test source set only** — the shipping app has no demo mode and never constructs it. It emulates a connected MetaMotion S on firmware 1.7.3: module discovery, Device Information / battery / MAC / temperature / humidity / pressure / illuminance / log reads, synthetic waveforms on every sensor including the packed registers, all seven fusion outputs, altitude and ambient light, and full logging round trips — streamed sensors, the polled timer / event / logger chain, and logger recovery across a simulated process restart. -- Turn it on with the **Demo mode** switch on the scan screen (no build flag or launch argument). The scan list shows a "Simulated MetaWear" board (`DE:30:DE:30:DE:30`), and the group-logging screen exposes a three-board simulated fleet. -- `DemoBleTransport.Identity.board(index)` mints up to 16 distinguishable identities (MAC, serial, waveform phase offset), so fleet flows work end-to-end with no hardware. -- The class is pure Kotlin (no Android imports), so the JVM unit tests drive it through the real `MetaWearDevice` — including full group-capture walks against the real persistence store. +- The JVM tests build a `MetaWearDevice` directly over it (`MetaWearDevice(identity.identifier, DemoBleTransport(scope, identity), scope)`) and drive the real device stack — connect, discovery, streaming, logging, download — with no hardware and no Android runtime. +- `DemoBleTransport.Identity.board(index)` mints up to 16 distinguishable identities (MAC, serial, waveform phase offset), so `GroupCaptureCoordinatorTest` walks a simulated three-board fleet end-to-end against the real persistence store. +- The class is pure Kotlin (no Android imports); `DemoBleTransportTest` and `DemoIdentityTest` cover its protocol fidelity and identity minting. The 3D orientation view is deliberately a Canvas wireframe cube: only the quaternion frame math is load-bearing, and it stays dependency-free. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a5f2d73..504b655 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -8,8 +8,7 @@ plugins { // Jetpack Compose MetaWear app: scanning, live streaming with ring-buffer // decimation, on-device logging + download, session history with CSV export, -// LED/haptic controls, device settings, firmware updates, and a hardware-free -// demo mode. +// LED/haptic controls, device settings, and firmware updates. android { namespace = "com.mbientlab.metawear.app" compileSdk = 36 @@ -59,7 +58,8 @@ dependencies { implementation(libs.androidx.core.ktx) // JVM unit tests: RingBuffer, Channel decimation, CSV export, filenames, - // bandwidth advisor, demo-transport round trips, group-capture walks. + // bandwidth advisor, group-capture walks, and end-to-end walks through the + // real MetaWearDevice against DemoBleTransport (a test-only emulator). testImplementation(libs.junit.jupiter) testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.turbine) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 305e15b..f83886b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,10 +19,14 @@ android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" /> + + - + diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/AppContainer.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/AppContainer.kt index a3a978d..8f6f0a9 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/AppContainer.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/AppContainer.kt @@ -6,7 +6,6 @@ import com.mbientlab.metawear.MetaWearDevice import com.mbientlab.metawear.MetaWearScanner import com.mbientlab.metawear.app.data.LogSessionRegistry import com.mbientlab.metawear.app.data.RememberedDeviceStore -import com.mbientlab.metawear.app.demo.DemoBleTransport import com.mbientlab.metawear.app.vm.GroupCaptureCoordinator import com.mbientlab.metawear.core.AndroidMetaWear import com.mbientlab.metawear.persistence.PersistenceDatabase @@ -25,7 +24,7 @@ class AppContainer(context: Context) { private val appContext = context.applicationContext - /** Process-lifetime host for SDK routers, scan collection, demo emitters. */ + /** Process-lifetime host for SDK routers and scan collection. */ val appScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) /** The app-wide scanner (one per process, per SDK guidance). */ @@ -52,38 +51,13 @@ class AppContainer(context: Context) { /** Identifier of the device the detail screens operate on. */ val activeDeviceId = MutableStateFlow(null) - /** Demo-mode toggle: surfaces the simulated device in the scan list. */ - val demoModeEnabled = MutableStateFlow(false) - - /** - * Simulated MetaMotion S fleet (see [DemoBleTransport]) — three boards - * with distinct identities so multi-board flows (group logging, per-board - * attribution) are exercisable without hardware. Lazily created, reused - * across connect/disconnect cycles, never persisted as remembered. - */ - val demoFleet: List by lazy { - (0 until 3).map { index -> - val identity = DemoBleTransport.Identity.board(index) - MetaWearDevice(identity.identifier, DemoBleTransport(appScope, identity), appScope) - } - } - - /** The legacy single demo board (fleet index 0). */ - val demoDevice: MetaWearDevice get() = demoFleet[0] - - /** Display name for demo fleet board [index] ("Simulated MetaWear", "… 2", …). */ - fun demoName(index: Int): String = - if (index == 0) DemoBleTransport.DEVICE_NAME else "${DemoBleTransport.DEVICE_NAME} ${index + 1}" - /** Fleet-wide group logging coordinator (survives navigation). */ val groupCapture: GroupCaptureCoordinator by lazy { GroupCaptureCoordinator(persistence, logSessions) } - /** Resolve an identifier to a device (demo or scanner-vended). */ - fun device(identifier: String): MetaWearDevice = - demoFleet.firstOrNull { it.identifier == identifier } - ?: scanner.deviceForKnownIdentifier(identifier) + /** Resolve an identifier to its scanner-vended device. */ + fun device(identifier: String): MetaWearDevice = scanner.deviceForKnownIdentifier(identifier) /** * Best display name for a board right now: live advertised name, then the @@ -91,12 +65,9 @@ class AppContainer(context: Context) { * time — names are unrecoverable later (boards go off air; the advertised * cache is per-host). */ - fun displayNameFor(identifier: String): String? { - val demoIndex = demoFleet.indexOfFirst { it.identifier == identifier } - if (demoIndex >= 0) return demoName(demoIndex) - return scanner.advertisedNames.value[identifier]?.ifEmpty { null } + fun displayNameFor(identifier: String): String? = + scanner.advertisedNames.value[identifier]?.ifEmpty { null } ?: remembered.devices.value.firstOrNull { it.mac == identifier }?.name - } /** The device the detail screens operate on. Null before any selection. */ fun activeDevice(): MetaWearDevice? = activeDeviceId.value?.let { device(it) } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/GroupLoggingScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/GroupLoggingScreen.kt index ea9c4e8..a239441 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/GroupLoggingScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/GroupLoggingScreen.kt @@ -75,7 +75,6 @@ fun GroupLoggingScreen(onBack: () -> Unit, onSessionHistory: () -> Unit) { val boards by coordinator.boards.collectAsState() val isBusy by coordinator.isBusy.collectAsState() val lastPass by coordinator.lastPass.collectAsState() - val demoMode by container.demoModeEnabled.collectAsState() val remembered by container.remembered.devices.collectAsState() val records by container.logSessions.records.collectAsState() val rssiById by container.scanner.advertisementRssi.collectAsState() @@ -84,17 +83,9 @@ fun GroupLoggingScreen(onBack: () -> Unit, onSessionHistory: () -> Unit) { var selections by remember { mutableStateOf(listOf(SensorSelection(SensorKey.ACCELEROMETER))) } var showStopDialog by remember { mutableStateOf(false) } - // Candidates: remembered boards, plus the demo fleet when demo mode is on. - val candidates = buildList { - if (demoMode) { - container.demoFleet.forEachIndexed { index, device -> - add(device.identifier to container.demoName(index)) - } - } - remembered.forEach { device -> - if (none { it.first == device.mac }) add(device.mac to device.name) - } - } + // Candidates: every remembered board (a board must have been connected + // to once so the app knows it). + val candidates = remembered.map { it.mac to it.name } fun members(ids: Collection): List = ids.map { id -> GroupCaptureCoordinator.Member( @@ -224,7 +215,7 @@ fun GroupLoggingScreen(onBack: () -> Unit, onSessionHistory: () -> Unit) { if (candidates.isEmpty()) { item { SectionFooter( - "No boards available — connect to a board once so it is remembered, or enable demo mode.", + "No boards available — connect to a board once so it is remembered.", ) } } else { diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/scan/ScanScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/scan/ScanScreen.kt index 45e816f..8df9cdd 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/scan/ScanScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/scan/ScanScreen.kt @@ -20,7 +20,6 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight -import androidx.compose.material.icons.filled.AutoAwesome import androidx.compose.material.icons.filled.BluetoothDisabled import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.FiberManualRecord @@ -31,15 +30,12 @@ import androidx.compose.material.icons.filled.SettingsInputAntenna import androidx.compose.material.icons.filled.WifiTethering import androidx.compose.material.icons.outlined.StopCircle import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -81,8 +77,8 @@ private fun requiredBlePermissions(): Array = } /** - * Connection screen: remembered boards, nearby advertisers, the demo board, - * and a link into session history — all on the brand orange field. + * Connection screen: remembered boards, nearby advertisers, and a link into + * session history — all on the brand orange field. */ @Composable fun ScanScreen( @@ -97,7 +93,6 @@ fun ScanScreen( val isScanning by vm.isScanning.collectAsState() val nearbyAll by vm.nearbyDevices.collectAsState() val remembered by vm.remembered.collectAsState() - val demoMode by vm.demoModeEnabled.collectAsState() val lastError by vm.lastError.collectAsState() val records by container.logSessions.records.collectAsState() val activeId by container.activeDeviceId.collectAsState() @@ -135,8 +130,7 @@ fun ScanScreen( } val bluetoothAvailable = vm.isBluetoothAvailable() - val demoRows = nearbyAll.filter { it.isDemo } - val nearby = nearbyAll.filter { row -> !row.isDemo && remembered.none { it.mac == row.identifier } } + val nearby = nearbyAll.filter { row -> remembered.none { it.mac == row.identifier } } val hasActiveGroup = records.any { it.groupID != null && (it.status == LogSessionRecord.Status.RUNNING || it.status == LogSessionRecord.Status.STOPPED) @@ -215,7 +209,7 @@ fun ScanScreen( when { !bluetoothAvailable -> item { ScanNote( - "Bluetooth is turned off or unavailable — turn it on to scan, or use demo mode.", + "Bluetooth is off — turn it on to scan.", icon = Icons.Filled.BluetoothDisabled, tint = Palette.warning, ) @@ -241,44 +235,6 @@ fun ScanScreen( } } - item { SectionHeader("Demo", color = Color.White) } - item { - BrandCard( - containerColor = MaterialTheme.colorScheme.surfaceContainerLowest, - contentPadding = PaddingValues(0.dp), - verticalSpacing = 0.dp, - ) { - ListItem( - headlineContent = { Text("Demo mode") }, - supportingContent = { Text("Synthetic sensors — no hardware needed") }, - trailingContent = { - Switch(checked = demoMode, onCheckedChange = { vm.setDemoMode(it) }) - }, - colors = cardListItemColors(), - ) - demoRows.forEach { demo -> - HorizontalDivider() - val isConnecting = activeId == demo.identifier && activeState == DeviceState.Connecting - ListItem( - headlineContent = { Text(demo.name) }, - supportingContent = { Text("Tap to connect to the simulated board") }, - leadingContent = { - Icon(Icons.Filled.AutoAwesome, contentDescription = null, tint = Palette.accent) - }, - trailingContent = { - if (isConnecting) { - CircularProgressIndicator(modifier = Modifier.size(20.dp), strokeWidth = 2.dp) - } else { - Icon(Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null) - } - }, - colors = cardListItemColors(), - modifier = Modifier.combinedClickable(onClick = { connect(demo.identifier) }), - ) - } - } - } - item { BrandCard( containerColor = MaterialTheme.colorScheme.surfaceContainerLowest, diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt index 5e4587f..d9e48d6 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt @@ -39,8 +39,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp -import com.mbientlab.metawear.app.demo.DemoBleTransport -import com.mbientlab.metawear.app.ui.appContainer import com.mbientlab.metawear.app.ui.appViewModel import com.mbientlab.metawear.app.ui.components.ActionRow import com.mbientlab.metawear.app.ui.components.AppScaffold @@ -62,15 +60,11 @@ import com.mbientlab.metawear.sensor.Settings @Composable fun SettingsScreen(onFactoryReset: () -> Unit, onBack: () -> Unit) { val vm = appViewModel(::SettingsViewModel) - val container = appContainer() val statusMessage by vm.statusMessage.collectAsState() val lastError by vm.lastError.collectAsState() val didFactoryReset by vm.didFactoryReset.collectAsState() - // Firmware update is a real-hardware operation — hidden for the demo - // board, whose synthetic firmware revision the catalog can't resolve. - val isDemo = container.activeDeviceId.collectAsState().value == DemoBleTransport.DEVICE_IDENTIFIER - val firmwareVm = if (isDemo) null else appViewModel(::FirmwareUpdateViewModel) + val firmwareVm = appViewModel(::FirmwareUpdateViewModel) var name by remember { mutableStateOf("") } var intervalMs by remember { mutableStateOf("417") } @@ -131,10 +125,8 @@ fun SettingsScreen(onFactoryReset: () -> Unit, onBack: () -> Unit) { } } - if (firmwareVm != null) { - item { SectionHeader("Firmware") } - item { FirmwareSection(firmwareVm) } - } + item { SectionHeader("Firmware") } + item { FirmwareSection(firmwareVm) } item { SectionHeader("Advertising") } item { diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/vm/DeviceViewModel.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/vm/DeviceViewModel.kt index 2b7adbc..1793e5e 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/vm/DeviceViewModel.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/vm/DeviceViewModel.kt @@ -10,7 +10,6 @@ import com.mbientlab.metawear.app.data.LogDownloader import com.mbientlab.metawear.app.data.OrphanLogState import com.mbientlab.metawear.app.data.RememberedDevice import com.mbientlab.metawear.app.data.foreignLogDecision -import com.mbientlab.metawear.app.demo.DemoBleTransport import com.mbientlab.metawear.clearLog import com.mbientlab.metawear.model.BatteryState import com.mbientlab.metawear.queryActiveLoggers @@ -72,7 +71,6 @@ class DeviceViewModel(private val container: AppContainer) : ViewModel() { val displayName: String get() { val id = device?.identifier ?: return "MetaWear" - if (id == DemoBleTransport.DEVICE_IDENTIFIER) return DemoBleTransport.DEVICE_NAME return container.scanner.advertisedNames.value[id]?.ifEmpty { null } ?: container.remembered.devices.value.firstOrNull { it.mac == id }?.name ?: id @@ -198,8 +196,6 @@ class DeviceViewModel(private val container: AppContainer) : ViewModel() { } private fun rememberDevice(device: MetaWearDevice) { - // The demo device is never persisted. - if (device.identifier == DemoBleTransport.DEVICE_IDENTIFIER) return val info = device.deviceInfo container.remembered.upsert( RememberedDevice( diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/vm/ScannerViewModel.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/vm/ScannerViewModel.kt index eeaa9bc..b67e44d 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/vm/ScannerViewModel.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/vm/ScannerViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.mbientlab.metawear.app.AppContainer import com.mbientlab.metawear.app.core.DeviceFreshness -import com.mbientlab.metawear.app.demo.DemoBleTransport import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -16,9 +15,8 @@ import kotlinx.datetime.Clock import kotlinx.datetime.Instant /** - * Scan-screen state: nearby devices merged from the SDK scanner's StateFlows, - * remembered devices from the prefs store, plus the demo device when demo - * mode is on. + * Scan-screen state: nearby devices merged from the SDK scanner's StateFlows + * plus remembered devices from the prefs store. */ class ScannerViewModel(private val container: AppContainer) : ViewModel() { @@ -27,13 +25,11 @@ class ScannerViewModel(private val container: AppContainer) : ViewModel() { val identifier: String, val name: String, val rssi: Int?, - val isDemo: Boolean = false, ) private val scanner = container.scanner val isScanning: StateFlow = scanner.isScanning - val demoModeEnabled: StateFlow = container.demoModeEnabled val remembered = container.remembered.devices private val _lastError = MutableStateFlow(null) @@ -67,16 +63,15 @@ class ScannerViewModel(private val container: AppContainer) : ViewModel() { } } - /** Nearby devices: discovered + fresh, demo device prepended when enabled. */ + /** Nearby devices: discovered and still fresh, sorted by name. */ val nearbyDevices: StateFlow> = combine( scanner.discoveredDevices, scanner.advertisedNames, scanner.advertisementRssi, - container.demoModeEnabled, lastSeen, - ) { discovered, names, rssi, demo, seen -> + ) { discovered, names, rssi, seen -> val now = Clock.System.now() - val rows = discovered.keys + discovered.keys .filter { DeviceFreshness.isFresh(seen[it], now) || !isScanningNow() } .map { id -> NearbyDevice( @@ -86,11 +81,6 @@ class ScannerViewModel(private val container: AppContainer) : ViewModel() { ) } .sortedBy { it.name } - if (demo) { - listOf(NearbyDevice(DemoBleTransport.DEVICE_IDENTIFIER, DemoBleTransport.DEVICE_NAME, rssi = -52, isDemo = true)) + rows - } else { - rows - } }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) private fun isScanningNow(): Boolean = scanner.isScanning.value @@ -103,7 +93,7 @@ class ScannerViewModel(private val container: AppContainer) : ViewModel() { fun startScan() { if (!container.isBluetoothAvailable()) { - _lastError.value = "Bluetooth is unavailable — try demo mode" + _lastError.value = "Bluetooth is off — turn it on to scan" return } runCatching { scanner.startScan() } @@ -114,10 +104,6 @@ class ScannerViewModel(private val container: AppContainer) : ViewModel() { runCatching { scanner.stopScan() } } - fun setDemoMode(enabled: Boolean) { - container.demoModeEnabled.value = enabled - } - /** Mark [identifier] active and hand off to the device detail flow. */ fun select(identifier: String) { container.activeDeviceId.value = identifier diff --git a/app/src/test/kotlin/com/mbientlab/metawear/app/DemoBleTransportTest.kt b/app/src/test/kotlin/com/mbientlab/metawear/app/DemoBleTransportTest.kt index 53d4824..a4ffe21 100644 --- a/app/src/test/kotlin/com/mbientlab/metawear/app/DemoBleTransportTest.kt +++ b/app/src/test/kotlin/com/mbientlab/metawear/app/DemoBleTransportTest.kt @@ -38,8 +38,8 @@ import org.junit.jupiter.api.Test /** * End-to-end exercises of the demo emulator through the real - * [MetaWearDevice] protocol stack — the same path the app uses in demo mode, - * with no Android or Bluetooth anywhere. + * [MetaWearDevice] protocol stack — the same path the app takes against a + * physical board, with no Android or Bluetooth anywhere. */ class DemoBleTransportTest { diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/demo/DemoBleTransport.kt b/app/src/test/kotlin/com/mbientlab/metawear/app/demo/DemoBleTransport.kt similarity index 97% rename from app/src/main/kotlin/com/mbientlab/metawear/app/demo/DemoBleTransport.kt rename to app/src/test/kotlin/com/mbientlab/metawear/app/demo/DemoBleTransport.kt index c16faae..4102588 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/demo/DemoBleTransport.kt +++ b/app/src/test/kotlin/com/mbientlab/metawear/app/demo/DemoBleTransport.kt @@ -22,8 +22,9 @@ import kotlinx.coroutines.isActive import kotlinx.coroutines.launch /** - * A protocol-level MetaWear emulator for demos, emulator runs, and screenshots - * — no hardware, no Bluetooth. + * A protocol-level MetaWear emulator used as a JVM test fixture — no hardware, + * no Bluetooth. Lives in the test source set only; the shipping app never + * constructs it. * * Behaves like a connected MetaMotion S on firmware 1.7.3: it answers module * discovery, Device Information reads, battery/MAC/log reads, streams @@ -32,13 +33,13 @@ import kotlinx.coroutines.launch * emulates the logging round trip (trigger allocation, LOG_LENGTH growth * while "recording", paged readout with progress). * - * Pure Kotlin (no Android imports) so the whole demo pipeline is exercised by - * JVM unit tests through the real [com.mbientlab.metawear.MetaWearDevice]. + * Pure Kotlin (no Android imports) so the whole app device stack is exercised + * by JVM unit tests through the real [com.mbientlab.metawear.MetaWearDevice]. * * Each instance wears an [Identity] — [Identity.board] mints up to 16 * distinguishable boards (identifier, serial, MAC, waveform phase) so - * multi-board flows are exercisable with a simulated fleet; the default is - * the legacy single demo board. + * multi-board flows are testable with a simulated fleet; the default is the + * legacy single demo board. */ class DemoBleTransport( private val scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default), @@ -47,8 +48,8 @@ class DemoBleTransport( companion object { /** - * Stable identifier so app-side code can recognise the demo device. - * Equal to `Identity.board(0).identifier` — the legacy single demo board. + * Stable identifier of the default board — equal to + * `Identity.board(0).identifier`, the legacy single demo board. */ const val DEVICE_IDENTIFIER: String = "DE:30:DE:30:DE:30" @@ -513,7 +514,7 @@ class DemoBleTransport( // any 2-chunk cartesian logger pair (accel/gyro/mag) plus every // single-chunk environmental logger — temperature (Int16 °C × 8), // humidity (UInt32 % × 1024), pressure (UInt32 Pa × 256) — so both - // streamed and polled logging round-trip in demo mode. + // streamed and polled logging round-trip through the emulator. class EnvProducer(val loggerId: Int, val rawData: (Double) -> Long) val cartesianPair: List diff --git a/settings.gradle.kts b/settings.gradle.kts index 7cc53f5..6b18f6c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -38,6 +38,6 @@ include(":metawear-persistence") // (catalog client, bootloader interlock, DFU orchestration). include(":metawear-firmware") -// Step 5: Jetpack Compose demo app -// (scan, live stream, logging, sessions, controls, settings, firmware, demo mode). +// Step 5: Jetpack Compose MetaWear app +// (scan, live stream, logging, sessions, controls, settings, firmware). include(":app") From d94d405cba5f8cd730bb75eb615fbc1d5a9d565c Mon Sep 17 00:00:00 2001 From: lkasso Date: Mon, 17 Aug 2026 08:27:32 -0700 Subject: [PATCH 4/9] =?UTF-8?q?Settings:=20Logging=20section=20=E2=80=94?= =?UTF-8?q?=20entry=20count,=20armed=20loggers,=20clear?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shows what the board's logging module holds: LOG_LENGTH entry count and every armed logger (id, module, register, chunk) — including stale ones left by earlier sessions or other apps — with Refresh and a confirmed Clear Logs & Loggers action (stops on-board logging, drops entries, removes all loggers). After a clear the count is set to zero locally rather than re-read, since the MMS reports a housekeeping sentinel for up to a minute. Co-Authored-By: Claude Fable 5 --- .../app/ui/settings/SettingsScreen.kt | 78 +++++++++++++++++++ .../metawear/app/vm/SettingsViewModel.kt | 70 +++++++++++++++++ 2 files changed, 148 insertions(+) diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt index d9e48d6..958c682 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt @@ -13,6 +13,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.DeleteSweep import androidx.compose.material.icons.filled.EventBusy import androidx.compose.material.icons.filled.FlashlightOff import androidx.compose.material.icons.filled.Memory @@ -41,6 +42,7 @@ import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import com.mbientlab.metawear.app.ui.appViewModel import com.mbientlab.metawear.app.ui.components.ActionRow +import com.mbientlab.metawear.app.ui.components.LabeledValue import com.mbientlab.metawear.app.ui.components.AppScaffold import com.mbientlab.metawear.app.ui.components.BrandCard import com.mbientlab.metawear.app.ui.components.GroupCard @@ -63,6 +65,8 @@ fun SettingsScreen(onFactoryReset: () -> Unit, onBack: () -> Unit) { val statusMessage by vm.statusMessage.collectAsState() val lastError by vm.lastError.collectAsState() val didFactoryReset by vm.didFactoryReset.collectAsState() + val loggingStatus by vm.loggingStatus.collectAsState() + val loggingBusy by vm.loggingBusy.collectAsState() val firmwareVm = appViewModel(::FirmwareUpdateViewModel) @@ -74,6 +78,7 @@ fun SettingsScreen(onFactoryReset: () -> Unit, onBack: () -> Unit) { var showClearMacrosDialog by remember { mutableStateOf(false) } var showClearEventsDialog by remember { mutableStateOf(false) } var showRestartDialog by remember { mutableStateOf(false) } + var showClearLogsDialog by remember { mutableStateOf(false) } LaunchedEffect(didFactoryReset) { // Board rebooted; return to the device hub, which offers Reconnect. @@ -128,6 +133,64 @@ fun SettingsScreen(onFactoryReset: () -> Unit, onBack: () -> Unit) { item { SectionHeader("Firmware") } item { FirmwareSection(firmwareVm) } + item { SectionHeader("Logging") } + item { + Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { + GroupCard { + val status = loggingStatus + LabeledValue( + "Entries in flash", + when { + status == null && loggingBusy -> "Reading…" + status == null -> "—" + else -> "%,d".format(status.entryCount) + }, + ) + HorizontalDivider() + LabeledValue( + "Active loggers", + when { + status == null -> "—" + status.activeLoggers.isEmpty() -> "None" + else -> status.activeLoggers.size.toString() + }, + ) + // One line per armed logger so a stale configuration + // from an earlier session (or another app) is visible. + status?.activeLoggers?.forEach { logger -> + HorizontalDivider() + LabeledValue( + " Logger ${logger.loggerID}", + "${logger.module.name.lowercase()} · reg 0x%02X · offset %d, %d B".format( + logger.register, logger.chunkOffset, logger.chunkLength, + ), + monospace = true, + ) + } + HorizontalDivider() + ActionRow( + "Refresh", + Icons.Filled.Refresh, + enabled = !loggingBusy, + onClick = { vm.refreshLoggingStatus() }, + ) + HorizontalDivider() + ActionRow( + "Clear Logs & Loggers", + Icons.Filled.DeleteSweep, + tint = Palette.danger, + enabled = !loggingBusy, + onClick = { showClearLogsDialog = true }, + ) + } + SectionFooter( + "Loggers stay armed on the board across disconnects and app restarts, from every app that " + + "ever configured one. The entry count is reported in flash pages, so it can read 0 for the " + + "first seconds of a session. Clear removes every logger and drops all entries — " + + "download first if you want the data.", + ) + } + } item { SectionHeader("Advertising") } item { Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { @@ -262,6 +325,21 @@ fun SettingsScreen(onFactoryReset: () -> Unit, onBack: () -> Unit) { ) } + if (showClearLogsDialog) { + ConfirmDialog( + title = "Clear all logs and loggers?", + text = "Stops any on-board logging, drops every entry in flash, and removes every armed logger — " + + "from ALL apps. Any session that hasn't been downloaded is lost.", + confirmLabel = "Clear Logs & Loggers", + destructive = true, + onConfirm = { + showClearLogsDialog = false + vm.clearLogsAndLoggers() + }, + onDismiss = { showClearLogsDialog = false }, + ) + } + if (showRestartDialog) { ConfirmDialog( title = "Restart this MetaWear?", diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/vm/SettingsViewModel.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/vm/SettingsViewModel.kt index 667a57a..4999923 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/vm/SettingsViewModel.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/vm/SettingsViewModel.kt @@ -4,8 +4,13 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.mbientlab.metawear.MetaWearDevice import com.mbientlab.metawear.app.AppContainer +import com.mbientlab.metawear.clearLog +import com.mbientlab.metawear.model.ActiveLogger +import com.mbientlab.metawear.queryActiveLoggers +import com.mbientlab.metawear.sensor.LogLength import com.mbientlab.metawear.sensor.Settings import com.mbientlab.metawear.sensor.eraseAllMacros +import com.mbientlab.metawear.stopOnBoardLogging import com.mbientlab.metawear.sensor.removeAllEvents import com.mbientlab.metawear.sensor.removeAllTimers import com.mbientlab.metawear.sensor.stopLed @@ -32,6 +37,71 @@ class SettingsViewModel(private val container: AppContainer) : ViewModel() { private val _didFactoryReset = MutableStateFlow(false) val didFactoryReset: StateFlow = _didFactoryReset.asStateFlow() + // ---- On-board logging state ---- + + /** What the board's logging module currently holds. */ + data class LoggingStatus( + /** Entries in flash as reported by LOG_LENGTH (page-granular on MMS). */ + val entryCount: Long, + /** Loggers armed on the board — from every app that ever configured one. */ + val activeLoggers: List, + ) + + private val _loggingStatus = MutableStateFlow(null) + /** `null` until the first refresh completes (or the device is absent). */ + val loggingStatus: StateFlow = _loggingStatus.asStateFlow() + + private val _loggingBusy = MutableStateFlow(false) + /** True while a refresh or clear is in flight. */ + val loggingBusy: StateFlow = _loggingBusy.asStateFlow() + + init { + refreshLoggingStatus() + } + + /** Re-read the entry count and enumerate the loggers armed on the board. */ + fun refreshLoggingStatus() { + val device = device ?: return + viewModelScope.launch { + _loggingBusy.value = true + runCatching { + val count = device.read(LogLength()).value + val loggers = device.queryActiveLoggers() + LoggingStatus(entryCount = count, activeLoggers = loggers) + } + .onSuccess { _loggingStatus.value = it } + .onFailure { _lastError.value = "Couldn't read logging state: ${it.message}" } + _loggingBusy.value = false + } + } + + /** + * Stop any on-board logging, drop every flash entry, and remove every + * armed logger — a clean slate for the logging module. Destructive: + * un-downloaded data is gone. + * + * The status is refreshed afterward but LOG_LENGTH is set straight to + * zero locally rather than trusted from the board: the MMS reports a + * housekeeping sentinel (~1) for up to a minute after a clear. + */ + fun clearLogsAndLoggers() { + val device = device ?: return + viewModelScope.launch { + _loggingBusy.value = true + runCatching { + device.stopOnBoardLogging() + device.clearLog() + } + .onSuccess { + _statusMessage.value = "Logs and loggers cleared" + val loggers = runCatching { device.queryActiveLoggers() }.getOrDefault(emptyList()) + _loggingStatus.value = LoggingStatus(entryCount = 0, activeLoggers = loggers) + } + .onFailure { _lastError.value = "Clear failed: ${it.message}" } + _loggingBusy.value = false + } + } + fun isNameValid(name: String): Boolean = Settings.isNameValid(name) /** Write a new BLE advertising name (validated; ≤ 26 ASCII chars). */ From e6de830c38d662df2950bbb90f2446e6e14e0e12 Mon Sep 17 00:00:00 2001 From: lkasso Date: Mon, 17 Aug 2026 08:37:15 -0700 Subject: [PATCH 5/9] Logging screen reconciles with the board's actual state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The screen was driven purely by the app's local session records, so it showed Start while the board was already logging (loggers armed by an earlier run, another app, or a lost record) and Download after the data had been cleared out from under a stale record. LogSessionViewModel now reconciles with the board on entry: loggers armed with no local running session surfaces a new BoardLogging phase (status card + a Stop board action); local pending records with no loggers and no entries on the board are dropped. SDK gains stopAndRemoveLoggers() — disable logging and remove all loggers while keeping flash entries recoverable — with a wire-byte test. Co-Authored-By: Claude Fable 5 --- .../metawear/app/ui/logging/LoggingScreen.kt | 25 +++++- .../metawear/app/vm/LogSessionViewModel.kt | 78 +++++++++++++++++++ .../com/mbientlab/metawear/DeviceLogging.kt | 16 ++++ .../com/mbientlab/metawear/LoggingTest.kt | 16 ++++ 4 files changed, 134 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/LoggingScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/LoggingScreen.kt index f226ac7..37e6dfd 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/LoggingScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/LoggingScreen.kt @@ -108,6 +108,15 @@ fun LoggingScreen(onBack: () -> Unit) { Spacer(Modifier.width(4.dp)) Text("Stop") } + phase is LogSessionViewModel.Phase.BoardLogging -> OutlinedButton( + onClick = { vm.stopBoardLogging() }, + enabled = !isBusy, + modifier = Modifier.padding(end = 8.dp), + ) { + Icon(Icons.Filled.Stop, contentDescription = null, modifier = Modifier.size(18.dp)) + Spacer(Modifier.width(4.dp)) + Text("Stop board") + } pending.isNotEmpty() && downloadPhase is DownloadViewModel.Phase.Idle -> Button( onClick = { downloadVm.downloadAll(pending) }, modifier = Modifier.padding(end = 8.dp), @@ -168,6 +177,12 @@ fun LoggingScreen(onBack: () -> Unit) { Palette.success, "Stopped · ${formatDuration(elapsed)} captured", ) + is LogSessionViewModel.Phase.BoardLogging -> Triple( + Icons.Filled.FiberManualRecord, + Palette.warning, + "Board is logging on its own · ${p.loggerCount} logger" + + (if (p.loggerCount == 1) "" else "s") + " · ${"%,d".format(p.entryCount)} entries", + ) } Column(verticalArrangement = Arrangement.spacedBy(6.dp)) { BrandCard { @@ -176,7 +191,15 @@ fun LoggingScreen(onBack: () -> Unit) { Text(text, style = MaterialTheme.typography.bodyLarge, fontFamily = FontFamily.Monospace) } } - SectionFooter("The board keeps logging even if you close the app.") + SectionFooter( + if (phase is LogSessionViewModel.Phase.BoardLogging) { + "This app didn't start this session — it was armed by an earlier run, another app, " + + "or a session whose record was lost. Stop board keeps the recorded entries; " + + "recover them from Settings, or clear them there." + } else { + "The board keeps logging even if you close the app." + }, + ) } } diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/vm/LogSessionViewModel.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/vm/LogSessionViewModel.kt index 0d20095..39b0124 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/vm/LogSessionViewModel.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/vm/LogSessionViewModel.kt @@ -9,7 +9,12 @@ import com.mbientlab.metawear.app.data.ConfiguredSensor import com.mbientlab.metawear.app.data.LogSessionRecord import com.mbientlab.metawear.app.data.startLoggingOn import com.mbientlab.metawear.app.data.stopLoggingOn +import com.mbientlab.metawear.clearLog import com.mbientlab.metawear.flushLogPage +import com.mbientlab.metawear.queryActiveLoggers +import com.mbientlab.metawear.sensor.LogLength +import com.mbientlab.metawear.sensor.LoggingEnabled +import com.mbientlab.metawear.stopAndRemoveLoggers import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow @@ -30,6 +35,15 @@ class LogSessionViewModel(private val container: AppContainer) : ViewModel() { data object Idle : Phase() data class Running(val startedAt: Instant) : Phase() data object Stopped : Phase() + + /** + * The board is logging (loggers armed) but this app holds no running + * session for it — a session from an earlier run of the app, another + * app, or one whose record was lost. The screen offers to stop the + * board's logging (data can then be recovered as a foreign log) rather + * than showing a misleading Start. + */ + data class BoardLogging(val loggerCount: Int, val entryCount: Long) : Phase() } private val device: MetaWearDevice? = container.activeDevice() @@ -70,6 +84,70 @@ class LogSessionViewModel(private val container: AppContainer) : ViewModel() { _phase.value = Phase.Running(startedAt) startElapsedTicker(startedAt) } + // Local records are only a memory of what this app did; the board + // is the truth. Reconcile the two so the screen never shows Start + // while the board is logging, or Download for data that's gone. + reconcileWithBoard() + } + } + + /** + * Ask the board what it actually holds and reconcile the local records: + * + * - Loggers armed on the board but no local running session → the board + * is logging on its own; surface [Phase.BoardLogging]. + * - Local RUNNING/STOPPED records but the board holds no loggers and no + * entries → the session was cleared out from under them (Settings → + * Clear, factory reset, another app); drop them so no phantom Download. + * + * Best-effort: any read failure leaves the local view untouched. + */ + fun reconcileWithBoard() { + val device = device ?: return + viewModelScope.launch { + val loggers = runCatching { device.queryActiveLoggers() }.getOrNull() ?: return@launch + val entries = runCatching { device.read(LogLength()).value }.getOrDefault(0L) + val local = container.logSessions.pendingFor(device.identifier) + val localRunning = local.any { it.status == LogSessionRecord.Status.RUNNING } + + when { + loggers.isNotEmpty() && !localRunning -> { + // Stale STOPPED records for a board that is now logging + // again refer to data that no longer maps to them. + if (local.isNotEmpty()) container.logSessions.remove(local.map { it.id }) + _phase.value = Phase.BoardLogging(loggers.size, entries) + } + loggers.isEmpty() && entries == 0L && local.isNotEmpty() -> { + container.logSessions.remove(local.map { it.id }) + activeSensors.clear() + elapsedJob?.cancel() + _phase.value = Phase.Idle + } + loggers.isEmpty() && _phase.value is Phase.BoardLogging -> _phase.value = Phase.Idle + } + } + } + + /** + * Stop logging that the board is doing on its own (see [Phase.BoardLogging]): + * flush the last page so whatever landed is readable, then disable the + * logging module and remove its loggers — entries are KEPT, so the data + * can still be recovered (Settings → Clear drops it if unwanted). + */ + fun stopBoardLogging() { + val device = device ?: return + viewModelScope.launch { + _isBusy.value = true + try { + runCatching { device.flushLogPage() } + device.stopAndRemoveLoggers() + _phase.value = Phase.Idle + reconcileWithBoard() + } catch (e: Exception) { + _lastError.value = e.message ?: "Couldn't stop the board's logging" + } finally { + _isBusy.value = false + } } } diff --git a/metawear-protocol/src/main/kotlin/com/mbientlab/metawear/DeviceLogging.kt b/metawear-protocol/src/main/kotlin/com/mbientlab/metawear/DeviceLogging.kt index 01bb820..655e002 100644 --- a/metawear-protocol/src/main/kotlin/com/mbientlab/metawear/DeviceLogging.kt +++ b/metawear-protocol/src/main/kotlin/com/mbientlab/metawear/DeviceLogging.kt @@ -819,6 +819,22 @@ suspend fun MetaWearDevice.stopOnBoardLogging() { writeRaw(Packet.command(Module.LOGGING, LOG_ENABLE, 0x00)) } +/** + * Stop on-board logging AND remove every armed logger, but keep the entries + * already in flash so they can still be downloaded (as a foreign log via + * anonymous signals, or after `recoverLoggers`). + * + * This is the "take the board out of a logging state you didn't start" + * action: [stopOnBoardLogging] alone leaves the loggers armed (the board + * still *reports* itself as logging), while [clearLog] also drops the data. + * The local logger registry is cleared to match the board. + */ +suspend fun MetaWearDevice.stopAndRemoveLoggers() { + writeRaw(Packet.command(Module.LOGGING, LOG_ENABLE, 0x00)) // stop logging + writeRaw(Packet.command(Module.LOGGING, LOG_REMOVE_ALL_TRIGGERS)) // remove all loggers, keep entries + loggerRegistry.clear() +} + /** * Flush the active logging page to flash so in-flight samples become readable. * diff --git a/metawear-protocol/src/test/kotlin/com/mbientlab/metawear/LoggingTest.kt b/metawear-protocol/src/test/kotlin/com/mbientlab/metawear/LoggingTest.kt index 699691a..fa5fdf2 100644 --- a/metawear-protocol/src/test/kotlin/com/mbientlab/metawear/LoggingTest.kt +++ b/metawear-protocol/src/test/kotlin/com/mbientlab/metawear/LoggingTest.kt @@ -500,6 +500,22 @@ class LoggingTest { assertTrue(after.none { it.size >= 2 && (it[0].toInt() and 0xFF) == 0x0B && (it[1].toInt() and 0xFF) == 0x10 }) } + // ---- stopAndRemoveLoggers ---- + + @Test + fun `stopAndRemoveLoggers disables logging and removes loggers but keeps entries`() = runTest { + val (device, transport) = connectedDevice() + transport.clearWrites() + device.stopAndRemoveLoggers() + + val cmds = transport.writtenCommands + assertArrayEquals(bytes(0x0B, 0x01, 0x00), cmds[0]) // logging disable + assertArrayEquals(bytes(0x0B, 0x0A), cmds[1]) // remove all loggers + assertEquals(2, cmds.size) + // Never the drop-entries command — data must survive. + assertTrue(cmds.none { it.size >= 2 && (it[0].toInt() and 0xFF) == 0x0B && (it[1].toInt() and 0xFF) == 0x09 }) + } + // ---- clearLog ---- @Test From d73f64845bac4918bfc5c0544a09641ea232f0ac Mon Sep 17 00:00:00 2001 From: lkasso Date: Mon, 17 Aug 2026 08:47:48 -0700 Subject: [PATCH 6/9] Settings: logger rows as ListItems; opaque top bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The per-logger detail was rendered through the two-column label/value row, and its width starved the label into a one-character column. Each logger is now a ListItem (headline + supporting byte-range line). The default AppScaffold top bar was transparent at rest — its scrolled color swap depends on a scrollBehavior that was never wired — so list content scrolled up underneath the title; it is now opaque. Co-Authored-By: Claude Fable 5 --- .../metawear/app/ui/components/Components.kt | 6 +++- .../app/ui/settings/SettingsScreen.kt | 30 ++++++++++++++----- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/Components.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/Components.kt index ed8219b..da7fa41 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/Components.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/components/Components.kt @@ -227,8 +227,12 @@ fun AppScaffold( navigationIconContentColor = topBarContentColor, ) } else { + // Opaque at rest. A transparent container only works with + // a scrollBehavior driving the scrolled color swap; without + // one the bar stays see-through and list content scrolls + // up underneath the title. TopAppBarDefaults.topAppBarColors( - containerColor = Color.Transparent, + containerColor = MaterialTheme.colorScheme.surface, scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainer, ) }, diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt index 958c682..6b37ee6 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/settings/SettingsScreen.kt @@ -20,6 +20,8 @@ import androidx.compose.material.icons.filled.Memory import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.AlertDialog +import androidx.compose.material3.ListItem +import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.FilterChip import androidx.compose.material3.HorizontalDivider @@ -39,6 +41,8 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.unit.dp import com.mbientlab.metawear.app.ui.appViewModel import com.mbientlab.metawear.app.ui.components.ActionRow @@ -155,16 +159,28 @@ fun SettingsScreen(onFactoryReset: () -> Unit, onBack: () -> Unit) { else -> status.activeLoggers.size.toString() }, ) - // One line per armed logger so a stale configuration + // One row per armed logger so a stale configuration // from an earlier session (or another app) is visible. + // A ListItem (headline + supporting line) instead of a + // label/value pair: the detail string is wide and a + // two-column row would starve the label of width. status?.activeLoggers?.forEach { logger -> HorizontalDivider() - LabeledValue( - " Logger ${logger.loggerID}", - "${logger.module.name.lowercase()} · reg 0x%02X · offset %d, %d B".format( - logger.register, logger.chunkOffset, logger.chunkLength, - ), - monospace = true, + ListItem( + headlineContent = { Text("Logger ${logger.loggerID}") }, + supportingContent = { + Text( + "${logger.module.name.lowercase().replaceFirstChar { it.uppercase() }} · " + + "register 0x%02X · bytes %d–%d".format( + logger.register, + logger.chunkOffset, + logger.chunkOffset + logger.chunkLength - 1, + ), + fontFamily = FontFamily.Monospace, + style = MaterialTheme.typography.bodySmall, + ) + }, + colors = ListItemDefaults.colors(containerColor = Color.Transparent), ) } HorizontalDivider() From fbb8b4eb8752734fe8e76424c0030029a2c88710 Mon Sep 17 00:00:00 2001 From: lkasso Date: Mon, 17 Aug 2026 09:00:03 -0700 Subject: [PATCH 7/9] Logging screen: status and download feedback above the fold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The download progress and result cards were the last items in the LazyColumn, below the full sensor picker — a download would run to completion (7 s for 2,560 entries on a MetaMotion S) with nothing visible on screen, so it looked like the button did nothing. Status card, then the Download section (pending records, live progress with entry count and percent, completion with per-session rows), then the sensor picker. Top-bar action for a board-owned session is now Stop, with the status card explaining that the session was started earlier. Verified end to end on hardware: Start → Stop → Download shows live 23% → complete, 747 samples saved to Session History. Co-Authored-By: Claude Fable 5 --- .../metawear/app/ui/logging/LoggingScreen.kt | 73 ++++++++++--------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/LoggingScreen.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/LoggingScreen.kt index 37e6dfd..5854240 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/LoggingScreen.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/logging/LoggingScreen.kt @@ -115,7 +115,7 @@ fun LoggingScreen(onBack: () -> Unit) { ) { Icon(Icons.Filled.Stop, contentDescription = null, modifier = Modifier.size(18.dp)) Spacer(Modifier.width(4.dp)) - Text("Stop board") + Text("Stop") } pending.isNotEmpty() && downloadPhase is DownloadViewModel.Phase.Idle -> Button( onClick = { downloadVm.downloadAll(pending) }, @@ -148,18 +148,6 @@ fun LoggingScreen(onBack: () -> Unit) { ), verticalArrangement = Arrangement.spacedBy(10.dp), ) { - item { - // While recording, mirror the running loggers into the locked - // picker so the user sees what is actually being captured. - SensorConfigSection( - modules = modules, - selections = if (isRunning && running.isNotEmpty()) running.map { it.selection } else selections, - onSelectionsChange = { selections = it }, - loggingMode = true, // hides stream-only altitude - locked = isRunning, - ) - } - item { val (icon, tint, text) = when (val p = phase) { LogSessionViewModel.Phase.Idle -> Triple( @@ -180,7 +168,7 @@ fun LoggingScreen(onBack: () -> Unit) { is LogSessionViewModel.Phase.BoardLogging -> Triple( Icons.Filled.FiberManualRecord, Palette.warning, - "Board is logging on its own · ${p.loggerCount} logger" + + "Logging · ${p.loggerCount} logger" + (if (p.loggerCount == 1) "" else "s") + " · ${"%,d".format(p.entryCount)} entries", ) } @@ -193,9 +181,9 @@ fun LoggingScreen(onBack: () -> Unit) { } SectionFooter( if (phase is LogSessionViewModel.Phase.BoardLogging) { - "This app didn't start this session — it was armed by an earlier run, another app, " + - "or a session whose record was lost. Stop board keeps the recorded entries; " + - "recover them from Settings, or clear them there." + "The board is currently logging. This session was started earlier — by a previous " + + "run of the app or by another app — so its sensors aren't listed here. Stop ends " + + "it and keeps what was recorded; download or clear it from Settings." } else { "The board keeps logging even if you close the app." }, @@ -203,25 +191,6 @@ fun LoggingScreen(onBack: () -> Unit) { } } - if (isRunning && running.isNotEmpty()) { - item { SectionHeader("Active Loggers") } - item { - GroupCard { - running.forEachIndexed { index, record -> - if (index > 0) HorizontalDivider() - ListItem( - headlineContent = { Text(record.selection.displayLabel) }, - supportingContent = { Text(record.startDate.formatTime()) }, - leadingContent = { - Icon(record.selection.key.icon, contentDescription = null, tint = Palette.accent) - }, - colors = cardListItemColors(), - ) - } - } - } - } - if (pending.isNotEmpty() || downloadPhase !is DownloadViewModel.Phase.Idle) { item { SectionHeader("Download") } if (pending.isNotEmpty()) { @@ -243,6 +212,38 @@ fun LoggingScreen(onBack: () -> Unit) { } item { DownloadStateCard(downloadPhase, onDismiss = { downloadVm.reset() }) } } + + item { + // While recording, mirror the running loggers into the locked + // picker so the user sees what is actually being captured. + SensorConfigSection( + modules = modules, + selections = if (isRunning && running.isNotEmpty()) running.map { it.selection } else selections, + onSelectionsChange = { selections = it }, + loggingMode = true, // hides stream-only altitude + locked = isRunning, + ) + } + + if (isRunning && running.isNotEmpty()) { + item { SectionHeader("Active Loggers") } + item { + GroupCard { + running.forEachIndexed { index, record -> + if (index > 0) HorizontalDivider() + ListItem( + headlineContent = { Text(record.selection.displayLabel) }, + supportingContent = { Text(record.startDate.formatTime()) }, + leadingContent = { + Icon(record.selection.key.icon, contentDescription = null, tint = Palette.accent) + }, + colors = cardListItemColors(), + ) + } + } + } + } + } } } From 7a1b75b48014d88f01941732ab57fa35ebd003f3 Mon Sep 17 00:00:00 2001 From: lkasso Date: Mon, 17 Aug 2026 09:10:12 -0700 Subject: [PATCH 8/9] Sensor picker: Motion / Fusion / Environmental as collapsible groups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 16-sensor picker was a flat wall of cards on both the Live Stream and Logging screens, pushing everything below it (active loggers, download) two screens down. Motion & Fusion is split into two groups, and each of the three groups sits behind a tappable header that starts expanded only when one of its sensors is selected — the common case is one open group of three rows and two folded lines. Collapsed headers still show a "N selected" count. Co-Authored-By: Claude Fable 5 --- .../app/ui/stream/SensorConfigSection.kt | 97 ++++++++++++++++--- 1 file changed, 82 insertions(+), 15 deletions(-) diff --git a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/stream/SensorConfigSection.kt b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/stream/SensorConfigSection.kt index 813d766..eff131a 100644 --- a/app/src/main/kotlin/com/mbientlab/metawear/app/ui/stream/SensorConfigSection.kt +++ b/app/src/main/kotlin/com/mbientlab/metawear/app/ui/stream/SensorConfigSection.kt @@ -1,30 +1,41 @@ package com.mbientlab.metawear.app.ui.stream +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ExpandLess +import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material3.Checkbox import androidx.compose.material3.FilterChip import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp import com.mbientlab.metawear.app.core.BandwidthAdvisor import com.mbientlab.metawear.app.core.SensorKey import com.mbientlab.metawear.app.core.SensorSelection import com.mbientlab.metawear.app.ui.components.BandwidthCard import com.mbientlab.metawear.app.ui.components.BrandCard -import com.mbientlab.metawear.app.ui.components.SectionHeader import com.mbientlab.metawear.app.ui.components.formatHz import com.mbientlab.metawear.app.ui.components.icon import com.mbientlab.metawear.app.ui.theme.Palette +import com.mbientlab.metawear.app.ui.theme.forText import com.mbientlab.metawear.model.ModuleInfo import com.mbientlab.metawear.protocol.Module @@ -71,22 +82,21 @@ fun SensorConfigSection( val available = availableSensors(modules) .filter { !loggingMode || it.canLog } .filterNot { it in excludeKeys } - val motion = available.filterNot { it.isEnvironmental } - val environmental = available.filter { it.isEnvironmental } + val groups = listOf( + "Motion" to available.filter { !it.isFusion && !it.isEnvironmental }, + "Fusion" to available.filter { it.isFusion }, + "Environmental" to available.filter { it.isEnvironmental }, + ).filter { it.second.isNotEmpty() } Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { - if (motion.isNotEmpty()) { - SectionHeader("Motion & Fusion") - motion.forEach { key -> - SensorCard(key, selections, onSelectionsChange, locked) - } - } - - if (environmental.isNotEmpty()) { - SectionHeader("Environmental") - environmental.forEach { key -> - SensorCard(key, selections, onSelectionsChange, locked) - } + groups.forEach { (title, keys) -> + CollapsibleSensorGroup( + title = title, + keys = keys, + selections = selections, + onSelectionsChange = onSelectionsChange, + locked = locked, + ) } BandwidthCard( @@ -96,6 +106,63 @@ fun SensorConfigSection( } } +/** + * One sensor group behind a tappable header. Starts expanded only when one + * of its sensors is already selected, so a screen with a single active + * sensor shows one open group and two folded ones — and the content below + * the picker (active loggers, download) stays within reach. A collapsed + * group still reports how many of its sensors are selected. + */ +@Composable +private fun CollapsibleSensorGroup( + title: String, + keys: List, + selections: List, + onSelectionsChange: (List) -> Unit, + locked: Boolean, +) { + val selectedCount = keys.count { key -> selections.any { it.key == key } } + var expanded by rememberSaveable(title) { mutableStateOf(selectedCount > 0) } + + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + Row( + modifier = Modifier + .fillMaxWidth() + .clip(MaterialTheme.shapes.small) + .clickable { expanded = !expanded } + .padding(horizontal = 4.dp, vertical = 6.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + title, + style = MaterialTheme.typography.titleSmall, + color = Palette.accent.forText(), + modifier = Modifier.weight(1f), + ) + if (selectedCount > 0) { + Text( + "$selectedCount selected", + style = MaterialTheme.typography.labelMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(end = 8.dp), + ) + } + Icon( + if (expanded) Icons.Filled.ExpandLess else Icons.Filled.ExpandMore, + contentDescription = if (expanded) "Collapse $title" else "Expand $title", + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } + AnimatedVisibility(visible = expanded) { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + keys.forEach { key -> + SensorCard(key, selections, onSelectionsChange, locked) + } + } + } + } +} + @Composable private fun SensorCard( key: SensorKey, From 42be68156f85fbac9565e3afe7ce4731a43b65bb Mon Sep 17 00:00:00 2001 From: lkasso Date: Mon, 17 Aug 2026 09:12:05 -0700 Subject: [PATCH 9/9] Docs: reflect the collapsible picker, logging reconcile, settings Logging section, test counts Co-Authored-By: Claude Fable 5 --- HARDWARE.md | 2 +- README.md | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/HARDWARE.md b/HARDWARE.md index 9fe832d..2db88b5 100644 --- a/HARDWARE.md +++ b/HARDWARE.md @@ -1,6 +1,6 @@ # Hardware verification guide -Everything in this repo is verified by 1,274 JVM tests that run without +Everything in this repo is verified by 1,275 JVM tests that run without hardware. Three things remain that can only be proven against a real board and phone. This guide is the exact procedure. diff --git a/README.md b/README.md index 7a36ae0..3a000c1 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ metawear-android/ The modules are intentionally split so an app can take just protocol + core without pulling in Room or the DFU library. `:metawear-protocol` is the foundation everything else builds on, written test-first to lock wire-format correctness before any BLE code. The full vertical slice — scan → connect → `startStream(accelerometer)` → `Flow>` — runs end-to-end against `MockBleTransport` on the JVM, and against real hardware via `:metawear-core`'s instrumented suites. -The repo carries **1274 JVM tests** across the four testable modules (1042 protocol + 46 persistence + 66 firmware + 120 app), plus **43 BLE + 13 persistence instrumented tests** that need a phone (both hardware-verified on a Pixel 7 + MetaMotion S). +The repo carries **1275 JVM tests** across the four testable modules (1043 protocol + 46 persistence + 66 firmware + 120 app), plus **43 BLE + 13 persistence instrumented tests** that need a phone (both hardware-verified on a Pixel 7 + MetaMotion S). --- @@ -120,12 +120,12 @@ The repo carries **1274 JVM tests** across the four testable modules (1042 proto |------|--------------| | **Scan & connect** | Runtime BLE permission flow, nearby MetaMotion boards with live name + RSSI from the scanner's `StateFlow`s, remembered devices persisted by MAC, reconnect | | **Device hub** | Connection state badge, model / firmware / battery summary, identify (LED flash), reconnect / disconnect, feature navigation | -| **Live stream** | Multi-sensor picker — a "Motion & Fusion" section (accelerometer, gyroscope, magnetometer, and all seven sensor-fusion outputs, with ODR/range chips and a 100 Hz BLE bandwidth advisor) and an "Environmental" section (polled temperature / humidity / pressure with a 1 s–5 min interval picker, plus streamed barometer pressure, altitude, and ambient light). Canvas line charts, live readouts, true effective-Hz, a tared 3D orientation cube on the quaternion output, and a fusion calibration badge | -| **Logging & download** | Start / stop multi-sensor flash logging with an elapsed clock — the board keeps recording while disconnected — then reconnect and download with progress, per-sensor typed decode, and persistence. Pending sessions survive process death (`recoverLoggers` rebuilds the chunk registry) | +| **Live stream** | Multi-sensor picker in three collapsible groups — Motion (accelerometer, gyroscope, magnetometer with ODR/range chips), Fusion (all seven sensor-fusion outputs), and Environmental (polled temperature / humidity / pressure with a 1 s–5 min interval picker, plus streamed barometer pressure, altitude, and ambient light) — each group opening only when one of its sensors is selected, with a 100 Hz BLE bandwidth advisor. Canvas line charts, live readouts, true effective-Hz, a tared 3D orientation cube on the quaternion output, and a fusion calibration badge | +| **Logging & download** | Start / stop multi-sensor flash logging with an elapsed clock — the board keeps recording while disconnected — then reconnect and download with live progress (entry count and percent), per-sensor typed decode, and persistence, all shown above the sensor picker. The screen reconciles with the board on entry: a session the app didn't start (an earlier run, another app) is surfaced as "Logging" with a Stop action that keeps the recorded entries, and stale local records for cleared data are dropped. Pending sessions survive process death (`recoverLoggers` rebuilds the chunk registry) | | **Group logging** | Record the same sensors across a fleet of boards under one shared group id, with a red recording heartbeat that re-arms itself via on-board disconnect events, then stop + download the whole batch with per-board progress. Foreign logs (another app's session) are detected on connect and offered for download via anonymous signals | | **Session history** | Browse saved sessions grouped by board, re-plot them, replay quaternion sessions in 3D with a scrub timeline (1×/2×/4×), swipe-to-delete, and export any session to CSV through the system share sheet | | **Controls** | LED color / pattern presets with play / stop, haptic motor strength and pulse-width sliders, buzzer pulse | -| **Settings** | Validated advertising rename, advertising interval / timeout, TX power, a Maintenance section (reset LED, clear macros, clear events + timers, restart-without-erase), and a confirm-dialog factory reset | +| **Settings** | Validated advertising rename, firmware update, a Logging section (entries in flash, every logger armed on the board with its module / register / byte range, refresh, and a confirmed Clear Logs & Loggers), advertising interval / timeout, TX power, a Maintenance section (reset LED, clear macros, clear events + timers, restart-without-erase), and a confirm-dialog factory reset | | **Firmware** | Catalog update check plus a Nordic-DFU update flow with state / progress UI | ### Running it @@ -1679,9 +1679,9 @@ Module IDs (`com.mbientlab.metawear.protocol.Module`): ./gradlew :metawear-protocol:test --tests '*TimerTest*' ``` -Four JVM test source sets ship with the repo — **1274 tests** in total: +Four JVM test source sets ship with the repo — **1275 tests** in total: -- **`:metawear-protocol`** — 1042 tests across 43 files. The full SDK surface, run against `MockBleTransport`, including reference byte vectors from the MetaWear C++ SDK's Python test suite. No hardware required. +- **`:metawear-protocol`** — 1043 tests across 43 files. The full SDK surface, run against `MockBleTransport`, including reference byte vectors from the MetaWear C++ SDK's Python test suite. No hardware required. - **`:metawear-persistence`** — 46 tests (`PersistableConformanceTest`, `PersistenceStoreTest`, `SessionExportTest`, `AttributionStampTest`) against an in-memory fake DAO — no hardware, no on-disk side effects. - **`:metawear-firmware`** — 66 tests (`BootloaderInterlockTest`, `DFUProgressTest`, `FirmwareBuildTest`, `FirmwareCatalogTest`, `FirmwareExceptionTest`, `FirmwareServerTest`, `MetaWearVersionTest`). - **`:app`** — 120 tests: ring buffer / decimation / effective-Hz math, quaternion cube and tare frame, CSV exporters, session grouping, foreign-log decisions, group-capture coordination, and end-to-end walks through the real `MetaWearDevice` against the test-only `DemoBleTransport`.