android-buschauffeurswekker/README.md

155 lines
No EOL
7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Buschauffeurswekker
Android-wekker-app die wekkers **automatisch** zet op basis van het EBS-dienstrooster
in de Nextcloud CalDAV-agenda **EBS-Diensten**. De wekker wekt zoals elke normale
wekker-app (geluid, trillen, volledig scherm, snooze) — maar het wanneer wordt zelf
bepaald uit de dienst van morgen.
## Werking
Elke dag om **12:00** haalt de app de agenda van **morgen** op en zet daaruit de
dienstaanvang. Die wordt **naar beneden afgerond op een heel kwartier**:
| Aanvang dienst | Aangenomen aanvang |
|---|---|
| 05:00 – 05:14 | 05:00 |
| 05:15 – 05:29 | 05:15 |
| 05:30 – 05:44 | 05:30 |
| 05:45 – 05:59 | 05:45 |
Technisch: `minuut = (minuut / 15) * 15` (floor op kwartier). Rond middernacht kan de
wekker op de vorige dag vallen (dienst 00:10 → Opstaan de dag ervoor 22:00).
Daarna worden drie wekkers gezet, elk voor de afgeronde aanvang:
| Wekker | Tijd vóór aanvang | Voorbeeld (06:15) |
|---|---|---|
| **Opstaan** | 2 uur | 04:15 |
| **Wally** | 1,5 uur | 04:45 |
| **Vertrek** | 0,5 uur | 05:45 |
**Geen dienst morgen → geen wekkers.**
## Kenmerken
- **Donkere wekker-UI** in de stijl van de systeemwekker: kop "Wekkers", één kaart
per wekker met grote tijd + naam, acties achter het **⋮-menu**.
- **Aan/uit per wekker** — schakelaar op elke kaart; een uitgezette wekker blijft uit
(ook de dagelijkse 12:00-controle plant hem niet opnieuw) tot je hem weer aanzet.
Uitgeschakelde kaart is grijs, aangezette blauw.
- **Eigen geluid per wekker** via Instellingen → Wekkergeluiden (systeem-ringtone-
kiezer). Standaard is het systeem-alarmgeluid; valt terug op die toon als het
gekozen bestand verdwenen is.
- **Volwaardige wekker**: geluid (herhalend) + trillen, volledig scherm óók bovenop
het vergrendelscherm, snooze 10 min, "wekker uit".
- **Werkt op de achtergrond**: exacte alarmen (ook in Doze), herstel na herstart,
mobiele data toegestaan.
- **Statusmelding om 12:00** met de wekkers van morgen; tikken opent de app.
## Schermen
- **Hoofdscherm** — "Wekkers": de drie wekkerkaarten (met aan/uit) van morgen; onderaan
de diensttijd ("Dienst om 06:18"). Het **⋮**-menu bevat *Nu controleren* en
*Instellingen*.
- **Instellingen** — CalDAV-gegevens (agenda-URL, gebruiker, app-wachtwoord + Opslaan)
en *Wekkergeluiden* (per wekker een geluid).
- **Afgaande wekker** — full-screen: naam, lopende klok, Snooze (10 min) / Wekker uit.
## Installatie
1. Bouw: `./gradlew :app:assembleDebug`
→ `app/build/outputs/apk/debug/app-debug.apk`
2. APK op de telefoon zetten en installeren (sideload).
3. App openen → **⋮ → Instellingen** → invullen:
- Agenda-URL: `https://nextcloud.eddydevink.nl/remote.php/dav/calendars/eddy/ebs_diensten-1/`
- Gebruikersnaam: `eddy`
- App-wachtwoord: een Nextcloud-app-wachtwoord (Nextcloud → Beveiliging →
App-wachtwoorden). Niet in de app hardcoden; zie de Obsidian-vault.
4. **Opslaan** → terug → **⋮ → Nu controleren** om te testen.
5. Meldingenmachtiging: systeemdialoog bij de eerste start.
Config is ook zonder UI te zetten (installatiehulp):
```bash
adb shell am start -n nl.eddydevink.buschauffeurswekker/.MainActivity \
--es url 'https://nextcloud.eddydevink.nl/remote.php/dav/calendars/eddy/ebs_diensten-1/' \
--es user 'eddy' --es pass '<app-wachtwoord>'
```
## Machtigingen
| Machtiging | Doel |
|---|---|
| Internet / netwerk | Agenda ophalen over wifi én mobiele data |
| Meldingen (`POST_NOTIFICATIONS`) | Afgaande wekker + status tonen |
| Exacte alarmen (`USE_EXACT_ALARM` / `SCHEDULE_EXACT_ALARM`) | Wekker óp tijd, ook in Doze |
| Volledig scherm (`USE_FULL_SCREEN_INTENT`) | Wekker bovenop het vergrendelscherm |
| Wakelock + trillen | Scherm aanhouden en trillen tijdens afgaan |
| Boot (`RECEIVE_BOOT_COMPLETED`) | Wekkers + 12:00-controle na herstart herstellen |
## Architectuur
```
App kanalen + 12:00-controle bij opstart
dagelijks 12:00
AlarmScheduler.scheduleNoonCheck (exact, allowWhileIdle)
→ DailyCheckReceiver → ShiftCheck.run
CalDavClient REPORT calendar-query (time-range, basic auth)
Ical iCal-parse: DTSTART (TZID/UTC), vroegste aanvang
ShiftPlanner kwartier-afronding + Opstaan/Wally/Vertrek
AlarmScheduler plan de aangezette wekkers
afgaan
AlarmScheduler.scheduleAlarm (exact, allowWhileIdle)
→ AlarmRingReceiver → full-screen intent + start → AlarmActivity
Ringer geluid (loop; eigen keuze met fallback) + trillen
snooze 10 min / dismiss
MainActivity / SettingsActivity UI (kaarten, menu, instellingen, geluidskeuze)
BootReceiver na reboot: wekkers + 12:00-controle herstellen
Prefs instellingen/status/plan, versleuteld (EncryptedSharedPreferences)
```
### Modules
- `core/ShiftPlanner.kt` — pure logica: kwartier-afronding + wekkerplanning (JVM-tests).
- `caldav/Ical.kt` — minimale RFC 5545-parser (VEVENT, DTSTART met TZID/UTC; all-day en
geannuleerde events worden overgeslagen).
- `caldav/CalDavClient.kt` — REPORT calendar-query (recept uit de scrapedienst).
- `core/ShiftCheck.kt` — orchestrator: ophalen → filteren op morgen → vroegste aanvang
→ plannen (aangezette) of annuleren; gedeeld door receiver, menu-knop en setup-intent.
- `core/AlarmScheduler.kt` — exacte alarmen, 12:00-controle, snooze, per-naam aanv/uit.
- `core/Notifier.kt` — kanalen "wekkers"/"status"; statusmelding tikt de app open.
- `core/Ringer.kt` — wekkergeluid (loop) gekozen per wekker + vibratie; fallback naar
de standaard alarmtoon als het gekozen bestand weg is.
- `receivers/DailyCheckReceiver.kt`, `AlarmRingReceiver.kt`, `BootReceiver.kt`.
- `AlarmActivity.kt` — full-screen wekker (geluid, trillen, snooze/dismiss).
- `SettingsActivity.kt` — CalDAV-gegevens + per-wekker geluidskeuze.
## Keuzes
- **12:00-controle = exact AlarmManager-alarm**, niet WorkManager: precies om 12:00
(WorkManager-periodiek is minimaal 15 min en onnauwkeurig).
- Meerdere events op één dag → **vroegste aanvang** telt als dienstaanvang.
- **All-day-events** ("vrij") tellen niet; alleen getimede diensten.
- Na een fout (bv. offline) blijven eerder geplande wekkers staan + statusmelding.
- Wachtwoord **versleuteld** op het toestel (EncryptedSharedPreferences AES256-GCM;
bij falen plain fallback in de app-sandbox). Nooit hardcoded.
- UI volgt Material 3: donker thema, standaard `MaterialSwitch` en systeemcomponenten.
## Build & test
- Gradle wrapper 8.13 · AGP 8.7.3 · Kotlin 2.0.21
- compileSdk/targetSdk 34, minSdk 27 (Android 8.1+)
- `./gradlew :app:assembleDebug` en `./gradlew :app:testDebugUnitTest`
- Unit tests dekken de afrondingsvoorbeelden (incl. de vier óf), wekkerplanning,
iCal-parse en een snapshot van een echte serverresponse door de pipeline.
## Gerelateerd
- **Scrapedienst** — vult de `ebs_diensten`-agenda (dagelijks 17:05):
`~/src/scrapedienst` + vault-notitie `Homelab/Software ontwikkeling/scrapedienst.md`
- **CalDAV-referentieclient**: `~/src/caldav-mcp`
- **Meldingen/wekkers** staan uit; zie de Obsidian-vault-notitie
`Homelab/Software ontwikkeling/Buschauffeurswekker.md`