LogGPX: an Android GPS tracker for ski touring, in Java, before I knew about Kotlin
An Android app I built for ski-touring traces in 2020. Notes on parsing GPX, why GPS altitude is a lie, the battery cost of high-frequency sampling, and what I would build differently five years later.
LogGPX is an Android app I wrote between February and May 2020, during the first lockdown in France, when ski touring was the most cabin-fever-friendly activity I could legally do. The app records a GPS trace as you walk up a mountain, exports it as a GPX file, and pairs that file with the existing VisuGPX web service so you can later overlay outings on a map and compare distance, elevation, and pace. Repo: github.com/aelmufti/LogGPX.
I am writing this five years after the fact, because the project has aged in interesting ways. Almost every choice I made has a 2026 alternative that would be better. Some of those choices were forced by what I knew at the time. The rest are real lessons.
Why Java for Android, in 2020
Kotlin had been Google's preferred Android language since 2017 and the official one since 2019. By 2020 most new Android tutorials were already in Kotlin. So why Java?
Honestly: that is what the curriculum taught. The Android course at my engineering school was still Java. I knew Java well from earlier projects and I knew Kotlin only as a rumour from job listings. Choosing Kotlin would have meant learning the language at the same time as building the app, on a fixed deadline, during a lockdown. I did not have the slack for that.
The cost shows up in the codebase. Java in 2020 lacked data classes, null safety, sealed types, extension functions — every feature that makes a small mobile app pleasant to write. The Java app is maybe 1.6× the lines a Kotlin equivalent would be, and several null-pointer crashes that Kotlin's type system would have prevented at compile time were instead found by users.
Parsing GPX, which is XML, which is regret
GPX is the de facto exchange format for GPS traces. It is XML. Specifically it is a flat tree of <trkpt lat=... lon=... ele=...> elements with optional timestamps and sometimes optional extensions.
The native Android XML parsers (DOM, SAX, XmlPullParser) all work, but each comes with its own awkwardness. DOM loads the whole file into memory, which is fine for a one-hour walk and not fine for a long backpacking trip's worth of points. SAX is event-based and verbose to write correctly. I ended up with XmlPullParser because Android-recommended, but the boilerplate for a clean GPX → typed-object pipeline is more than you would expect.
If I rebuilt the parser today I would either use a small library (there are several maintained ones) or write a streaming parser that emits points lazily and never holds the whole trace in memory. Memory is cheap on a 2026 phone; battery for parsing a 50 MB file is not.
GPS altitude is a lie, and the barometer is not always there
The first version of LogGPX displayed elevation gain from the GPS-reported altitude. The number was wildly noisy. Walking a flat trail at constant elevation produced 80 metres of "gain" over an hour because the GPS altitude estimate jumps by tens of metres second to second.
Two real fixes exist:
- Smooth the GPS altitude. A simple moving average over the last 30 seconds reduces the noise but smooths out genuine climbs at the start and end of a leg. A Kalman filter is better but I did not have the confidence in 2020 to implement one and trust its output.
- Read the barometer. Many Android phones have a barometric pressure sensor, which gives a much more stable relative altitude (you cannot trust absolute altitude from a barometer without local calibration, but relative climb is exactly what you want for elevation gain).
LogGPX shipped with smoothed GPS altitude and a fallback path to use the barometer where available. The right answer — combine both via a Kalman filter, weighted by the barometer when present — was on my list and never made it to ship. Five years later I still have the issue open.
Battery is the actual product constraint
High-frequency GPS sampling drains a phone fast. A modern phone running GPS at 1 Hz with the screen off will lose 20-30% of its battery per hour of recording. For a long ski-touring day — out at six, back at six — that is the whole battery.
LogGPX has an adaptive sampling rate. When the user is moving in a consistent direction at a consistent pace, the sampling rate drops to one point every five to ten seconds. When the heading or speed changes appreciably, it spikes back up. The result is a trace that is honest about turns and switchbacks without paying the battery cost of constant sampling on long straight sections.
The other battery decision: a foreground service with a permanent notification. The notification is ugly and intrusive. It is also what keeps Android from killing your recording when the user backgrounds the app. Inconvenience is what makes the recording reliable.
The VisuGPX integration
VisuGPX is a long-running French website for storing and analysing GPX traces. The LogGPX → VisuGPX path is the actual workflow: record a trace in the field, hit upload, the trace lives on a server you do not have to maintain, and you can compare it with the other ten thousand traces in your VisuGPX library.
Integrating against a third-party service you do not control is the part of the project I would do differently today. The dependency is a foot-gun: any change in the VisuGPX upload endpoint breaks the app silently. The right engineering move was a "save locally + optional upload" flow rather than upload-as-the-main-path. I did not see that clearly at the time.
What I would build today
Probably nothing. Strava, Locus Map, Komoot, Gaia GPS — the market for "track a hike on Android" has fully solved itself. The reasons I shipped LogGPX in 2020 were that none of those services handled the specific ski-touring terrain analysis my friends and I wanted, and that building the thing was its own goal. Both reasons have weakened.
If I were starting from scratch with the same constraint — track and analyse touring traces — I would write a small Kotlin app with Jetpack Compose, the modern Location APIs, and the foreground-service pattern. The total LoC would be a fraction of what Java required. And I would treat battery profiling as a first-class test, not a thing I notice when my phone dies on a ridge at 2 PM.
The compounding-knowledge angle
LogGPX is the oldest of my published projects. Reading the code now is uncomfortable in the way reading any code five years old is. It also taught me things I now reach for instinctively: sensor fusion, foreground services, streaming parsers, GPS reliability quirks, and the basic humility that a phone is not a server. Every project compounds onto the next one. This one was a thicker layer of that compounding than I realised at the time.