Sunrise, sunset & daylight tracker for multiple locations. Animated sun/moon arcs, time-of-day gradients, yearly daylight chart. Kotlin + Jetpack Compose + Material 3.
  • Kotlin 86%
  • HTML 10%
  • Python 3.2%
  • TypeScript 0.8%
Find a file
Paweł Orzech 8206f7f225
Some checks failed
Android CI / instrumented-tests (push) Has been cancelled
Android CI / verify (push) Has been cancelled
docs: internal track claim matches what was observed
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BdYueh3J2Ze577L6uJHLyK
2026-09-24 09:49:04 +02:00
.agents/skills/publish-sunzones Round up short notification delays and disable Glance trampolines 2026-08-02 00:24:59 +02:00
.claude/commands Remove worktree dirs from tracking and update gitignore 2026-04-11 17:53:21 +02:00
.codex Round up short notification delays and disable Glance trampolines 2026-08-02 00:24:59 +02:00
.github/workflows ci(claude): gate each event on the author of its own text 2026-09-23 00:00:34 +02:00
.idea Fix merge integration issues: duplicate fields, import paths, JUnit version 2026-04-11 17:53:11 +02:00
app release: 1.7.18 (vc 32) — location disclosure before Use my location 2026-09-24 09:39:14 +02:00
artifacts/device-evidence/20260822T111124Z docs: device evidence for the 1.7.2 WorkManager fix 2026-08-22 13:12:47 +02:00
build/reports/problems Stop revoking Pro from paying users who switch Google accounts 2026-07-23 17:26:31 +02:00
docs release: 1.7.17 (vc 31) — prominent disclosure for background location 2026-09-23 17:02:25 +02:00
google-play docs: record 1.7.18 — 1.7.17 rejected, vc 32 in review 2026-09-24 09:47:13 +02:00
gradle feat: in-app review + in-app updates, and an ASO rewrite of the store listing 2026-08-20 17:40:27 +02:00
Plans docs: record 1.7.13 production submission and passing release gates 2026-09-07 12:10:13 +02:00
screenshots Add screenshots to README 2026-01-27 16:54:20 +01:00
tools release: 1.7.18 (vc 32) — location disclosure before Use my location 2026-09-24 09:39:14 +02:00
.gitattributes Initial commit 2026-01-27 16:33:25 +01:00
.gitignore feat: apply UX audit improvements and release tooling 2026-07-30 10:07:44 +02:00
AGENTS.md docs: internal track claim matches what was observed 2026-09-24 09:49:04 +02:00
AUDIT_REPORT.md fix(location): no toast when location permission is denied; report residuals 2026-09-23 00:09:38 +02:00
AUDIT_REPORT_ROUND4.md Round 4 audit: reports, agent changelog, widget receiver label 2026-07-24 21:59:28 +02:00
AUDIT_REPORT_ROUND5.md docs: round-5 audit reports 2026-07-27 20:01:33 +02:00
build.gradle.kts feat(crashlytics): crash reporting that cannot carry a place or a coordinate 2026-07-27 02:43:00 +02:00
CHANGELOG.md release: 1.7.18 (vc 32) — location disclosure before Use my location 2026-09-24 09:39:14 +02:00
CHANGELOG_AGENT.md fix(location): no toast when location permission is denied; report residuals 2026-09-23 00:09:38 +02:00
CLAUDE.md docs: merge CLAUDE.md into AGENTS.md; CLAUDE.md becomes an @import 2026-09-19 21:51:27 +02:00
gradle.properties Initial commit 2026-01-27 16:33:25 +01:00
gradlew Initial commit 2026-01-27 16:33:25 +01:00
gradlew.bat Initial commit 2026-01-27 16:33:25 +01:00
IMPROVEMENTS_REPORT.md Improve accessibility and search UX 2026-08-23 14:37:13 +02:00
ISA.md Add SunZones Pro monetization: freemium + one-time premium_lifetime purchase 2026-07-19 12:44:34 +02:00
LICENSE Initial commit 2026-01-27 16:33:25 +01:00
README.md release: 1.7.1 (versionCode 15) 2026-08-20 17:56:05 +02:00
settings.gradle.kts Initial commit 2026-01-27 16:33:25 +01:00
UX_RECOMMENDATIONS.md docs: audit round 9 — report, UX recommendations, changelog 2026-09-23 00:05:20 +02:00
UX_RECOMMENDATIONS_ROUND4.md Announce page position to screen readers and explain a failed widget add 2026-07-24 22:03:37 +02:00
UX_RECOMMENDATIONS_ROUND5.md docs: round-5 audit reports 2026-07-27 20:01:33 +02:00

SunZones

Track sunrise, sunset and daylight across multiple locations around the world. A minimal, single-screen Android app with time-aware gradient backgrounds, animated sun/moon arcs, and a yearly daylight chart.

Screenshots

Warsaw at sunset Adeje at midday Warsaw at night

Features

  • Multi-location tracking -- swipe between saved locations to see real-time sun and moon data; tap page indicator to manage cities
  • Sunrise & sunset times with day/night length and countdown to next sunrise
  • Animated sun/moon arc -- a visual progress indicator showing where the sun (or moon) is in its path across the sky
  • Moon phase & illumination -- current lunar phase emoji and illumination percentage
  • Local time, labelled -- each card and city-list row says whose clock it is showing ("19:00 local time · 7 h ahead of you") and flags when the date over there is already different
  • Time-of-day gradients -- card backgrounds shift dynamically through sunrise, morning, midday, afternoon, sunset, twilight and night palettes
  • Resizable home-screen widget -- three designs from a 2x1 strip to a 4x4 block, each with a full-width sun/moon arc as its horizon line
  • Timezone-aware -- each location stores its own IANA timezone, so times are always correct regardless of where your device is
  • Search or GPS -- add locations by city name search or use your current position
  • Sunrise/sunset reminders -- optional local notifications, scheduled entirely on-device
  • 20 languages -- English, Polish, Chinese, Hindi, Spanish, French, Arabic, Bengali, Portuguese, Russian, Urdu, Indonesian, German, Japanese, Nigerian Pidgin, Marathi, Telugu, Turkish, Tamil, Vietnamese

SunZones Pro

A single one-time purchase (premium_lifetime, Google Play Billing -- no subscription, no ads, no account). The free tier holds three locations and every core sun feature above.

  • Unlimited locations
  • Twilight & light -- golden hour, blue hour and the full civil/nautical/astronomical twilight timeline
  • Night & Moon -- moonrise and moonset, the astronomical-night window, how much of it is moonless, the next new moon, and whether the Milky Way core clears the horizon after dark
  • Yearly daylight chart -- month-by-month day length, tap a month to jump to it
  • Time travel -- step the date back and forward, or pick any day between 1900 and 2100, and every reading on the card follows
  • Golden hour reminders

Privacy

No account, no backend, no analytics SDK, no ads. Sun and moon maths run entirely on-device; saved places never leave it.

One exception, stated up front: crash reports are sent to Google's Firebase Crashlytics, on by default, and can be turned off in Settings -> Diagnostics. Coordinates, saved place names and device identifiers are stripped from the message, breadcrumbs and custom keys before anything is sent; the stack trace and Crashlytics' own device/installation metadata are what remain.

All three store-facing documents are current: the privacy policy and Data Safety form were republished on 2026-07-30, and the store listing was rewritten on 2026-08-20 (see Plans/GROWTH.md).

Architecture

Clean MVVM, single-activity Jetpack Compose app with Hilt dependency injection.

Room DB (Flow)
  -> LocationDao -> LocationRepository -> GetLocationsUseCase(date, includeAstro)
       -> CalculateSunTimesUseCase (+ CalculateAstroNightUseCase) -> SunLocation
            -> MainViewModel (StateFlow) -> Compose UI
                                         -> WidgetRenderData -> Glance widget
Layer Responsibility
data/local Room entity, DAO, database
data/repository Thin DAO wrapper, current-location replacement logic
data/preferences DataStore: notification settings, crash-reporting opt-out
domain/usecase Sun/moon calculations via commons-suncalc, astronomical night and Milky Way season, yearly daylight aggregation
domain/model SunLocation (computed fields), AstroNight, ArcState (the one day/night decision), MonthDaylight
domain/util Timezone-id hardening, administrative-suffix stripping for geocoded names
diagnostics Crash-report scrubbing (pure JVM) + the Crashlytics seam behind it
billing EntitlementRepository, Play Billing data source, monetization config/killswitch
ui/main HorizontalPager of SunCard composables, date bar, 60-second auto-refresh; SelectedDatePolicy validates the restored/intent-borne date
ui/widget Glance widget; layout breakpoints, element gating and bitmap arcs decided in pure functions outside the Glance tree
ui/addlocation Geocoder search + GPS, timezone resolution via Android ICU
di Hilt modules providing Room, DAO, Geocoder, FusedLocationProviderClient, billing and diagnostics

Tech stack

Language Kotlin
UI Jetpack Compose, Material 3
DI Hilt
Database Room (schema exported, migrations tested)
Preferences DataStore
Widget Jetpack Glance 1.1.1
Background work WorkManager
Billing Google Play Billing 9.1.0
Crash reporting Firebase Crashlytics (opt-out; no Firebase Analytics)
Sun/moon math commons-suncalc 3.11
Location Google Play Services FusedLocationProviderClient
Geocoding Android Geocoder
Timezone lookup android.icu.util.TimeZone (built-in)
Tests JUnit 5, MockK -- 391 JVM unit tests
Min SDK 26 (Android 8.0)
Target SDK 36 (Android 16)

Build

Requires Java 17 and the Android SDK.

A google-services.json for the Firebase project must be present at app/google-services.json -- it is committed, since this repository is private.

export JAVA_HOME="/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home"
./gradlew assembleDebug

Full gate:

./gradlew lint testDebugUnitTest assembleDebug

assembleRelease now uploads the R8 mapping file to Firebase, so a release build needs network access. Debug builds upload nothing.

License

MIT -- Pawel Orzech