Skip to content

Step sensor: expose via getSensorStepData, stop bypassing Health Connect - #20

Merged
dmarttila merged 8 commits into
health-10.2.0from
steps-sensor-hc-merge
Aug 27, 2026
Merged

dmarttila merged 8 commits into
health-10.2.0from
steps-sensor-hc-merge

Conversation

@ojwalch

@ojwalch ojwalch commented Aug 14, 2026

Copy link
Copy Markdown
Member

Companion to shiftapp_flutter#1529.

The step sensor is meant to be a steps-only fallback, but getData() short-circuited on stepSensorActive and sent every data type to the sensor store. Since getSensorData() only handles STEPS, sleep / heart rate / daylight requests silently returned empty lists whenever sensor mode was on.

  • getData() no longer branches on stepSensorActive — Health Connect always serves it.
  • New getSensorStepData method (Kotlin + Dart) returns the sensor's step records, tagged source_name: "sensor_step", including the fetchCacheData refresh that the old path did before reading.

Callers merge the two step feeds per time bucket (max, not sum) rather than picking one — see the app PR.

Merge this first, then re-pin the app's pubspec to a tagged ref.

🤖 Generated with Claude Code

https://claude.ai/code/session_018ktvCjd71EnESb9fS1AwBs

ojwalch and others added 8 commits August 14, 2026 12:07
…lth Connect

The step sensor is a steps-only fallback. Previously, when it was active,
getData() routed EVERY data type to the sensor store, silently returning
empty lists for sleep, heart rate, and daylight. Now getData() always uses
Health Connect, and sensor step records are exposed through a dedicated
getSensorStepData method (source_name "sensor_step") so callers can merge
the two feeds per time bucket instead of choosing one or summing them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ktvCjd71EnESb9fS1AwBs
- getSensorStepData now does its ObjectBox query on Dispatchers.IO via
  the plugin's supervised scope, and replies exactly once on Main.
  Handler(...).run {} is Kotlin's scope function — it ran the block
  inline, so the query and the map serialization were on the platform
  thread. SensorStep rows are only pruned by an explicit
  clearStepSensorData, so an established sensor user's table is large
  enough for that to jank the app on every health fetch.
- An exception in the reply path previously escaped the ad-hoc coroutine
  with no handler and left the Dart caller awaiting a reply that never
  arrived. Every path now returns success or error exactly once.
- Return empty rather than building the ObjectBox store on demand;
  onAttachedToEngine already builds it with a real context, and the
  on-demand call passed the plugin instead of a Context.
- fetchCacheData: persist BEFORE advancing last_q_epoch, and advance
  only to the window actually read. The cursor is the sole lower bound
  on the next read, so moving it past records that were never written
  (a throwing put, or steps recorded while Tasks.await blocked) lost
  them permanently.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ktvCjd71EnESb9fS1AwBs
…leepData

Sleep types are now always routed through getSleepData/_dataSleepQuery
on iOS, so the SLEEP_* filter branches in getData() were unreachable
and duplicated the category-value mapping already encoded in
_requestedIosSleepType. Removed to avoid the two copies drifting.

getSleepData() was writing the literal string "unknown" when a sample
had no HKDevice, which defeats health_data_point.dart's null-coalescing
fallback to Health().deviceId (that fallback only triggers when the key
is absent/null). Now omits source_device_id entirely when there's no
device, matching the pattern used elsewhere in this file.
@dmarttila
dmarttila merged commit c0fcd6b into health-10.2.0 Aug 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants