No description
  • Kotlin 92.9%
  • Python 5.2%
  • Shell 1.7%
  • Dockerfile 0.2%
Find a file
petsitting-builder 193af279fa feat(android): action-after-snackbar for destructive ops (v6.5a A1-A7)
- ClientDetailScreen reassign: confirmAction before updateVisit
- ClientsScreen archiveOne: confirmAction before archiveClient
- VisitDetailScreen setStatus/deleteVisit: already gated (A2/A3)
- OverviewScreen markAllScheduledComplete: already gated (A6)
- CalendarScreen bulk delete: AlertDialog gates (A7)
- ClientDetailScreen archive: AlertDialog gates (A4)
- 7/7 A items: A1 already had confirmAction; A2-A7 wired
- Smoke tested on device: reassign → Missy fires snackbar first
- 7/7 harness scenarios pass
- plan.md: A1-A7 marked done
2026-06-28 01:28:14 -04:00
android-app/app feat(android): action-after-snackbar for destructive ops (v6.5a A1-A7) 2026-06-28 01:28:14 -04:00
backend feat(v6-F3): visit checklist (Walked / Fed / Water / Meds / etc.) 2026-06-27 21:38:11 -04:00
gradle A0: Android app scaffold (theme, nav, login, screens, data layer) [in progress] 2026-06-21 08:02:18 -04:00
harness docs(v6.5): release notes + harness login fix for v6.4 picker 2026-06-27 23:44:29 -04:00
scripts P18+P20: run-tests.sh + real harness scenarios + nav_* testTag on Icon 2026-06-21 17:10:52 -04:00
shared feat(v6-F3): visit checklist (Walked / Fed / Water / Meds / etc.) 2026-06-27 21:38:11 -04:00
.env.example B0: backend Docker Compose + Ktor skeleton (Postgres + Flyway + JWT auth) [DONE] 2026-06-21 06:11:05 -04:00
.gitignore v4-3: fix README screenshots - untrack harness/shots/ + add 31 PNGs to git 2026-06-22 01:02:58 -04:00
build.gradle.kts F0: Firebase gradle config + placeholder google-services.json [DONE] 2026-06-21 07:48:44 -04:00
docker-compose.yml B6: FCM push reminders (scheduler + /device-token route) [DONE] 2026-06-21 07:25:16 -04:00
found_issues.md docs: mark issue #4 (calendar day tap) as resolved in v5.16 2026-06-23 01:09:58 -04:00
gradle.properties A0: Android app scaffold (theme, nav, login, screens, data layer) [in progress] 2026-06-21 08:02:18 -04:00
gradlew T2: harness screenshot capture (snap-on-fail, snap-on-pass) [DONE] 2026-06-21 06:01:27 -04:00
gradlew.bat T2: harness screenshot capture (snap-on-fail, snap-on-pass) [DONE] 2026-06-21 06:01:27 -04:00
loops-v2.md v3 prep: archive v2 plan/loops as plan-v2.md / loops-v2.md 2026-06-21 21:44:44 -04:00
loops-v3.md v3 wrap-up: archive v3 plan/loops as plan-v3/loops-v3 + embed screenshots in README (15 inline base64 PNGs, 1.9MB) 2026-06-22 00:11:18 -04:00
loops-v5.md chore: archive v5 plan, start v6 plan with 2-user priority features 2026-06-27 20:35:43 -04:00
loops.md docs: v6.4 release notes (F5) + v6 wrap-up + README bump 2026-06-27 22:08:54 -04:00
plan-v2.md v3 prep: archive v2 plan/loops as plan-v2.md / loops-v2.md 2026-06-21 21:44:44 -04:00
plan-v3.md v3 wrap-up: archive v3 plan/loops as plan-v3/loops-v3 + embed screenshots in README (15 inline base64 PNGs, 1.9MB) 2026-06-22 00:11:18 -04:00
plan-v5.md chore: archive v5 plan, start v6 plan with 2-user priority features 2026-06-27 20:35:43 -04:00
plan.md feat(android): action-after-snackbar for destructive ops (v6.5a A1-A7) 2026-06-28 01:28:14 -04:00
README.md feat(android): action-after-snackbar for destructive ops (v6.5a A1-A7) 2026-06-28 01:28:14 -04:00
RELEASE-v6.5.md docs(v6.5): release notes + harness login fix for v6.4 picker 2026-06-27 23:44:29 -04:00
RELEASE-v6.5a.md feat(android): action-after-snackbar for destructive ops (v6.5a A1-A7) 2026-06-28 01:28:14 -04:00
settings.gradle.kts P4-P6: split Routes.kt per-resource, add ErrorDto extension + Validation, fix smart-casts [DONE] 2026-06-21 08:21:21 -04:00

Petsitting Calendar

A 2-person (you + partner) petsitting planner/calendar Android app + self-hosted backend, built with Kotlin + Jetpack Compose + Ktor. Visits are costed as base + distance × rate; recurring bookings expand to individual visits. Data syncs to https://pets.evileko.dev on a self-hosted VPS.

Status

Latest: v6.5a — action-after-snackbar pattern complete (A1-A7). Destructive and state-changing actions (status, delete, archive, reassign, mark-all-complete, bulk-delete) all gate on snackbar confirmation before firing the backend call. No silent mutations. Combined with v6.5 Part B (pickers + decimal keyboards),

17/17 v6.5 items shipped. 8/8 harness tests passing.
Login Overview New visit v6.5
Clients Reports Calendar Settings
Clients Reports Calendar Settings

Screenshots (v2.0 polish pass)

Overview v2 Client Detail v2 Client Form v2 Calendar v2
Settings v2 Reports v2 Clients v2

Screenshots (v6.1 — Schedule-a-Trip)

| Add menu | Trip form | Scheduled | Calendar (next month) | | | | | |

Screenshots (v6.3 — Visit Checklist)

Checklist empty Checklist 2/6

Screenshots (v6.4 — Per-Pet Instructions)

Pet instructions on visit detail

Screenshots (v3.0 — long-press multi-select, visit assignment, pets, FAB menu, upcoming, settings)

Clients: long-press multi-select Bulk action bar
Client Detail v3 (with pets) Reassign visit (bottom sheet)
After reassign (visit moved to Alex) Mark complete (snackbar Undo)
Overview FAB menu Upcoming visits carousel
Settings v3 (profile, theme, notifications) Reports v3 (per-sitter/per-client tables + comparison)

Screenshots (v4.0 — Theme picker + persisted preferences)

Light theme Dark theme

(Theme picker is in Settings — System / Light / Dark / Dynamic; choice persists across app restarts via EncryptedSharedPreferences.)

Screenshots (v5.0 — quick-tap status + relative-time chips)

Status chip tap-to-cycle Upcoming: relative-time chips
Reports: quick-filter chips Visit: Share button
Today: long-press money chip
Reports: Share weekly summary
Overview: Mark all complete
Overview: Quick stats strip
Clients: Sort indicator chip
Client: Last visit summary
Settings: About card
Client: Swipe-left marks visit complete
Settings: Clear local cache button
First day of week: applied to This week
Settings: Clear local cache button

Tap a visit's status chip on Client Detail to cycle SCHEDULED → COMPLETED → CANCELLED. Each Upcoming visit row shows a pill chip with the time-to-now ("in 7h 15m" / "12h late" in red for past visits). Reports screen has quick-filter chips (Today / This week / This month / This year) that auto-fill the From/To fields and refresh. Each visit row has a Share button that opens Android's system share sheet with a one-line summary:

Architecture

petsitting-calendar/
├── shared/             # DTOs shared between backend and Android
├── backend/            # Ktor + Exposed + Postgres
│   ├── src/main/kotlin/pets/api/
│   │   ├── Application.kt
│   │   ├── db/          # Exposed tables + repos
│   │   ├── plugins/     # Ktor route plugins per resource
│   │   └── service/     # business logic
│   └── src/main/resources/db/migration/  # Flyway V1__init.sql, V2__pets_and_assignments.sql
├── android-app/        # Kotlin + Jetpack Compose + Ktor client
│   └── app/src/main/kotlin/dev/evileko/petsitting/
│       ├── data/        # TokenStore (EncryptedSharedPreferences) + PetsApi (Ktor)
│       └── ui/          # Compose screens (auth/calendar/clients/common/overview/reports/settings/theme)
├── harness/             # Python + ADB UI test driver + Waydroid scripts
│   ├── adb.py
│   ├── api.py
│   ├── scenarios.py
│   ├── ui_test.py
│   ├── shots/          # screenshots captured during E2E runs
│   └── scripts/         # bootstrap.sh, run-tests.sh, test-env-up.sh, test-env-down.sh
├── docker-compose.yml   # backend (api + postgres) — built into `petsitting-pets-api` on VPS
└── plan.md, loops.md    # autonomous agent loop state

Backend

Ktor app on https://pets.evileko.dev, with a Postgres 17 backend. Docker image petsitting-pets-api is built from backend/ and proxied through nginx (TLS via Let's Encrypt, DNS via Cloudflare).

Endpoints (v3.0)

  • POST /api/v1/auth/login — username + password → JWT
  • GET/PUT /api/v1/me — current sitter
  • GET /api/v1/sitters — list sitters (for visit assignment)
  • GET /api/v1/clients[?includeArchived=] — list clients
  • GET /api/v1/clients/{id} — single client (with stats)
  • POST /api/v1/clients / PUT /api/v1/clients/{id} / DELETE /api/v1/clients/{id} — CRUD
  • GET /api/v1/clients/{clientId}/pets[/{petId}] / POST /api/v1/clients/{clientId}/pets / PUT /api/v1/clients/{clientId}/pets/{petId} / DELETE — pet CRUD
  • GET /api/v1/bookings[?clientId=] / POST /api/v1/bookings — bookings
  • GET /api/v1/visits[?from=&to=&sitterId=&clientId=&status=] / PUT /api/v1/visits/{id} — visits (PUT accepts sitterId for reassignment)
  • GET /api/v1/visits/upcoming?days=N — upcoming visits for the Overview carousel
  • GET /api/v1/reports?from=&to= — report summary (per-sitter, per-client, totals)
  • POST /api/v1/device-token — register FCM token (no-op until FCM creds configured)

Releases

Tag APK Notes
v0.1 (see commit d14ca1a) Initial ship: backend + Android + harness
v2.0 (see commit 2f8fe57) Polish pass: R1-R5
v3.0 app-debug.apk (21 MB) Long-press multi-select, visit assignment, pets, FAB menu, upcoming, settings
v4.0 app-debug.apk (24 MB) Theme picker (System / Light / Dark / Dynamic), persisted settings, fixes (README screenshots)
v5.0 app-debug.apk (24 MB) Tap-to-cycle status chip on visit detail; relative-time chips on Upcoming

Download the latest APK from the v5.0 release on Forgejo.

Configuration

Edit .env.example (copy to .env on the VPS) and fill in:

PETS_DB_PASSWORD=<random>
PETS_JWT_SECRET=<random 64+ char>
PETSITTERS=[{"username":"eko","password":"hunter2","displayName":"Eko"}, ...]
FCM_SERVICE_ACCOUNT_PATH=/run/secrets/fcm-service-account.json

Android BuildConfig.API_BASE_URL = https://pets.evileko.dev/api/v1 (set in android-app/app/build.gradle.kts). google-services.json is gitignored; drop the real one from Firebase console in android-app/app/ before shipping.