activity_files 0.7.2 copy "activity_files: ^0.7.2" to clipboard
activity_files: ^0.7.2 copied to clipboard

A pure Dart toolkit for parsing, editing, validating, and converting workout activity files in GPX, TCX, FIT, GeoJSON, and CSV formats.

Changelog #

0.7.2 #

Fixed #

  • ActivityFiles.channelSamplesFrom() emitted second-resolution timestamps instead of milliseconds, which didn't match what builderFromStreams()/convertAndExport() expect — round-tripping through it silently produced dates near 1970 instead of the correct time. If you built on this in 0.7.0/0.7.1, re-check any stored output.
  • convertAndExport(location: ..., autoFix: ...) silently ignored autoFix when building from raw streams instead of a source file.

0.7.1 #

Fixed #

  • No changes to the package itself
  • GitHub workflow fix (fixes automated publishing to pub.dev)

0.7.0 #

Breaking #

  • FIT parsing is now remapped to the official FIT profile. Since 0.6.0, session/lap stats and the sport value were parsed incorrectly
    • Migration: re-parse any FIT summaries stored with ≤ 0.6.x.
  • FIT encoding fixed accordingly: lap distance and sport values are now written correctly. FIT files exported with ≤ 0.6.x carry these defects; re-export them from source.
  • Multi-track GPX is now preserved instead of merged: the first <trk> becomes the primary RawActivity; the rest are stored in RawActivity.additionalTracks. Single-track encoders (TCX/FIT/CSV/GeoJSON) merge them automatically via the new RawActivity.flattened(), so conversions keep every point. But activity.points after loading a multi-track GPX now contains only the first track.
  • Default normalization now repairs sentinel values: RawEditor.trimInvalid() (run by convert/export when normalize: true, the default) removes Null Island points (lat/lon ≈ 0) and clears elevations ≤ −499 m while keeping the point. Repairs are reported via RawEditor.repairDiagnostics and in conversion/export diagnostics (repaired.sentinel_coords_removed, repaired.sentinel_elevation_cleared). Pass normalize: false to opt out.
  • ValidationResult.isValid / LapValidationResult.isValid now derive from the structured diagnostics list as well as the legacy errors list.

Added #

Parsers and encoders now round-trip everything a format can hold; anything the target format can't represent is reported via a lossy.* diagnostic (lossy.sets_dropped, lossy.events_dropped, lossy.lengths_dropped, lossy.sessions_dropped, lossy.summary_dropped, lossy.laps_dropped, lossy.multi_track_flattened) instead of being dropped silently. Per format:

  • FIT import: multi-session files (e.g. triathlons), timer events, per-length swim data, and unknown record/session/lap fields (extraFitFields/extraFitArrays) are all preserved; developer fields decode using their declared name, type, scale, and offset.
  • FIT export: lossless FIT→FIT round-trips: session/lap stats, sets, events, swim lengths, device metadata, and custom channels (including unmapped ones as named developer fields) all write back.
  • GPX: waypoints and routes are preserved as structured data (gpxWaypoints, gpxRoutes) instead of being folded into the track; multi-segment tracks and unknown extensions round-trip.
  • TCX: power (ns3:TPX Watts/Speed/RunCadence) now parses as channels instead of being dropped; notes, author, and lap intensity/trigger fields round-trip.
  • GeoJSON: elevation, per-point timestamps, and feature properties all round-trip; polygons parse as tracks.
  • CSV: unknown numeric columns round-trip as custom channels.

New data model and API:

  • Swim metrics: ActivitySummary.poolLengthMeters/.numActiveLengths/.swimStroke/.avgStrokeCount, Lap.numActiveLengths/.swimStroke, the SwimStroke enum, plus ActivitySummary.subSport and .totalCycles.
  • Strength-training sets: FIT set messages parse into RawActivity.sets as WorkoutSet (type, repetitions, weight, exercise category; raw id kept for round-trips).
  • Point-level editing on RawEditor: insertPoint, deletePointAt, updatePoint, deleteRange, insertPause, removePause, all with lap/set boundary clipping; shiftTime now also shifts sets.
  • Batch import: ActivityFiles.loadBatch(sources) with per-file error capture, onProgress, and stopOnError.
  • Structured validation diagnostics: ValidationDiagnostic (stable code, suggestedFix, priority) on every validation result; ParseDiagnostic gains suggestedFix/priority; new validateDeviceMetadata() and validateChannels() validators; DiagnosticCategory documents the code prefixes.
  • Smaller additions: ActivityFiles.channelSamplesFrom() (channels → export-ready stream samples), Lap.copyWithoutSport(), ValidationResult.fromDiagnostics()/LapValidationResult.fromDiagnostics(), and the ParseFidelityMode enum (declared for 0.8.0; this release always uses pragmaticNormalize).

Changed #

  • GPX diagnostics gpx.wpt.invalid_timestamp/gpx.rtept.invalid_timestamp now say the point is kept with an epoch fallback time (the previous "ignored" wording contradicted the behaviour; codes unchanged).

Fixed #

  • FIT device metadata was read from the wrong fields: device_info was shifted by one (the serial number as the manufacturer, the hardware revision as the software version), and metadata from paired sensors overwrote the recording device's (a connected speed sensor could replace the head unit's name). product_name is now captured as ActivityDeviceMetadata.model.
  • Modern Garmin files no longer collapse to a handful of points: message definitions with more than 96 fields (routine on current devices) were rejected, and multi-byte array fields misaligned every following field in the message. Together, these could reduce a multi-hour ride to a few points and zero laps.
  • Real-device laps were silently dropped: the fallback record heuristic (0.6.0) misclassified normal lap messages as GPS records.
  • Valid legacy FIT files were flagged as corrupt: the trailer CRC must be computed over header + data, not data alone, and a header CRC of 0x0000 means "not computed" and is no longer compared. 12-byte-header files (including the official FIT SDK sample) and Edge 810-era files now validate cleanly, while genuinely corrupt files are still detected.
  • GPX temperatures are now written as decimals: atemp/wtemp were rounded to whole numbers although the schema types them as decimal (18.5 °C came back as 19).

0.6.0 #

Breaking #

  • ActivityFileFormat now includes csv and geojson; exhaustive switch statements must handle the new enum values.
  • Facade conversion/export helpers now validate by default (runValidation: true). Set runValidation: false to keep previous behavior.

Added #

  • FIT regression suite for problematic real-world files.
  • Additional integration coverage for CSV/GeoJSON conversion and format detection.
  • New pipeline option models: FitCorruptionHandling and ActivityAutoFixOptions.
  • FIT Developer-Fields Support: decodes developer fields and exposes them as channels. (lib/src/parse/fit_parser.dart)
  • Extended FIT Message Coverage: handlers for messages 23, 34, 49. (lib/src/parse/fit_parser.dart)
  • Typed FIT Views: asFitView() typed accessors for session/lap/record data. (lib/src/fit/typed_views.dart)
  • Auto-Lap Heuristics: autoLapByDistance option generates laps by distance with sport defaults. (lib/src/api/pipeline_options.dart, lib/src/api/activity_files_facade.dart)

Changed #

  • CI workflows were consolidated and hardened
  • CSV/GeoJSON are now first-class in the unified ActivityFileFormat path (detectFormat, load, convert, export, parser/encoder routing, CLI options).
  • Shared lap-boundary validation logic is reused between validateRawActivity() and RawEditor.validateLapBoundaries().
  • GPX parser: more robust TrackPoint parsing and TrackPointExtension handling

Fixed #

  • Auto-Lap: regenerate placeholder laps and recompute distance before marking. (lib/src/api/activity_files_facade.dart)
  • Autofix diagnostic autofix.laps.auto_generated is emitted when laps are generated. (lib/src/api/activity_files_facade.dart)
  • FIT parser now performs best-effort extraction for problematic FIT variants by applying in-stream local-definition updates and bounded timestamp recovery (fit.record.recovered_timestamp) instead of returning empty output.
  • GeoJSON parser now uses a deterministic UTC-epoch fallback timestamp for points without timestamp instead of runtime-dependent DateTime.now().
  • TCX parser cache lifecycle is now scoped with weak-key caching to avoid retaining parsed XML documents.
  • ActivityFiles.splitBySport() now preserves lap metadata fields (e.g. avg/max metrics, calories, FIT event fields) while removing per-lap sport in split outputs.
  • strictFitIntegrity now throws the same detailed troubleshooting FormatException across direct load and stream pipeline paths.

0.5.1 #

Changed #

  • Upgraded dependencies
  • Minor Fixes

0.5.0 #

Migration #

  • Upgrading? This version mitigates the 0.4.0 breaking changes: the 64MB payload limit is now configurable per-call via maxPayloadBytes and file paths work with allowFilePaths: true or by passing File objects.

Added #

  • CSV & GeoJSON Support: Full import/export for CSV and GeoJSON formats with roundtrip testing.
  • Public API Methods: Facade methods for CSV/GeoJSON: exportToCsv(), importFromCsv(), exportToGeojson(), exportToGeojsonPoints(), importFromGeojson().
  • Better Error Messages: All exceptions now include actionable recovery hints and troubleshooting steps.
  • Parser Performance: XML element caching and batch lookups reduce parsing time by 2-5x for large files (5000+ points).
  • Full Garmin TrackPointExtension v2 schema support: GPX parser and encoder now handle all v2 fields (wtemp, depth, speed, course, bearing) plus existing v1 fields (hr, cad, power, atemp) with corresponding channel types and ChannelSnapshot accessors.
  • Expanded FIT manufacturer database from 28 to 179 entries for comprehensive device identification.
  • Multi-sport activity support: TCX and FIT parsers handle triathlon files with multiple sport segments. New Lap.sport field enables per-lap sport tracking, and ActivityFiles.merge()/splitBySport() combine or divide multi-sport activities.
  • FIT session + lap stats are now parsed into ActivitySummary and additional Lap fields; extra record fields are surfaced as custom channels.
  • Added an artificial fixture generator (scripts/generate_artificial_fixtures.dart) that refreshes sample fixtures for tests and examples.
  • Lap boundary validation via RawEditor.validateLapBoundaries() for detecting timing mismatches.
  • Performance improvements in normalization and hot paths.
  • Parser optimization: through eliminating redundant XML traversals, removing case-insensitive string comparisons and caching child element lookups during trackpoint parsing. Large files (>5000 points) see the most benefit.

Changed #

  • CSV parser normalizes line endings (\r\n\n).
  • Optimized sport string conversion with caching.
  • GPX/TCX parsing now uses batch element lookups instead of repeated traversals.
  • Lap class includes optional sport field; null values inherit from activity-level sport.
  • TCX parser sport mapping extended to include swimming.
  • Test suite reorganized into focused categories with shared fixtures. See doc/testing.md for details.
  • Payload limit now configurable per-call via optional maxPayloadBytes parameter (default: 64MB).

Fixed #

  • FIT parser now fails gracefully (with diagnostics) instead of crashing when a data message references an unknown local definition (e.g. issue #2 user fixture). New diagnostics: fit.data.unknown_definition, fit.no_usable_data.

0.4.4 #

Added #

  • New Unit tests
  • New Integration tests
  • Synthetic test data generator with clean fixtures

0.4.3 #

Fixed #

  • Fix FIT parser to remove corrupted data points with invalid timestamps or coordinates, ensuring FIT→GPX and FIT→TCX output matches reference files

0.4.2 #

Fixed #

  • Fix FIT parser for non-standard message ordering. Thanks @hallr-boulder for reporting (fixes #2)
  • Improved support for swimming activity files with non-standard message ordering
  • Parser now gracefully handles unknown message types instead of crashing

0.4.1 #

Fixed #

  • Fix formatting and prevent it from happening again with a local git hook

0.4.0 #

Breaking #

  • Plain string sources are always treated as inline payloads; pass a File or set allowFilePaths: true on load/convert/convertAndExport/ ActivityExportRequest.fromSource to read from disk.
  • A 64MB cap (ActivityFiles.defaultMaxPayloadBytes) applies to load/convert/detect and streamed pipelines; oversized inputs throw or emit error diagnostics. Stream from disk/network or split files to go larger; only ActivityParser.parseStream(maxBytes: ...) lets you override the limit.

Added #

  • Stream-backed loads are replayable; ActivityLoadResult.bytesPayload exposes buffered bytes even when the source was a Stream<List<int>>.
  • FIT integrity: header/trailer CRCs and truncation are reported as error diagnostics (or throw when strictFitIntegrity: true); the encoder now emits invalid coordinate sentinels for sensor-only activities.
  • Structural validation enforces lap ordering/overlap and warns when sensor channels extend past the point timeline during load/convert/export flows.
  • GPX/TCX version selection: EncoderOptions and CLI flags can emit GPX 1.0/1.1 and TCX v1/v2; defaults remain GPX 1.1/TCX v2. FIT remains core-workout only.
  • GPX 1.0 round-trips root metadata, track extensions, and labels when gpxVersion is GpxVersion.v1_0.

Changed #

  • RawTransforms and RawEditor live in dedicated modules (transforms/raw_transforms.dart, transforms/raw_editor.dart); transforms.dart still exports both for existing imports.
  • ChannelMapper.cursor exposes reusable per-channel cursors; mapAt now wraps it so overlays reuse cached lookups.
  • GPX/TCX/FIT encoders reuse the cursor (TCX/FIT also use distance readings) to avoid repeated binary searches.
  • Resampling pre-sizes timetables and reuses sliding cursors to keep RawTransforms.resample/_resampleNearest fast on long recordings.
  • RawEditor skips work on sorted/valid inputs and reuses its timestamp cursor during downsampleTime.
  • FIT exports defer base64 decoding until asBytes() when FIT sources arrive as base64 strings.
  • RawEditor.smoothHR now uses a sliding window to stay O(n).

Fixed #

  • RawEditor.downsampleDistance keeps the final point even on short hops or duplicate timestamps, preserving distance/duration and channel alignment.
  • RawEditor.markLapsByDistance recovers from non-monotonic distance channels (e.g. pause resets) to keep splits accurate.
  • RawEditor.smoothHR respects even-numbered windows instead of averaging an extra sample.
  • Format detection inspects only a small prefix and honors payload caps.
  • Exports with normalize: false auto-sort/dedup when needed; recomputeDistanceAndSpeed also self-sorts to avoid invalid speed/distance.
  • RawEditor.sortAndDedup clones lists before sorting to keep prior RawActivity instances immutable.
  • Malformed GPX/TCX and invalid FIT binaries now return structured ParseDiagnostic errors instead of raw exceptions.
  • Stream parsing (parseStream, convertAndExportStream, runPipeline with streams) returns diagnostics for malformed/oversized payloads instead of throwing.
  • CLI convert honors explicit --encoding, reads GPX/TCX as bytes to avoid Latin-1 corruption, and exits non-zero on parser errors.
  • ActivityFiles.runPipeline no longer runs validation twice when runValidation is enabled.
  • RawActivity.copyWith keeps collections immutable, recognizes canonical inputs to avoid clones, and reuses cached distances.
  • RawTransforms.resample sorts points before resampling to avoid RangeErrors and keep start/end ordering.
  • ActivityFiles.convert enforces the export ordering guard when normalize is false; detectFormat no longer probes filesystem paths unless allowed.

0.3.2 #

Fixed #

  • wrong version in pubspec.yaml

0.3.1 #

Fixed #

  • ActivityFiles.load/convert now honor the encoding parameter for GPX/TCX byte payloads (without BOMs), so Latin-1 and other single-byte exports no longer throw FormatException.
  • ActivityParser.parseBytes exposes an encoding argument for callers that read non-UTF-8 text files directly into byte buffers.

0.3.0 #

Added #

  • ActivityFiles.export produces encoded payloads with optional validation, normalization, and aggregated diagnostics in a single helper.
  • ActivityFiles.convertAndExport exposes the same workflow directly from raw sources, including optional validation.
  • ActivityFiles.exportAsync and convertAndExportStream mirror the export workflow off the UI thread and for streamed payloads.
  • ActivityExportRequest and ActivityFiles.runPipeline let callers describe parse → normalize → export pipelines with consistent isolate controls.
  • Diagnostic summary getters (warningCount, hasErrors, diagnosticsSummary, etc.) are now available on load, conversion, and export results to simplify UI surfacing.
  • ActivityProcessingStats and NormalizationStats capture normalization and validation metrics alongside export diagnostics.
  • Facade convenience wrappers expose common transforms (sortAndDedup, trimInvalid, smoothHeartRate, crop), structural validation, and channel snapshots.
  • RawActivity and RawActivityBuilder gained device metadata and namespace aware GPX extension support, enabling richer encoder output without custom glue.
  • DiagnosticsFormatter provides reusable helpers for summarising ParseDiagnostic collections across logs and UI surfaces.
  • Stream-aware builder/export helpers (builderFromStreams, convertAndExport with location/channels) accept timestamp/value tuples so backends can export without manual model translation.
  • ActivityFiles.registerSportMapper supplies pluggable sport inference and ships string/ID heuristics for common wearable categories.
  • GPX encoders honour builder-supplied metadata/track names and expose helper factories (gpxActivityLabelNode, gpxDeviceSummaryNode) for custom extensions with automatic namespace registration.

Changed #

  • GPX encoder now emits device metadata and custom extensions, automatically declaring any additional namespaces used.
  • TCX encoder/parser preserve device metadata and custom extensions, and FIT files now round-trip device metadata, including explicit manufacturer/product identifiers supplied via ActivityDeviceMetadata.
  • ActivityFiles.convert and convertAndExport now accept exportInIsolate, bringing isolate offloading parity while ensuring FIT byte caches refresh when the encoded payload changes.

Fixed #

  • RawEditor.markLapsByDistance now reports per-split distances correctly and always emits a trailing partial lap when applicable, ensuring summary totals stay accurate.
  • RawEditor.downsampleTime keeps the final point in the activity and performs sample matching without quadratic scans, avoiding data loss on closely spaced tracks.
  • Channel deduplication retains the most recent sample when multiple readings share the same timestamp instead of discarding later values.
  • ActivityFiles.channelSnapshot now uses binary search when resolving channel samples, reducing lookup cost from O(n) to O(log n) for large time-series streams.

0.2.0 #

Added #

  • Asynchronous parsing surface: ActivityParser.parseAsync, parseBytesAsync, and parseStream optionally offload work to isolates for smoother UIs and streaming IO.
  • ActivityFiles facade providing ergonomic load, convert, and edit helpers tailored for app integrations.
  • RawActivityBuilder for incremental creation of activities.
  • Asset-backed integration tests cover ActivityFiles.load, detectFormat, and conversion flows with real GPX/TCX/FIT fixtures.

Changed #

  • GPX, TCX, and FIT parsers emit structured diagnostics instead of raw strings.
  • Converter, CLI, documentation, examples, and tests now surface diagnostics in output flows.
  • README/example now highlight the high-level facade and builder workflows.
  • Added facade-focused regression tests covering format detection and builder seeding.

Deprecated #

  • ActivityParseResult.warnings remains available but now forwards to the new structured diagnostics; it is marked deprecated to encourage migration.
  • ActivityConverter.convert still accepts the warnings parameter, which is deprecated in favor of the richer diagnostics sink.

0.1.2 #

Fixed #

  • pub.dev score fix.

0.1.1 #

Fixed #

  • Fix README.

0.1.0 #

Added #

  • Handle FIT compressed timestamp headers and ensure unknown message types advance the reader instead of hanging.
  • Add ActivityParser.parseBytes, broaden ActivityConverter.convert input support, and let the CLI operate on raw FIT binaries without manual base64.
  • Document the new FIT workflow and add regression coverage for compressed headers.

0.0.2 #

Added #

  • Add example/main.dart illustrating a minimal GPX round-trip.

Changed #

  • Upgrade dependencies and SDK.

0.0.1 #

Added #

  • Initial release of activity_files with GPX/TCX parsing, editing, validation, and encoding utilities plus a conversion/validation CLI scaffold.
4
likes
160
points
3.22k
downloads

Documentation

API reference

Publisher

verified publishereikedreier.xyz

Weekly Downloads

A pure Dart toolkit for parsing, editing, validating, and converting workout activity files in GPX, TCX, FIT, GeoJSON, and CSV formats.

Repository (GitHub)
View/report issues

Topics

#health #fitness #workout #converter #sports

License

BSD-3-Clause (license)

Dependencies

args, async, collection, csv, xml

More

Packages that depend on activity_files