An offline Muslim prayer-times, qibla, and Hijri-date app for Android, built with Jetpack Compose and Material 3 Expressive.
Play Store title: Material Prayer Times · package app.waqt · free, no
ads, no accounts, no analytics.
- Today — the five prayers plus sunrise, in one of two layouts: Madar (the day's orbit — a band of wavy prayer windows above the hero countdown) or Rawdah (the shape garden — larger prayer glyphs and a quick-actions menu).
- Qibla — a compass dial with true-north declination correction, degrading to a plain bearing on devices with no magnetometer.
- Widget — a Glance home-screen widget showing the next prayer and the shape of the day.
- Notifications — per prayer, four modes (Alarm / Chime / Silent / Off), plus an optional persistent countdown that uses Android 16's segmented progress style where available.
- Calculation — 11 methods, both Asr madhabs, four high-latitude rules, per-prayer minute adjustments to match your local mosque, and a Hijri offset.
Everything is computed on-device with adhan2. There is no backend.
./gradlew :app:assembleDebug # debug APK
./gradlew :app:testDebugUnitTest # unit tests
./gradlew :app:lintVitalRelease # the lint pass that gates a release build
./gradlew :app:bundleRelease # signed release .aab (needs keystore.properties)Requires a JDK 17 toolchain to build. Unit tests run on JDK 21 — adhan2-jvm
is published as class-file 65, so running them on 17 fails with
UnsupportedClassVersionError before any assertion executes. The
foojay resolver in settings.gradle.kts provisions that JDK automatically.
bundleRelease needs a keystore.properties in the repo root, which is
gitignored and must never be committed:
storeFile=upload-keystore.p12
storePassword=…
keyAlias=upload
keyPassword=…Without it the build still configures and runs, it just produces an unsigned
artifact. The .p12 is the upload key, not the app signing key — Google
holds the latter under Play App Signing. Back both the keystore file and its
password up somewhere off this machine.
AAB=app/build/outputs/bundle/release/app-release.aab
unzip -l "$AAB" | grep -E 'META-INF/.*\.(RSA|SF)$' # must be signed
java -jar bundletool.jar dump config --bundle="$AAB" | grep alignment
# PAGE_ALIGNMENT_16K expected — re-check after every dependency bumpTwo things worth re-checking after a dependency bump, because both fail silently rather than at build time:
grep INSTANCE app/build/outputs/mapping/release/seeds.txt # nav3 back-stack restoreand that enum name literals survive R8 — settings and the adhan2 method bridge are both persisted and resolved by name.
app/src/main/java/app/waqt/
data/ repositories: settings + location DataStores, adhan2 wrapper
domain/ Prayer, PrayerDay, the timeline walk (computeUpcoming)
notifications/ alarm chain, channels, prayer + ongoing notifications
ui/ Compose screens: home, qibla, settings, about, onboarding
widget/ Glance app widget and its refresh worker
The alarm design is a one-alarm chain: only the next marker is ever
scheduled. When it fires, the receiver re-arms the following one first, then
does its rendering work. WidgetUpdateWorker also re-arms on every run, so a
receiver killed mid-flight self-heals rather than leaving the app permanently
silent.
See PRIVACY.md. The short version: no servers, no accounts, no
analytics. The one thing that does touch the network is Android's Geocoder
when you search for a city — the app is explicit about this in Settings, in the
policy, and in its Play Data safety declaration, and all three say the same
thing.
See ATTRIBUTION.md for bundled assets and their licences. The in-app Open Source Licences screen reproduces the notices the dependencies require.
The source code is licensed under the Apache License 2.0.
That covers the code in this repository only. Bundled assets carry their own terms and are not relicensed by it:
- Prayer time calculation uses adhan2, MIT.
- Cairo (the Arabic display face in
app/src/main/res/font/) is under the SIL Open Font License 1.1.
See ATTRIBUTION.md for the full notices, including what was modified and why.


