|
All checks were successful
build-apk / build (push) Successful in 28m55s
The ongoing countdown now appears whenever a car is parked, paid or free,
and sticks around the way ntfy's does on GrapheneOS.
Why it wasn't showing at all on a paid park: StartSessionScreen carried its
own stricter copy of parseApiTime that demanded MM-DD-YYYY + AM/PM. The
dual-format fix from
|
||
|---|---|---|
| .forgejo/workflows | ||
| app | ||
| docs | ||
| parksmarter-client | ||
| scripts | ||
| server | ||
| .gitignore | ||
| INSTALL.md | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
BigBrainParking
An unofficial, de-Googled client for the ParkSmarter (IPS Group) parking system, built to run on GrapheneOS with UnifiedPush notifications and distributed via Obtainium — no Google Play Services, no Firebase, no app store.
Install it (users)
BigBrainParking installs and updates through Obtainium — no Google Play, no account. Works on GrapheneOS. On your phone (with Obtainium installed), tap:
➕ Add BigBrainParking to Obtainium
or follow INSTALL.md for the manual steps.
Monorepo layout
bigbrainparking/
├── parksmarter-client/ # Zero-dep TypeScript API client (reverse-engineered, live-verified)
├── app/ # Expo / React Native app (BigBrainParking)
└── .gitea/workflows/ # CI: build signed APK -> publish release for Obtainium
parksmarter-client— the API layer. All ~40 endpoints, the custom-header auth model, and response models (most verified against production). Runs anywhere; the app imports it directly. See its own README for the API details.app— the phone app. React Native (Expo prebuild), MapLibre maps, VisionCamera QR scanning, expo-secure-store token storage, local session-expiry reminders, and UnifiedPush wiring.
Features
| Feature | Status | Notes |
|---|---|---|
| Phone + password login | ✅ wired | tokens in OS keystore |
| Map of nearby meters (clickable, zoom, live GPS) | ✅ wired | MapLibre + OpenFreeMap tiles (no key) |
| Last-lot + My-location search | ✅ wired | opens on your last session's lot; GPS sent only via explicit "My location" |
| QR kiosk scan → meter lookup | ✅ wired | on-device VisionCamera |
| Save / share kiosks | ✅ wired | local (no server favorites API exists) |
| Active / past sessions | ✅ wired | list views + tap for full receipt |
| Start a paid session | ✅ works | confirmed live end-to-end (real $0.10 DL-zone charge); declines surfaced |
| Ongoing parking countdown | ✅ wired | foreground service; ticks down, End / Extend buttons, survives reboot |
| Session-expiry reminders | ✅ wired | local on-device notifications — no server, no push |
| UnifiedPush (ntfy) | ⚪ optional | not needed for reminders; stub for future server-initiated msgs |
Privacy
BigBrainParking sends your location to ParkSmarter only when you explicitly tap "My location" and search; it opens on your last parking lot instead of your GPS, and ships no analytics/tracking (no Segment/Amplitude/Firebase/Sentry, no ad-ID, no Google services). For a plain-language comparison with the official app — which auto-sends your GPS on map open and bundles that tracking stack — see docs/OFFICIAL_APP_PRIVACY.md. What reaches the API and what never does is also spelled out in the app's About page.
Build & run (dev)
Requires Node 20, JDK 17, Android SDK, and a GrapheneOS device (or any Android device) with USB debugging.
npm install # installs both workspaces
npm run build --workspace parksmarter-client # compile the client
cd app
npx expo prebuild --platform android # generate native project
npx expo run:android # build + install a dev client
The app talks to prod (apiv2.parksmarter.com) by default — change extra.psEnvironment
in app/app.json to point elsewhere.
Notifications on GrapheneOS
Everything here is entirely on-device — no server, no push, no FCM, no Play Services — so it works fully offline. UnifiedPush (ntfy) is wired only as an optional, no-op stub for any future server-initiated messages; nothing time-based needs it.
The ongoing parking countdown. Whenever a session is active — a paid one you bought or
a free check-in — a persistent notification shows the time left and ticks down, with
End and Extend buttons. It is held up by a real foreground service
(modules/bbp-notify, type specialUse), which is what makes it stick on GrapheneOS the
way ntfy's does: it survives the app being killed, can't be swiped away, and a
BOOT_COMPLETED receiver brings it back after a reboot. The service's life is exactly the
session's life — it stops itself, removing the notification, on End, when the meter
runs out, or whenever there's no session to show.
The countdown itself costs no battery: the end time is handed to Android as a chronometer, and the system redraws the ticking text with the app closed and no timer of its own.
- End stops tracking and clears the notification. On a free check-in that genuinely ends it. On a paid session it only stops the display — ParkSmarter has no stop-session endpoint, so time you already bought keeps running at the meter either way.
- Extend (labeled Pay on a free check-in) opens the purchase screen for that exact zone. The active session is stored locally with its zone, so this works offline, in Anonymous Mode, and after a reboot. Extending doesn't end anything until the purchase actually goes through.
Expiry reminders fire a configurable lead time (default 15 min) before the end, via
AlarmManager. Configure them in Account → Notifications, where a "Send a test
reminder" button lets you confirm they fire on your phone; the same screen has a toggle
for the ongoing countdown.
Distribution via Obtainium (self-hosted)
Tag a release and CI builds a signed APK and publishes it to this repo's releases; your
phone's Obtainium tracks the repo and offers updates. See
docs/DISTRIBUTION.md for the full server + CI + keystore setup.