Commit Graph
15160 Commits
Author SHA1 Message Date
Vitor PamplonaandGitHub fa5d126ba7 Merge pull request #3245 from vitorpamplona/release/1.12.1
Release 1.12.1
v1.12.1
2026-06-17 11:27:32 -04:00
Vitor Pamplona 1437d1e0b0 deleting unnecessary file 2026-06-17 11:21:58 -04:00
Vitor PamplonaandClaude Opus 4.8 0c59bb89ee docs: complete the build/release guide and add RELEASE_OPS
Make BUILDING.md everything a fork needs to build and release, and add a
maintainer-facing ship checklist.

- BUILDING.md: full CI secrets inventory (Android keystore vs GPG/Maven, with
  generation commands), a distribution-channels table (GitHub, Maven, Play,
  F-Droid pull, Zapstore, Homebrew/Winget), and a "generated & vendored
  artifacts" section (Material Symbols subset, Arti native libs).
- RELEASE_OPS.md (new, repo root): Amethyst-specific shipping — pre-tag
  checklist, per-channel rollout (manual Play AAB, Zapstore `zsp publish` with
  our nsec, F-Droid build-from-source), the push-notification server
  (amethyst-push-notif-server), secret ownership, and verification.
- README.md: slim the Deploying section to point at both docs, fix the stale
  quartz dependency version, and add a Maven Central badge plus JitPack
  snapshot docs.
- android-expert skill: drop the duplicated build-config block, delegate to
  gradle-expert, keep only the Android-specific flavor note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 11:20:36 -04:00
Vitor PamplonaandClaude Opus 4.8 02ee493502 chore(release): 1.12.1
Bump the app version to 1.12.1 (versionCode 449) in the catalog, which now
drives every module. Add the v1.12.01 changelog (Health Connect workouts,
share-as-image, immersive system bars, deterministic Tor Active fix) and index
it. Refresh the quartz-integration skill's artifact-version references.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 11:20:18 -04:00
Vitor PamplonaandClaude Opus 4.8 270d229d0a build: drive all module versions from the catalog
Make every module read its release version from gradle/libs.versions.toml
instead of carrying its own literal, so a release bump is a single-file edit.

- Move the Android versionCode out of amethyst/build.gradle.kts into the
  catalog as `appCode`; amethyst reads it via libs.versions.appCode.get().
- quartz publishes with version = libs.versions.app.get().
- geode generates a BuildConfig.VERSION from the catalog (new
  generateVersionFile task) and RelayInfo.VERSION reads it, so the NIP-11
  software version tracks releases.
- Update the root build comment to point at the appCode entry.

Version-neutral: everything still resolves to the current 1.12.0 / 448.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 11:19:52 -04:00
Vitor Pamplona af27b7750e Fixes versions in the Skill file 2026-06-17 10:34:00 -04:00
Vitor PamplonaandGitHub 06cba4e1db Merge pull request #3244 from vitorpamplona/claude/lucid-turing-atp9z3
Support measurement system detection on Android API <28
2026-06-17 10:30:50 -04:00
David KasparandGitHub 61706c167a Merge pull request #3243 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-06-17 16:13:56 +02:00
Crowdin Bot b608e82cde New Crowdin translations by GitHub Action 2026-06-17 14:12:23 +00:00
Claude ef3c11da15 fix: guard ICU getMeasurementSystem behind API 28 check
LocaleData.getMeasurementSystem requires API level 28 but the module's
minSdk is 26, which lint (NewApi) flagged as an error. Guard the ICU call
behind a Build.VERSION.SDK_INT check and fall back to a country-code based
heuristic on API 26-27.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JDcx7i1VDRyT7rDcuSB5Co
2026-06-17 14:09:38 +00:00
davotoula 92ffc76bb0 update cz, se, pt, de 2026-06-17 16:03:47 +02:00
Vitor PamplonaandGitHub 940458d767 Merge pull request #3242 from vitorpamplona/claude/gallant-bardeen-78e9n1
Add Health Connect integration for workout suggestions
2026-06-17 09:15:24 -04:00
Claude c0df0048bd feat: render workout notes through the kind-1 text pipeline
Replace the plain-text note with TranslatableRichTextViewer (inside
SensitivityWarning), the same renderer kind-1 notes use — so workout notes get
links, mentions, hashtags, embeds, image/URL previews and inline translations.
WorkoutDisplay now receives backgroundColor, canPreview, quotesLeft,
accountViewModel and nav; both call sites (NoteCompose, ThreadFeedView) pass
them through.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-17 12:29:51 +00:00
Vitor PamplonaandGitHub 9ca1a14145 Merge pull request #3240 from davotoula/fix/immersive-system-bars
Hide Android system bars in immersive scroll and full-screen media
2026-06-17 08:22:44 -04:00
Vitor PamplonaandGitHub 1e8fdfdf2e Merge pull request #3241 from davotoula/l10n/swahili-base
Promote Kenyan Swahili to the base resource qualifier
2026-06-17 08:21:57 -04:00
davotoula 0a7b8300df fix(l10n): promote Kenyan Swahili to the base resource qualifier 2026-06-17 13:13:04 +02:00
David KasparandGitHub 77ff7945d6 Merge pull request #3237 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-06-17 13:07:26 +02:00
davotoula f920ab0c6f Code review:
- Hoist the constant systemBarsBehavior assignment out of the snapshotFlow collect loop
- Track the top in-app bar's collapse fraction (falling back to the bottom bar on screens with no top bar) instead of max(top, bottom)
2026-06-17 11:40:43 +02:00
davotoula 67764300a8 feat: hide OS system bars during full-screen media
Install a DisposableEffect in ZoomableImageDialog that hides both status
and navigation bars via WindowInsetsControllerCompat when the full-screen
media dialog opens, and restores them on dispose.
2026-06-17 11:24:50 +02:00
davotoula 04d7316e34 feat: hide OS status bar during scroll immersive mode 2026-06-17 11:24:17 +02:00
Claude 5179757019 feat: render workout notes and show metrics in the viewer's units
- Render the kind 1301 content (the user's notes) below the stats grid in
  WorkoutDisplay; previously it was published but never shown.
- Convert distance, pace, speed, elevation, and weight to the viewer's preferred
  measurement system (phonePrefersMiles) via WorkoutInfo.inUnits, so a workout
  stored in km shows in miles for an imperial viewer (and vice versa).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-17 03:15:13 +00:00
Claude d8c66a4a25 feat: default workout distance unit to the phone's measurement system
Read the phone's measurement preference (Android 14+ Regional preferences
override via the locale's -u-ms- extension, else ICU's locale-derived
US/UK = miles) and default the New Workout composer's km/mi selector to it.
Health Connect distances (always metres) are converted into the chosen unit on
pre-fill instead of being forced to km.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-17 03:08:43 +00:00
Claude d4c7577828 feat: record the source app/device name from Health Connect
Instead of a generic "health_connect" source, capture the app/device that wrote
the record (the Health Connect data origin) and use it as the workout source —
so the card shows e.g. "SAMSUNG HEALTH" / "GOOGLE FIT" instead of "HEALTH
CONNECT".

resolveSourceName resolves the data-origin package to the installed app's
label, falling back to a known-package map (Samsung Health, Google Fit, Fitbit,
Garmin, Strava, Nike Run Club), then the raw package, then "Health Connect".
DetectedWorkout carries this as `source` and it flows into the published kind
1301 source tag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-17 02:40:00 +00:00
Claude 89ab47c0ec fix: tighten New Workout composer vertical spacing
Reduce the section spacing from 16dp to 12dp so the gaps around the activity
chip rows aren't oversized.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-17 01:46:14 +00:00
Claude 631c110a4f fix: center the activity type chips in the New Workout composer
The activity FilterChip FlowRow was start-aligned; center it under the hero
with Arrangement.spacedBy(.., Alignment.CenterHorizontally).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-17 01:36:41 +00:00
Claude ee1a7aec2f feat: move Health Connect entirely into the New Workout composer
Per request, the Workouts feed no longer has any Health Connect code. Both the
permission prompt and the detected-workout list now live in the New Workout
carousel: it shows a Connect card when permission is missing and the workout
list once granted.

- Remove WorkoutConnectBanner and the feed wiring; revert the FeedLoaded header
  slot that fed it.
- Restore the Connect prompt in DetectedWorkoutCarousel (tri-state permission:
  unknown renders nothing, denied shows Connect, granted shows the list).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-17 01:15:51 +00:00
Crowdin Bot e5353703e3 New Crowdin translations by GitHub Action 2026-06-17 01:10:46 +00:00
Vitor PamplonaandGitHub ae3883052b Merge pull request #3235 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-06-16 21:09:00 -04:00
Claude 63aa4102d0 feat: keep only the connect prompt on the feed; workouts list in New Workout
Refine the previous change: the Workouts feed shows a connect-only Health
Connect banner (a new WorkoutConnectBanner) so permission can be granted there,
but never lists detected workouts. The detected-workout list is shown solely in
the New Workout composer's carousel, which reverts to list-only (no connect
card).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-17 01:03:54 +00:00
Claude a60d2b6c1c feat: drop the Health Connect feed banner; keep it in New Workout only
Per request, Health Connect suggestions no longer appear on the Workouts feed.
The integration now lives solely in the New Workout composer's carousel.

- Remove the feed banner wiring from WorkoutsScreen and revert the FeedLoaded
  header slot that fed it.
- Delete the banner (WorkoutSuggestionCard), its state holder
  (WorkoutSuggestionState), and the handled-id store (HealthConnectStore);
  the carousel shows the full window and needs no dedup. LOOKBACK_DAYS moves to
  HealthConnectManager.
- Fold the Connect prompt into the carousel so Health Connect permission can
  still be granted (the banner was the previous entry point) — it shows a
  Connect card when permission is missing, the carousel once granted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-17 00:52:36 +00:00
Claude 125947bf85 feat: modernize the New Workout composer
Give the workout composer more energy and a clearer hierarchy:

- Hero header: a large activity badge (primaryContainer circle) plus the
  activity name, both crossfading as the type changes — the form's focal point.
- Activity selector chips now carry their per-activity icon.
- Distance unit switched from two FilterChips to a Material3 segmented button.
- Leading icons on the inputs (title, distance, calories, notes) and an
  icon + label section header for Duration, for faster visual scanning.
- Roomier 16dp spacing and padding.

Pure presentation; the ViewModel and published event are unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-17 00:34:37 +00:00
Claude ad1d28d0a6 chore: remove Health Connect workout detection plan doc
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-17 00:21:48 +00:00
Claude 692583cac2 feat: limit the feed workout banner to today's workouts
The Workouts feed banner now only surfaces workouts that started today; older
workouts from the past week remain reachable from the New Workout composer's
carousel. Keeps the feed focused on what just happened while still letting
people post earlier workouts on demand.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-16 23:52:09 +00:00
Crowdin Bot 66b0428cb2 New Crowdin translations by GitHub Action 2026-06-16 23:48:41 +00:00
Vitor PamplonaandGitHub fc35f5afe3 Merge pull request #3236 from vitorpamplona/claude/fervent-pascal-dy03ve
Add share-as-image feature for notes
2026-06-16 19:46:32 -04:00
Claude 397cf7d302 feat: add a recent-workouts carousel to the New Workout composer
Show a horizontal list of workouts found in Health Connect over the last 7 days
at the top of the New Workout screen; tapping one pre-loads the form (activity,
duration, distance, calories, heart rate, steps, elevation, start time).

Unlike the feed banner, the carousel shows every workout in the window (not
filtered by what was already shared/dismissed) and re-reads on resume. The
DetectedWorkout→Route mapping and duration/relative-time formatting are
extracted to a shared file so the banner and carousel stay in sync, and the
ViewModel gains applyPrefill() to overwrite fields on tap (vs the once-only
prefill used for the nav argument).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-16 23:46:11 +00:00
Claude 7bad2a09b3 fix: use the Add icon on the New Workout FAB for consistency
The workout FAB used the directions_run glyph, which is visually off-center
inside the circular button and looked misaligned next to the app's other
"create" FABs. Use MaterialSymbols.Add (the + the other new-item FABs use) so it
matches; the button's size/shape/color were already identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-16 23:46:09 +00:00
Claude 7bed12b508 chore: add diagnostic logging to Health Connect workout reads
HealthConnectManager only logged on failure, so a successful-but-empty scan was
invisible. Add info logs (tag HealthConnectManager) for the permission check
result, the number of exercise sessions read in the window, and per session the
activity type with its mapping (or the skip reason + data origin). Makes it
possible to see why a watch workout does or doesn't surface as a suggestion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-16 23:34:43 +00:00
Claude a9cb2f464b fix: stop the connect card from flashing on every screen open
hasPermission started false while the async permission check ran, so the
"Share your workouts" connect card flashed for ~1s on every open before the
check confirmed permission was already granted. Make permission a tri-state
(null = not checked yet) and render nothing until it resolves, so the connect
prompt only appears once we actually know permission is missing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-16 23:29:12 +00:00
Claude fdf7682409 feat: re-scan Health Connect when the app resumes
The suggestion only scanned once when the Workouts screen first opened, so a
workout that synced afterwards (e.g. a watch workout reaching Health Connect
while Amethyst was backgrounded) never appeared until a cold restart. Replace
the one-shot LaunchedEffect with a LifecycleResumeEffect so we scan on entry and
again every time the app returns to the foreground.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-16 23:23:43 +00:00
Claude 0e1233f760 fix: keep Health Connect prefs off the main thread (StrictMode)
HealthConnectStore opened SharedPreferences in its constructor, which ran during
composition on the main thread and tripped a StrictMode DiskReadViolation
(~265ms). Make the prefs lazy so construction touches no disk, and move the
read (refresh) and write (handle) onto Dispatchers.IO via the state holder's
scope.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-16 23:17:25 +00:00
Claude 723ba29006 feat: scroll the workout suggestion banner with the feed
Move the Health Connect suggestion banner from a pinned position above the feed
into the feed's LazyColumn as the first item, so it scrolls away with the list.
Adds an optional `header` slot to the shared FeedLoaded (default none, so other
feeds are unaffected) and passes the banner through WorkoutsScreen's onLoaded.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-16 23:14:20 +00:00
Claude b0bcadc6b3 fix: add Android 14+ Health Connect permission rationale activity-alias
The Connect button did nothing on Android 14+ because the permission request
fails silently without a ViewPermissionUsageActivity declaration. We only had
the pre-14 ACTION_SHOW_PERMISSIONS_RATIONALE intent-filter; add the required
activity-alias handling VIEW_PERMISSION_USAGE + HEALTH_PERMISSIONS, guarded by
START_VIEW_PERMISSION_USAGE, routed into MainActivity, so Health Connect shows
the permission dialog.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-16 23:11:35 +00:00
Claude 03e8f86a83 fix: align Health Connect import with RUNSTR (active calories + title)
Reviewed RUNSTR's healthConnectService.ts and workoutPublishingService.ts and
matched two details so our imported kind 1301 events line up with theirs:

- Calories: RUNSTR reads ActiveCaloriesBurned; we were using TotalCaloriesBurned
  (active + basal), which over-reports. Now prefer active calories and fall back
  to total only when a source records no active energy. Adds the
  READ_ACTIVE_CALORIES_BURNED permission.
- Title: RUNSTR always emits a title, generated from the activity when none
  exists. Default the pre-filled title to the activity name so every shared
  event carries one.

The 1301 wire format already matched (distance [value,unit], HH:MM:SS duration,
lowercase exercise, plain-text content). We remain a richer superset — we also
publish avg/max heart rate and elevation_gain, which RUNSTR reads but does not
publish — and RUNSTR's lax parser ingests these without issue. Parity notes
recorded in the plan doc.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-16 23:07:42 +00:00
Claude fa2dd03be9 feat: polish share-as-image screen
- Re-layout the screen as a centered "hero" preview on a subtle gradient
  backdrop, with a soft drop shadow, instead of a top-aligned full-width
  bitmap above a settings row.
- Move the primary action into a bottom bar: a clearer server picker plus
  a full-width "Share" button with an icon. Top bar is now a plain back bar.
- Brand the captured card with a divider + Amethyst logo watermark and
  roomier padding.
- Capture in two passes (quick first preview, then a refined snapshot after
  a short settle delay) so async media has time to load into the image
  instead of showing empty placeholders.
- Show real upload progress (stage + percentage) via the shared
  UploadProgressIndicator instead of a bare spinner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y5ipS9wu6ffCPk5rtB8k3x
2026-06-16 23:02:33 +00:00
Claude 65d7eab85c feat: polish the Health Connect workout suggestion banner
Reworks the suggestion UI from a plain overlay into a designed, in-scroll card:

- Inline placement: the banner now sits above the feed (pushing it down) inside
  a Column rather than overlaying and covering the first feed items. The feed's
  scaffold top padding is applied to the Column and stripped from the feed to
  avoid doubling; the bottom padding (and the disappearing-bar animation) are
  preserved.
- Per-activity icon (run/ride/swim/hike/strength/yoga…) in a circular tinted
  badge, reusing the existing ExerciseType.symbol() mapping — no new glyphs.
- Metric chips (duration, distance, heart rate, calories, steps) in place of the
  single truncated summary line.
- Title shows the workout name or activity, subtitle shows a relative time;
  filled-tonal Share + text Dismiss actions; rounded 16dp cards.
- animateContentSize so dismissing a suggestion collapses smoothly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-16 22:51:48 +00:00
Claude 1c5c6d8a6b feat: add Compose setting to disable Health Connect workout suggestions
Adds a "Suggest workouts to share" toggle under Compose Settings (defaults on,
preserving current behavior) wired through UiSettings / UiSettingsFlow /
UiSharedPreferences. When set to NEVER, the Workouts screen no longer scans
Health Connect or shows the connect/suggestion banner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-16 22:33:05 +00:00
Vitor PamplonaandClaude Opus 4.8 230c247909 fix(desktop): consistent macOS .icns + multi-size Windows .ico
The macOS icon.icns shipped inconsistent artwork across its embedded
sizes — a transparent full-bleed glyph at 256/512 (shown on the DMG mount
window and Spotlight) but a white-carded glyph at 128 (shown in the Dock).
It was also missing every @2x Retina tier and its 16/32/48 entries decoded
to corrupt noise, a signature of a generic PNG->ICNS converter rather than
iconutil. Regenerate from a single transparent glyph master into a proper
iconset (all standard sizes + @2x) compositing one consistent rounded-card
look at every size, then assemble with iconutil.

The Windows icon.ico held a single 32x32 BMP, so Windows upscaled a blurry
32px everywhere it needed a larger icon. Rebuild as a multi-size .ico
(16/32/48/64/128/256, PNG-encoded) from the same glyph, full-bleed and
transparent per Windows convention (matches the Linux icon.png).

Linux icon.png is unchanged — a single transparent PNG never had the
inconsistency and Linux desktops expect transparent icons.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 18:29:03 -04:00
Claude 6de43dec2d feat: detect workouts from Health Connect and suggest a kind 1301 post
Adds foreground auto-detection of finished workouts on Android via Google
Health Connect — the single aggregator every Android health source funnels
into (Samsung Health/Galaxy Watch, Google Fit, Fitbit, Garmin, Strava), the
same Android path RUNSTR uses. On opening the Workouts screen, Amethyst scans
Health Connect for sessions the user hasn't handled and surfaces a banner that
opens the existing workout composer pre-filled, ready to publish as a NIP-101e
WorkoutRecordEvent (kind 1301).

- HealthConnectManager reads ExerciseSessionRecord + aggregated distance,
  calories, heart rate, steps and elevation, mapping each to DetectedWorkout.
- ExerciseTypeMapper maps Health Connect activity types to NIP-101e verbs.
- HealthConnectStore remembers handled sessions per account so each is
  offered once; 7-day foreground lookback, no background service.
- WorkoutSuggestions banner: connect prompt (on-demand permission request,
  never on cold start) or detected-workout rows on the Workouts screen.
- Route.NewWorkout carries optional pre-fill; NewWorkoutViewModel publishes the
  richer metrics (heart rate, steps, elevation, start time) with
  source=health_connect (new SourceTag constant in quartz).
- androidx.health.connect:connect-client (Apache-2.0) + read-only health
  permissions and the required privacy-rationale manifest entries.

Design notes in amethyst/plans/2026-06-16-health-connect-workout-detection.md;
background ~15-min polling + notification left as a documented future seam.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
2026-06-16 22:19:58 +00:00
Vitor PamplonaandClaude Opus 4.8 3a21df7775 fix(desktop): exclude leaked kotlinx-coroutines-test from release dmg
composemediaplayer 0.10.0 publishes kotlinx-coroutines-test as a runtime
dependency in its POM. That jar ships a META-INF/services registration for
kotlinx.coroutines.CoroutineExceptionHandler -> ExceptionCollectorAsService.
The release-only ProGuard pass strips the unreferenced provider class but
keeps the services manifest, so the packaged dmg crashed at startup with a
ServiceConfigurationError the first time the coroutine exception handler
loaded (DesktopHttpClient.<init>). Dev runs were unaffected since they don't
run ProGuard.

Exclude the test-only artifact so it never lands on the production classpath.
Verified: rebuilt release dmg no longer bundles the jar and launches clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 17:52:48 -04:00