Alan Gaudet
AND-02 · Rev 1.2.3

Under Pressure

A blood pressure log for Android with a Rust core that cites a published guideline for every band.

Closed testing on Google Play

See it on the bench

Today screen: last reading with its band strip and ESH 2023 citation, a coverage strip, and the rolling average.
Sample readings from a seeded emulator.
Trends zone scatter: readings plotted over the ESH 2023 band regions with cut points labelled.
Trends, zone view.
Clinician summary: average, range, pulse pressure and band distribution for a period, with Share PDF and CSV buttons.
Clinician summary.

Description

A blood pressure reading means little on its own and a lot as a series. I wanted a log I could type into quickly and read back later as trends, a distribution, and a summary I could hand to a clinician. The app makes no network call and has nothing to sign in to. It labels each reading with the band a cited guideline assigns, and gives no advice, verdict or alert, because advice would move it from the Health and Fitness category into the Medical one, which a personal developer account cannot publish in.

Block diagram

  1. Compose UI
  2. UniFFI boundary
  3. pressure-core
  4. SQLite

Decisions

The core never imports Android

pressure-core is a plain Rust library that imports nothing from Android. Persistence, statistics and guideline classification live there, which puts most of the real logic under cargo test with no emulator in the loop. The same crate backs a desktop CLI, and the export writer and reader are the same code, so they cannot drift apart. The cost is a UniFFI boundary to maintain, with one Kotlin repository file that owns every type conversion across it.

The FFI surface never panics

Every exported function returns a Result, and unwrap, expect, slice indexing, overflowing arithmetic, panic and unreachable are denied by workspace lints. Dependency calls that can panic internally are banned in clippy.toml and wrapped so they return typed errors. The release profile pins panic = "unwind" as the backstop: UniFFI then catches a stray panic and Kotlin gets an untyped InternalException. Under panic = "abort" the same bug would be a native crash, which counts against Android Vitals.

Bands come from a cited guideline edition

Classification tables are data in the Rust core: ESH 2023 by default, with ACC/AHA 2017 as a second choice. The Kotlin layer renders what the core returns and hardcodes no cut point, and the citation appears wherever a band does. The clinician summary formats readings and does not interpret, recommend or flag, and scans over the package enforce that. The cost is that the app can offer no verdicts or alerts about a reading.

Errata

  • Google Play refused the first application for production access, so the app is open only to closed testers until a second application is accepted.
  • A bottom sheet is its own window: if the phone switches between light and dark while a sheet is open, the status bar icons keep the colour of the theme the sheet opened under.

Revision history

Version Changes
1.2.3 Removed enableEdgeToEdge(), whose deprecated window calls the Play Console flagged; bar icons follow the theme picked in the app.
1.2.2 Cleared the Play Console items against 1.2.1: an outdated fragment library and two retired theme attributes.
1.2.0 Themes became full light and dark palettes, and an About room added licence text and credits.
1.1.0 Added ACC/AHA 2017 as a second cited guideline, backups with restore, a pulse chart in Trends, and a new icon.