Export and share the recorded ride GPX (issue #47) #112
No reviewers
Labels
No labels
area:companion
area:docs
area:shared
area:tooling
area:watchapp
blocker
kind:chore
kind:feature
kind:spike
kind:test
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
robert/PedalPebble!112
Loading…
Reference in a new issue
No description provided.
Delete branch "area/gpx-export"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #47.
Closes the loop #20 opened: a finished
GpxRecorder/GpxWriterrecording can now be shared straight to komoot/Strava/anywhere via the Android share sheet, named sensibly, and found again later from a ride-history list.Filename (
rideExportFilename,:companion:core, pure)PedalPebble-<date>-<time>-<route>.gpx— route name sanitised (Windows-reserved characters stripped, whitespace collapsed to-, capped at 60 chars) and omitted entirely when absent or unusable after sanitising. Seconds-resolution so two rides on the same route on the same day never collide.RideGpxFiles(:companion:ride) now names the on-disk file with this same function — no rename-for-export step is needed, sinceFileProviderreports a sharedUri's display name as the underlying file's real name.Sharing (
GpxExporter,:companion:ride)Intent.ACTION_SEND+FileProvider, matching currentdeveloper.android.com/reference/androidx/core/content/FileProviderguidance (verified 2026-09-05 against the live page, not recalled from memory):<provider>manifest entry,android:exported="false",android:grantUriPermissions="true"res/xml/file_paths.xml:<external-files-path name="gpx" path="gpx" />— the same subdirectoryRideGpxFilesalready writes to (getExternalFilesDir("gpx"))FileProvider.getUriForFile()+ explicitFLAG_GRANT_READ_URI_PERMISSIONVerified against the real merged manifest: the
${applicationId}placeholder resolves tode.butzei.pedalpebble.debug.fileproviderfor the debug variant, matching whatGpxExportermints itsUriagainst at runtime (context.packageName).Ride history — what existed vs. what this issue built
Checked before building anything: nothing in this codebase persisted "what did I record" before this issue — #20 built the writer, not an index of what it wrote. So this is real, new, deliberately minimal scope:
RideRecord/RideHistoryStorage/RideHistory(:companion:core, pure) — mirrorsRouteRecordStorage/RouteLibrary's storage/business-logic split exactly. No rename/favouriting/search — the issue only asks for "listed and re-exportable".RideHistoryDatabase(Room) /RoomRideHistoryStorage/RideHistoryStore(:companion:ride) — this module's first Room database, its own table, deliberately not folded into:companion:route'sRouteDatabase(different domain, wrong dependency direction).RideHistoryScreen/RideHistoryActivity(:companion:ride) — this module's first Compose UI, same shape as:companion:route'sRouteLibraryScreen: a list, newest first, share + delete per row.RideServicenow records oneRideRecord(file, started-at, active route name, moving distance) when a recording finishes, on a fire-and-forgetDispatchers.IOcoroutine — documented honestly in its own KDoc as a best-effort write that could in principle lose a race against process teardown, not silently assumed reliable.Heart rate / elevation
Already correctly optional end-to-end per #20's
GpxWriter/GpxTrackPoint— unchanged by this issue.GpxRecorderTest's existing full round trip (write →GpxImporterre-import, asserting geometry/elevation/HR survive exactly) already covers this and is still green; not duplicated here.komoot / Strava verification
Cannot be done from this sandbox — no komoot/Strava account, no real device. The closest available proxy — a real round trip through this project's own
GpxImporter— already exists from #20 (GpxRecorderTest, "a full recording ... round-trips through GpxImporter") and passes. Real komoot/Strava verification needs Robert on a real device.Testing
./gradlew :companion:core:test— 257 tests, 0 failures (14 new: filename generation incl. sanitisation/truncation/collision cases, history list/delete ordering, timestamp display formatting in two zones/locales)../gradlew :companion:assembleDebug— real build succeeds: Room/KSP codegen for the newRideHistoryDatabaseruns, Compose compiles (RideHistoryScreen/RideHistoryActivityboth need and have@OptIn(ExperimentalMaterial3Api::class)), and the merged manifest'sFileProviderentry was inspected directly (see above).RouteDatabase's own pre-existing honesty note from #25.RoomRideHistoryStorage's mapping functions have no Room API surface of their own but are, likewise, not independently unit-tested here (same gapRoomRouteRecordStoragealready had).https://claude.ai/code/session_01DAoXbRmJUf2uxNYBfdAXPt