Free, open-source running trainer for Android. No Google services. No tracking. No ads.
- Pre-C25K — 3-week starter for absolute beginners who find C25K Week 1 too hard
- C25K — 9-week program to run 5K
- C210K — 14-week program to run 10K
- Bridge to 10K — 6-week bridge for C25K graduates
- One Hour Runner — 13-week progression to 60 minutes continuous running
- 5K Improver — 8-week speed and stamina program for runners who can already complete 5K
- Audible voice prompts with mid-run encouragement (Android built-in TTS — no internet needed)
- Customizable countdown warning thresholds
- Voice volume and speed controls
- Music ducking during voice announcements
- Background timer with lock-screen notification and pause/stop controls
- Optional GPS tracking (distance & pace) — works without it too
- Progress tracking across sessions
- Guide screen with FAQ and glossary
- Localised in English, Spanish, Galician, German, French, Brazilian Portuguese, and Russian
- Landscape layouts on tablets and foldables (phones stay portrait)
- Fully offline — no internet permission
- Compatible with GrapheneOS and any de-Googled Android device (no Google Play Services)
- JDK 21 (install via SDKMAN:
sdk install java 21.0.5-tem) - Android SDK with platform API 36 and build-tools 36+ (install via Android Studio or
sdkmanager)
git clone https://github.com/AnalogGhost/c2k.git
cd c2k
echo "sdk.dir=$HOME/Android/Sdk" > local.properties
./gradlew assembleFossDebugAPK: app/build/outputs/apk/foss/debug/app-foss-debug.apk
./gradlew testadb install app/build/outputs/apk/foss/debug/app-foss-debug.apkOr, to boot an emulator and install the debug build in one step: bash run-emulator.sh (uses the Pixel_10 AVD — adjust the script if yours is named differently).
Use the Docker build script to produce an unsigned APK in the same environment F-Droid uses:
bash docker-build.shAPK: app/build/outputs/apk/foss/release/app-foss-release-unsigned.apk
F-Droid builds from source and applies their own signature. Build from the tagged commit before making any further commits.
CI runs this script twice on every push and fails if the two builds don't produce byte-identical output — see .github/workflows/ci.yml.
- Generate a keystore (one-time):
keytool -genkeypair -v \
-keystore ~/c2k-release.jks \
-alias c2k \
-keyalg RSA -keysize 4096 \
-validity 10000- Add to
local.properties(this file is gitignored — never commit it):
sdk.dir=/home/YOUR_USER/Android/Sdk
storeFile=/home/YOUR_USER/c2k-release.jks
storePassword=YOUR_STORE_PASS
keyAlias=c2k
keyPassword=YOUR_KEY_PASS- Build and sign:
bash docker-build.sh
$HOME/Android/Sdk/build-tools/<version>/apksigner sign \
--ks ~/c2k-release.jks \
--ks-key-alias c2k \
--ks-pass pass:YOUR_STORE_PASS \
--key-pass pass:YOUR_KEY_PASS \
--out app/build/outputs/apk/foss/release/app-foss-release-signed.apk \
app/build/outputs/apk/foss/release/app-foss-release-unsigned.apkC2K is available on F-Droid. The app metadata file is com.hackerapps.c2k.yml.
To release a new version:
- Bump
versionCodeandversionNameinapp/build.gradle.kts - Add a changelog at
fastlane/metadata/android/en-US/changelogs/<versionCode>.txt(and the other locale folders) - Commit that version bump
- Run
bundle exec fastlane release— tags, runs the reproducible Docker build, signs, then pauses showing the real SHA-256 before pushing the tag and creating the GitHub release. F-Droid picks up new tags automatically once pushed. Run it from an interactive terminal — the confirmation prompt aborts in non-interactive shells (everything before it is local, so it's safe to rerun after deleting the local tag).
See fastlane/Fastfile for what the lane does step by step, and FDROID_PUBLISHING.md for the full initial submission guide.
C2K also ships on Google Play, built from the same source under the play product flavor (com.hackerapps.c2k, same as foss — only the signing config differs). bundle exec fastlane playstore builds the release AAB and uploads it plus store listing metadata; it defaults to a dry run (validate_only:true) against the internal track, so pass track:production validate_only:false to actually publish. Image/screenshot uploads are skipped by default (pass skip_images:false skip_screenshots:false when they change), and bundle exec fastlane metadata pushes listing-text fixes between releases without touching the build.
Store-listing copy in fastlane/metadata/android/ feeds both Play and F-Droid. For Play, short descriptions must stay ≤80 characters and avoid price/promotion keywords ("free" and translations) and third-party brand names.
| Permission | Why |
|---|---|
FOREGROUND_SERVICE |
Keeps the timer running with screen off |
FOREGROUND_SERVICE_HEALTH |
Required service type on Android 14+ |
FOREGROUND_SERVICE_LOCATION |
Required service type on Android 14+ when a workout is GPS-tracked |
ACTIVITY_RECOGNITION |
Required runtime permission for the health service type on Android 14+ — requested for every workout, not just GPS ones |
WAKE_LOCK |
Prevents CPU sleep mid-workout |
POST_NOTIFICATIONS |
Shows workout notification with pause/stop controls |
ACCESS_FINE_LOCATION |
Optional GPS for distance & pace — you can skip this |
ACCESS_COARSE_LOCATION |
Coarse fallback for location permission dialog |
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS |
Prompts to exempt the app from battery optimization so a workout isn't killed mid-run |
VIBRATE |
Optional haptic cue on interval change |
No INTERNET permission — the app is fully offline.
app/src/main/kotlin/com/hackerapps/c2k/
├── data/
│ ├── model/ # Program definitions, interval types, coaching tips
│ ├── db/ # Room database — sessions and GPS route points
│ ├── repository/ # SessionRepository
│ └── prefs/ # DataStore user preferences
├── engine/
│ ├── WorkoutEngine.kt # Coroutine tick loop, state machine
│ ├── WorkoutState.kt # Sealed class: Idle / Active / Paused / Completed
│ └── tts/ # TextToSpeech wrapper, audio focus, announcements
├── service/
│ └── WorkoutService.kt # ForegroundService, wake lock, notification
├── location/ # GPS abstraction (graceful fallback if unavailable)
└── ui/
├── screen/home/ # Program selection, recent history, streak
├── screen/program/ # Week/day picker with completion badges
├── screen/workout/ # Live timer, interval ring, distance/pace
├── screen/history/ # Past sessions list with CSV/GPX export
├── screen/settings/ # Voice, GPS, vibration, display preferences
├── screen/guide/ # FAQ and glossary
└── screen/contributors/ # Contributors credits
See CONTRIBUTING.md for how to contribute, including translation credits.
Copyright (C) 2026 Matt Brown
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
See LICENSE for the full text.