All work
React Native Mobile App

FuelTrack: Offline-First Trip & Fuel Reconciliation

Built an Android app that auto-detects driving trips via the Activity Recognition API and reconciles them against real fuel purchases to derive an accurate per-trip cost, no manual logging, fully offline.

Sector
React Native Mobile App
Timeline
In development
Role
Solo, Architecture & Build
  • React Native
  • Android
  • SQLite
  • op-sqlite
  • Firebase
  • Google Maps SDK
  • Offline-First
FuelTrack: Offline-First Trip & Fuel Reconciliation
3 taps
Daily User Effort

Per-trip review, the entire interaction surface in normal use

100%
Offline Capability

Local SQLite is the source of truth; Firestore is a sync target, not a dependency

Offline PDF
Report Output

Generated on-device so reporting never requires connectivity

The problem

Manual trip logging is the single reason people abandon mileage tracking. Employees reimbursed 'as per actual' fuel cost either guess their numbers or give up, and continuous GPS polling, the obvious fix, drains the battery badly enough that the app gets uninstalled within a week.

The solution

Used Android's Activity Recognition API to detect in-vehicle state transitions and only then sample GPS, reducing the user's daily work to a three-tap review per trip. Fuel cost is never typed. It is derived by a reconciliation engine that maps real fuel purchases against odometer deltas.

Approach

How it was built.

01

Detect, Don't Poll

Activity Recognition transitions gate the GPS sampler: location is only sampled while the device is confidently in a detected driving state. Battery cost tracks actual driving time instead of wall-clock time, which is what makes background tracking survivable.

02

Review, Don't Log

By the time the user opens the app, every trip already exists with a route and a distance. The screen is a triage queue, label it or exclude it, not a data-entry form. Excluded trips are hidden from reports, never deleted.

03

Cost Is Derived, Never Typed

Per-trip fuel cost is computed from fueling entries reconciled against odometer readings. Before enough history exists, it falls back to a claimed km/L figure and is clearly labeled as estimated rather than reconciled.

Technical detail

The parts worth explaining.

SQLite Over WatermelonDB

WatermelonDB's sync protocol assumes a purpose-built pull/push backend. With Firestore as the sync target a custom push worker is required either way, so its main advantage evaporates, and the reconciliation engine is SQL-shaped (window queries over fueling entries, time-range aggregates over trips), which raw SQL keeps direct and testable.

Three-State Sync Marking

Every row carries a sync status that moves local_only → sync_pending on post-sync edits → synced, so an opportunistic sync worker can resume from any interruption without a full diff.

UPDATE trips SET sync_status = 'sync_pending'
 WHERE id = ? AND sync_status = 'synced';

Lazy Reverse Geocoding

Trip creation never blocks on a geocoding call. Coordinates are captured immediately and addresses are resolved later, opportunistically, the difference between a trip that records and one that silently fails in a dead zone.

What I took from it
  • Battery cost, not accuracy, is the real constraint in background mobile tracking
  • Choosing the boring storage layer paid for itself the moment the reconciliation queries got complex
  • Soft-delete everywhere, users make mistakes and recovery has to be possible from inside the app
Where it goes next
  • Multi-tenant company accounts with employer approval workflows
  • Fixed-rate reimbursement policies alongside actual-cost reconciliation
  • iOS build once the Android tracking model is proven in daily use

Got a problem shaped like this?

If something here looks like what you're facing, tell me about it. The first conversation is an architecture discussion, not a sales call.