Skip to content

Implement body-weight Health sync from Health Connect and HealthKit#515

Merged
9thLevelSoftware merged 2 commits into
mainfrom
feat/512-health-body-weight-sync
Jun 7, 2026
Merged

Implement body-weight Health sync from Health Connect and HealthKit#515
9thLevelSoftware merged 2 commits into
mainfrom
feat/512-health-body-weight-sync

Conversation

@9thLevelSoftware

Copy link
Copy Markdown
Owner

No description provided.

Copilot AI review requested due to automatic review settings June 7, 2026 20:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces one-way body weight synchronization from Google Health Connect (Android) and Apple HealthKit (iOS) into the application. It includes the implementation of a sync manager, a source classifier to filter out manual entries and identify eligible scale sources, foreground sync triggers, and UI updates to display the latest synced weight. The review feedback highlights two important improvements: first, avoiding the strict check on the overall granted permission flag in the view model to prevent blocking the integration when optional permissions are denied; second, falling back to metadata keyword matching on Android (similar to iOS) because many smart scales syncing to Health Connect do not properly populate the device type field.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

"hasWorkoutWritePermission=$hasWorkoutWritePermission, " +
"hasBodyWeightReadPermission=$hasBodyWeightReadPermission for ${provider.key}"
}
if (granted && hasWorkoutWritePermission && hasBodyWeightReadPermission) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Checking the granted parameter here can prevent the integration from connecting if the user denies optional permissions (such as writing calories), even if they granted the required workout write and body weight read permissions. Since hasWorkoutWritePermission and hasBodyWeightReadPermission are the actual required permissions, we should only check those to determine if the integration is successfully connected.

Suggested change
if (granted && hasWorkoutWritePermission && hasBodyWeightReadPermission) {
if (hasWorkoutWritePermission && hasBodyWeightReadPermission) {

Comment on lines +51 to +57
return when (evidence.platform) {
HealthBodyWeightSourcePlatform.ANDROID -> {
if (evidence.deviceType == HealthBodyWeightDeviceType.SCALE) {
HealthBodyWeightSourceClassification.ELIGIBLE_SCALE
} else {
HealthBodyWeightSourceClassification.UNKNOWN_SOURCE
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

On Android, only checking deviceType == HealthBodyWeightDeviceType.SCALE is highly restrictive. Many smart scales (e.g., Withings, Renpho) syncing to Health Connect do not populate the device metadata or device type properly, or Health Connect might not expose it if the record was synced via a cloud-to-cloud integration.

We should fall back to hasScaleLikeMetadata(evidence) on Android, just like we do on iOS, to support these popular smart scales whose apps sync to Health Connect but might not populate the device.type field.

            HealthBodyWeightSourcePlatform.ANDROID -> {
                if (evidence.deviceType == HealthBodyWeightDeviceType.SCALE || hasScaleLikeMetadata(evidence)) {
                    HealthBodyWeightSourceClassification.ELIGIBLE_SCALE
                } else {
                    HealthBodyWeightSourceClassification.UNKNOWN_SOURCE
                }
            }

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a169f40629

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +415 to 416
if (healthIntegration.hasPermissions() && healthIntegration.hasBodyWeightReadPermission()) {
markHealthIntegrationConnected(provider, profileId)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Request HealthKit Body Mass for existing connections

On iOS this branch skips requestPermissions() whenever workout write access is already present, but hasBodyWeightReadPermission() in the iOS implementation only checks that HealthKit and the Body Mass type exist because HealthKit does not expose read authorization status. Existing Apple Health users who granted only workout permissions before this change will therefore be marked connected without ever seeing the new Body Mass read prompt, and body-weight queries will just return no samples. In the iOS path, force an authorization request when enabling so the new read type is requested even if workout write permission is already authorized.

Useful? React with 👍 / 👎.

@9thLevelSoftware 9thLevelSoftware linked an issue Jun 7, 2026 that may be closed by this pull request
Resolved conflicts between the body-weight Health sync feature (PR #515)
and the structural Health Connect export refactor (PR #516) that landed
on main. Kept both the body-weight read/sync functionality and the new
unified HealthWorkoutData/segments architecture. Removed the obsolete
writeRoutineWorkout from iOS since writeHealthWorkout now handles both
standalone and routine exports.

https://claude.ai/code/session_01AKWBtFX7AhSMKasky4TaSj

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fb986d62c6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


internal val requiredHealthPermissions = setOf(
HealthPermission.getWritePermission(ExerciseSessionRecord::class),
HealthPermission.getReadPermission(WeightRecord::class),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep body-weight read out of workout write checks

Adding READ_WEIGHT to requiredHealthPermissions also changes Android HealthWorkoutWriter.hasPermissions() and the writeHealthWorkout() guard to require body-weight read access. If a connected user later revokes only the Weight read permission in Health Connect while leaving WRITE_EXERCISE granted, completed workouts and backfills stop exporting even though the write permission needed for that path is still present; keep the workout-write permission check separate from the body-weight import permission check.

Useful? React with 👍 / 👎.

@9thLevelSoftware 9thLevelSoftware merged commit 51e8f5e into main Jun 7, 2026
9 checks passed
@9thLevelSoftware 9thLevelSoftware deleted the feat/512-health-body-weight-sync branch June 7, 2026 23:26
9thLevelSoftware added a commit that referenced this pull request Jun 16, 2026
…ers (#552)

PR #515 added `HealthPermission.getReadPermission(WeightRecord::class)` to
`requiredHealthPermissions`. Because `HealthIntegration.hasPermissions()` is
the only gate guarding `writeHealthWorkout()`, every Android user who had
the Health Connect integration enabled before #515 shipped silently lost
their post-workout export on upgrade:

1. `hasGrantedPermissions(requiredHealthPermissions)` checks the granted set
   contains `Write(ExerciseSession)` AND `Read(WeightRecord)`.
2. Pre-#515 users were only ever asked for `Write(ExerciseSession)`.
3. `hasPermissions()` therefore now returns false for those users.
4. `writeHealthWorkout()` returns `Result.failure(SecurityException)`.
5. The `onFailure` branch in `ActiveSessionEngine.{writeHealthData,
   writeRoutineHealthData}` only logs at `Logger.w` — there is no
   user-facing notification or status reset.

Net effect: workouts complete normally in Phoenix but never reach Health
Connect (and therefore Fitbit / Garmin / etc. via Health Connect), and the
integration card keeps showing `CONNECTED`. The user has no signal that
their data has stopped flowing.

This patch restores the pre-#515 invariant by moving the body-weight read
permission out of `requiredHealthPermissions` into its own
`bodyWeightReadHealthPermissions` set, mirroring the iOS implementation
where `hasPermissions()` only inspects `requiredWriteTypes` and the body-
weight read is gated separately by `hasBodyWeightReadPermission()`. The
launcher still requests body-weight read alongside workout write so new
users get a single permission prompt for both capabilities.

Adds `HealthPermissionScopesTest` to lock the partitioning down:
- `requiredHealthPermissions` must contain only the workout-write
  permission.
- The body-weight read permission must live in its own set.
- The onboarding-only `workoutExportRequestedHealthPermissions` must not
  request body-weight read.
- The integration-toggle launcher (`requestedHealthPermissions`) must still
  prompt for write, optional calories, and body-weight read together.

Issue #531.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Devil <9thLevelSoftware@users.noreply.github.com>
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.

[Feature] Google Health Connect & Apple Health Body Weight Sync

3 participants