A quiet, eyes‑closed interval timer for Reiki and meditation sessions, built for the Pixel Watch 3 (Wear OS 5). You pick two things — how long the whole session lasts and how long to hold each hand position — and the watch gently buzzes on every position change and again, more firmly, when the session ends. No position names, no numbers, no fuss: just two countdowns and your hands.
The timer runs in a foreground service with a wake lock, so the cues keep coming even when the screen is off and the watch is dozing — exactly what you need when your eyes are closed and your wrist is resting.
- Two simple dials — session length and per‑position length, with a live hint
of how many positions fit (e.g.
10 positions). - Dual countdowns during a session — time left on the current position (large) and time left in the whole session (below).
- Distinct haptics — a double "heartbeat" buzz on each position change, and three strong half‑second pulses at the very end.
- Pause / Stop — pause both countdowns (handy when a client turns over from back to front); stop ends the session and shows the summary.
- Shake to pause — a firm wrist shake toggles pause without opening the screen.
- Heart‑rate summary — optional. A Fitbit‑style "Done" screen reports duration, positions, average / minimum BPM and start → end heart rate.
- Keep screen on — optional, for when you want the display to stay lit.
- Drift‑free timing — the timer is anchored to wall‑clock time, so it stays accurate even when the app is backgrounded and the CPU sleeps.
- Settings — keep‑screen‑on, heart rate, linked vs independent session math, and default position / session durations, all persisted between runs.
The Session by positions setting controls how the two dials relate:
- On (linked) — the session length is always a whole number of positions.
Stepping the session moves it one position at a time; changing the position
re‑snaps the session to a clean multiple. The hint reads e.g.
10 positions. - Off (independent) — session and position are set separately (session steps
by one minute). The number of positions is rounded up and shown as
≈ N.
SessionService— a foreground service (foregroundServiceType="health") that owns the timer, holds aPARTIAL_WAKE_LOCK, drives theVibrator, and publishes session state the UI observes. Because it's a foreground service with a wake lock, vibration cues survive screen‑off and Doze.- Drift‑free timer — elapsed time is computed from
SystemClock.elapsedRealtimeanchors (start time, accumulated pause time) rather than counting ticks, so a backgrounded session never runs long. - Heart rate — captured through Health Services
ExerciseClient(HEART_RATE_BPM). Health Connect is unavailable on the watch, so a lightweight Health Services workout is used purely to sample BPM for the summary. - Shake detection —
ShakeDetectorwatches the accelerometer for a burst of spikes above a g‑force threshold within a short window, with a cooldown. - UI — Jetpack Compose for Wear OS. The whole app is three screens: setup, running session, and the post‑session summary, plus a settings screen reached by the gear button below the setup screen (swipe right to leave without saving).
Kotlin · Jetpack Compose for Wear OS · Health Services Client · Foreground service
- wake lock · SharedPreferences.
compileSdk 35 · minSdk 30 · targetSdk 34 · AGP 8.5.2 · Kotlin 1.9.24 ·
Gradle 8.7 · JDK 17 (Temurin).
app/src/main/java/com/pavel/reikitimer/
MainActivity.kt Compose host: setup screen, settings screen, navigation
SessionScreen.kt Running‑session UI + Fitbit‑style summary
SessionService.kt Foreground service: timer, wake lock, vibration, heart rate
SessionState.kt Observable UI state + summary model
SessionMath.kt Pure timing math (positions, clamping, snapping)
TimeFormat.kt mm:ss / h:mm:ss formatting
ShakeDetector.kt Accelerometer shake-to-pause
Settings.kt Settings data class
SettingsStore.kt SharedPreferences persistence
app/src/test/java/ JVM + Robolectric unit tests
docs/ Design notes and screenshots
./build.sh # debug APK -> app/build/outputs/apk/debug/app-debug.apkbuild.sh sources env.sh (which sets JAVA_HOME / ANDROID_HOME) and runs
./gradlew assembleDebug.
The watch has no data USB port, so install over Wi‑Fi (Mac and watch on the same network). On the watch: Settings → System → About → tap Build number ×7 → Developer options → enable ADB debugging and Wireless debugging → Pair new device (this shows an IP:port and a pairing code).
./deploy-watch.sh pair <IP:PAIRING_PORT> <CODE> # once
./deploy-watch.sh install <IP:DEBUG_PORT> # IP:port from the Wireless debugging pageThe debug port rotates each time wireless debugging reconnects — read the current one from the Wireless debugging screen. Turn wireless debugging off afterwards to save battery.
source env.sh && emulator -avd reiki_wear # Wear OS 5, arm64./gradlew testDebugUnitTestTwo tiers: pure‑JVM logic tests (timing math, formatting, shake detection, UI
state) and Robolectric tests for Android‑dependent pieces — 30 tests across
SessionMathTest, SessionUiStateTest, TimeFormatTest, ShakeDetectorTest,
SettingsStoreTest, and SessionServiceTest.
A personal project, shared in case it's useful to other Reiki and meditation practitioners. The violet hands icon nods to the crown‑chakra colour often used to depict Reiki energy.



