Record and manage rides, runs and hikes on an offline map, with no internet permission at all. Tracks are standard GPX files, and maps are optional Mapsforge files you import yourself. Runs on Android 12 or later.
- Record tracks in the background with pausing, waypoints and live charts
- Recordings survive most crashes
- Speed and elevation charts by distance or time, with zoom
- Import and export
.gpxtracks compatible with other apps - Trim, search, sort and categorize tracks
- Show, hide, export or delete many tracks at once
- Maps are optional; without one, tracks draw on a blank background
- All maps share one canvas and don't need to be adjacent
- Anything requiring internet access
- External device integration
- Map routing or searching
Is internet-based data collection possible?
Not through the app itself, which lacks theINTERNET permission and fails to build with it. Android's cloud backup is disabled and only device-to-device transfers are allowed, though some backup apps, such as Seedvault, can use the device-to-device path and store your data wherever they're set to. However, recording needs system location on, so there is no guarantee that Android itself or other apps don't collect that data.
Was AI used?
Yes, a large portion of this codebase was written by Claude. However, a significant amount of time and human effort was put into polishing the user interface and ensuring the app stays lightweight with as few permissions as possible.Needs the Android SDK (sdk.dir in local.properties, or ANDROID_HOME) and any JDK 17+
to start Gradle, which then builds with an installed JDK 21 or fetches one.
./gradlew :app:assembleDebug # app/build/outputs/apk/debug/app-debug.apk
./gradlew :app:assembleRelease # app/build/outputs/apk/release/app-release-unsigned.apkThe release build is minified but unsigned; sign it with your own key.
The build fails if any dependency adds a network permission to the merged manifest
(CheckNoNetworkPermissions in app/build.gradle.kts).
./gradlew test # JVM unit tests for :core and :app, no device needed
./gradlew coverageVerification # coverage report in app/build/reports/coverage, with per-file minimums
./gradlew ktlintCheck # style per .editorconfig; ktlintFormat fixes most of it
./gradlew check # all of the above, plus Android lintInstrumented tests in app/src/androidTest need a connected device or emulator:
./gradlew :app:connectedDebugAndroidTestDependencies are checked against gradle/verification-metadata.xml. After changing one, refresh it
with a device or emulator connected:
./gradlew --write-verification-metadata sha256 check :app:assembleRelease :app:connectedDebugAndroidTest-
Bump
versionCodeandversionNameinapp/build.gradle.kts, write the notes tofastlane/metadata/android/en-US/changelogs/<versionCode>.txt, and runcheckand the instrumented tests. -
Commit, tag it
v<versionName>, and check out the tag. The build embeds the commit hash, so build only from the tagged commit. -
Build clean and sign. F-Droid rebuilds each release with Debian's OpenJDK 21 and ships it only if it matches the signed APK on GitHub, which a stale baseline profile from an incremental build would break.
./gradlew clean :app:assembleRelease --no-build-cache apksigner sign --ks release.jks --alignment-preserved --v1-signing-enabled false --out offline-gpx-<version>.apk app/build/outputs/apk/release/app-release-unsigned.apk
-
Push the commit and tag, and attach the signed APK to the tag's GitHub release.
| Path | Contents |
|---|---|
core/ |
JVM-only Gradle module, so no Android. model: Track, TrackPoints columns, TrackPoint for single points, GeoBounds. analysis: FixFilter, SpeedWindow, TrackAnalyzer → TrackProfile. |
app/…/data/ |
gpx: streaming parser, writer and relabeller. db: Room. map: MapStore, TileIndex, and the VTM tile source and theme in MapTiles. record: LocationSource, RecordingWal, RecordingService/Controller/Recovery. settings. track: TrackRepository, TrackCache. |
app/…/ui/ |
map: MapScreen, VTM canvas, layers, generated render theme. track: the sheet. chart: hand-rolled Canvas charts. library, record, settings, format, theme, nav. |
app/…/di/ |
AppContainer: manual wiring, no Hilt. |
app/src/main/java/org/oscim/ |
MapFileHeaders, in VTM's package to reach its package-private header reader. |
app/schemas/ |
Room's exported schemas, used by MigrationTest. |
fastlane/ |
F-Droid listing and screenshots. images/icon.png is a 512 px render of the launcher icon's drawables. |
app/… is app/src/main/java/dev/samuelq/gpx, which also holds MainActivity and GpxApplication.
Offline GPX is licensed under the GPLv3 or any later version. See LICENSE for details.
Bundled libraries are credited within the app at Settings > Libraries.
The test fixture app/src/test/resources/andorra-fragment.map, which is not part of
the app, contains map data © OpenStreetMap contributors,
available under the Open Database License.
It is cut from a mapsforge extract.



