Building an Australian health tracker with a pedometer
Building a health tracker with a pedometer can be a practical Android project rather than a generic sensor demo. The app records daily steps, estimates distance from a user-defined stride, and presents a weekly history. That fits Australian routines, from morning walks around Sydney Harbour to coffee-and-laneway strolls in Melbourne, while distances remain in kilometres rather than miles.
The project combines the hardware step counter, runtime permissions, SharedPreferences, and SQLite. It also separates the sensor listener, data layer, and user interface. Each part remains small enough for a beginner to assemble, yet the finished app can be extended with charts, goals, reminders, or Health Connect data.
Set up the project and permissions
Create a Kotlin app with a single activity, a sensor service class, and a SQLiteOpenHelper. Choose an empty views project rather than Compose so the XML layout, ListView, and lifecycle code stay visible. Set a recent target SDK and keep the minimum SDK practical for the devices in your audience.
The manifest declares physical-activity access and treats the sensor as optional, allowing installation on phones without a step counter:
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
<uses-feature
android:name="android.hardware.sensor.stepcounter"
android:required="false" />
On Android 10 and later, request ACTIVITY_RECOGNITION at runtime with Activity Result APIs. Explain that the permission supports step counting, refuse cleanly when it is denied, and avoid requesting location because the sensor does not require a GPS fix.
Build the first screen with Material cards for today’s steps, distance, and goal progress. Add a ListView below it for previous days, plus a preference screen for stride length and daily target. Keep sensor code out of the activity by sending readings to the database helper.
Read and normalise step sensor events
TYPE_STEP_COUNTER reports a cumulative count since the device’s last reboot. A new installation therefore needs a baseline: record the first sensor value, save it with the current local date, and subtract it from later readings. A negative result means the sensor reset, so replace the baseline rather than displaying an impossible step total.
The listener performs that normalisation before updating the daily record:
private val stepListener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
if (event.sensor.type != Sensor.TYPE_STEP_COUNTER) return
val counter = event.values[0].toLong()
val steps = (counter - baseline).coerceAtLeast(0)
repository.saveSteps(LocalDate.now().toString(), steps)
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) = Unit
}
Register the listener in onResume and unregister it in onPause for an activity-scoped tracker. For screen-off tracking, move registration into a foreground service with a persistent notification and declare the required foreground-service permissions for the target Android version. At midnight, or when the local date changes, store the completed row and start a fresh baseline. Save the selected time zone because daylight-saving changes affect daily boundaries around Sydney and Melbourne.
Store daily activity with SQLite
SQLite gives the app a durable local history without a server. Store one row per date with the day as the primary key, along with steps, estimated distance, and an update timestamp:
CREATE TABLE daily_activity (
day TEXT PRIMARY KEY NOT NULL,
steps INTEGER NOT NULL,
distance_km REAL NOT NULL DEFAULT 0,
updated_at INTEGER NOT NULL
);
Use ISO dates such as 2026-03-08 so sorting remains chronological.
Perform reads and writes away from the main thread with a coroutine or Executor, then publish the result back to the UI. An update should query the existing day, update it inside a transaction, or insert a new row. SharedPreferences is suitable for the small baseline and stride settings, but it is a poor fit for an expanding activity history.
Expose repository methods such as today(), week(), and history() instead of scattering SQL through adapters. Populate the ListView from a BaseAdapter that formats each date, step count, and kilometre value. Refresh the list after midnight or when returning from the settings screen, and close every Cursor when the adapter no longer needs it.
Build the dashboard and local report
The dashboard should update without recreating the activity each time the sensor delivers a value. Observe a small state holder or LiveData, let the view model combine it with the saved goal, and render progress only when the numbers change. A simple custom View can draw a seven-day bar chart, while accessibility labels should state the actual step total rather than relying on colour.
A printable summary can be generated as controlled HTML and opened in a WebView. The practical steps for opening local reports cover asset loading and local file handling. Keep the generated report inside app storage, pass only numeric values into it, and avoid unrestricted WebView file access; destroy the WebView when its screen closes.
Calculate distance by multiplying steps by the user’s stride length in metres, then convert the result to kilometres. Label it an estimate because stride length varies by person and pace. TYPE_STEP_COUNTER has no timestamp for every step, so defensible activity timing and minutes per kilometre require a step detector, a wearable source, or another timed activity signal. Avoid turning the tracker into a medical device without appropriate validation.
Compare tracking approaches and prepare for release
Test on physical Android phones as well as in the emulator, because real sensor batching and battery use are hard to reproduce. Cover permission denial, an app restart, a reboot, midnight rollover, and daylight-saving transitions. Use a familiar route such as Brisbane’s riverside paths or Adelaide’s parklands to check that distance and goal displays behave sensibly in warm, bright conditions.
A continuously running pedometer needs a visible foreground-service notification, restricted sensor access, and a clear deletion option. The Australian Google Play release should complete the Data safety form, explain sensor and health-data permissions, and present the tracker as a general wellness tool. Local expectations favour kilometres and minutes per kilometre, so retain those units throughout the interface and store listing.
The final architecture depends on whether the app is a simple daily counter, a timed activity tracker, or a bridge to data from a watch:
| Approach | Best suited to | Main advantage | Main limitation |
|---|---|---|---|
| Phone step counter | Everyday daily tracking | Efficient and usually accurate | Requires baseline and daily reset handling |
| Step detector | Activity timing and pace estimates | Provides individual step events | More work and potentially inconsistent readings |
| Accelerometer analysis | Devices without a step sensor | Works on a wider range of phones | Estimates require calibration and use more battery |
| Health Connect or wearable data | Unified phone and watch history | Uses existing health records | Availability and permissions depend on the source |
This balance keeps the hardware-backed counter as the efficient default while making estimates, background limits, and permission choices visible to Australian users.