An offline-first Android productivity app that combines timers, alarms, Pomodoro, journaling, and gym tracking into a single tool — with no accounts, no cloud, no ads, and no network access whatsoever. Everything you record stays on your device as plain Markdown files you own.
LifePace requests zero internet permissions. Your data never leaves your phone.
- Daily Notes — Every feature logs to a single Markdown file per day (
yyyy.mm.dd.md), stored in a folder you choose. A#### Summarysection at the top is rebuilt from scratch on every write, so it never accumulates duplicates. External edits (from any text app) are preserved via a read-merge-write cycle — LifePace never blindly overwrites your file. - Pomodoro — Configurable work / rest / long-rest intervals and cycle count. Type notes during a work session; they're appended to the day's file with a timestamp when the session ends. Optional "keep text between cycles" and a clear button.
- Countdown Timer — Keypad entry, start / pause / reset, and extend buttons (+30s, +5m, and a custom amount) to add time to a running timer.
- Gym Timer — Name an exercise, set weight and rest duration, then tap to start each rest countdown and log rep counts. Sets are grouped by exercise in the daily note; returning to the same exercise later appends under the existing group.
- Awareness Chime — A subtle periodic chirp at a configurable interval to keep you oriented in time. Runs alongside Pomodoro and Stopwatch; pauses automatically while a Countdown or Gym timer is active, then resumes.
- Alarms — Unlimited alarms with per-alarm name, time, day-of-week repeat schedule, sound, snooze, and on/off toggle. Survive reboot.
- Stopwatch — Start / pause / reset / lap, with a large split display (hours on top, minutes:seconds:centiseconds below).
- Journal — A chat-style interface where you message yourself. Each entry is appended to the daily note with a timestamp.
- Light & dark themes, following the system setting.
- Optional vibration on alerts.
All timers run in a foreground service, so they keep ticking accurately when the app is backgrounded or the screen is off.
There is no app-store listing — LifePace is distributed as source. To run it you build the app yourself and install it on your device (see below). If you just want the app on your phone and are comfortable with Android Studio, the build takes a few minutes.
- Android Studio (a recent stable release)
- JDK 17
- Android SDK with API level 35 installed
- An Android device or emulator running Android 8.0 (API 26) or newer
The project uses the Gradle wrapper (Gradle 8.11.1), so you don't need Gradle installed separately.
git clone <your-repo-url>
cd lifepace
./gradlew installDebugThis builds and installs a debug APK on a connected device/emulator. No signing setup required.
Release builds are signed with a keystore that is not included in this repository. To produce your own signed build:
- Generate a keystore (or use an existing one):
keytool -genkey -v -keystore your-release-key.jks \ -keyalg RSA -keysize 2048 -validity 10000 -alias your_alias
- Copy the template and fill in your values:
Edit
cp keystore.properties.template keystore.properties
keystore.propertiesto point at your keystore and credentials. - Build:
./gradlew assembleRelease
keystore.properties and all keystore files are gitignored and must never be committed. If keystore.properties is absent, the release build is produced unsigned rather than failing.
LifePace is a single-Activity Jetpack Compose app following MVVM.
- UI: Jetpack Compose with Material 3. Each screen has its own ViewModel; the Timer tab hosts Countdown, Gym, and Awareness as separate sub-screens with independent state.
- State: Kotlin Coroutines and
StateFlowthroughout (no LiveData or RxJava). Data flows down, events flow up. - Timers: A single foreground service (
TimerService) owns all ticking and coordinates the awareness chime. Timer math lives in a pureTimerEngineclass driven by aClockinterface, so it's unit-testable without Android dependencies. - Notes:
DailyNoteRepositoryis the single point of file contact, using a coroutineMutexto serialize writes and Storage Access Framework (ContentResolver+DocumentFile) for file I/O.SummaryBuilderandNoteFormatterare pure functions. - Alarms: Room database +
AlarmManagerexact scheduling. ABootReceiverre-registers enabled alarms after reboot. - Settings: Persisted via
SharedPreferences. - DI: Manual, via a single
AppContainer.
com.lifepace/
App.kt · MainActivity.kt · AppContainer.kt
core/
prefs/ — SharedPreferences wrapper
notes/ — repository, summary builder, formatter (read-merge-write)
audio/ — chime scheduling + playback
time/ — time formatting
service/ — foreground service, timer engine, alarm & boot receivers
data/alarm/ — Room entity, DAO, database, repository
ui/ — theme, shared components, one package per screen
navigation/ — Compose NavHost + bottom bar
Kotlin · Jetpack Compose (Material 3) · Coroutines + StateFlow · Room · Storage Access Framework · single foreground service. Min SDK 26, target/compile SDK 35, JDK 17.
- No internet permission is declared in the manifest.
- All data is stored on-device as Markdown files in a directory you select.
- Notes can be shared out manually via the Android share sheet; nothing is transmitted automatically.
Foreground service (keeps timers running), notifications (alerts), vibration, exact alarm scheduling, and boot-completed (to restore alarms after reboot). Storage access is granted per-folder through the system folder picker.