Buschauffeurswekker v1: CalDAV-dagcheck 12:00, kwartier-afronding, wekkers Opstaan/Wally/Vertrek, fullscreen alarm-UI

This commit is contained in:
eddy 2026-10-14 16:31:50 +02:00
commit 34505be9b8
31 changed files with 1881 additions and 1 deletions

116
README.md Normal file
View file

@ -0,0 +1,116 @@
# Buschauffeurswekker
Android-app die de functie van de normale wekker-app vervult, maar de wekkers
automatisch zet op basis van de Nextcloud CalDAV-agenda **EBS-Diensten**.
## Werking
- Elke dag om **12:00** leest de app de agenda van de **volgende dag**.
- De dienstbegintijd wordt **naar beneden afgerond op een heel kwartier**:
- 05:00–05:14 → 05:00
- 05:15–05:29 → 05:15
- 05:30–05:44 → 05:30
- 05:45–05:59 → 05:45
- Er worden drie wekkers gezet, alle drie vóór de afgeronde aanvangstijd:
- **Opstaan** — 2 uur vóór aanvang
- **Wally** — 1,5 uur vóór aanvang
- **Vertrek** — 0,5 uur vóór aanvang
- Staat er **geen dienst** in de agenda voor morgen, dan worden **geen wekkers** gezet.
- De wekker gaat volwaardig af (geluid + trillen, volledig scherm) **ook bij een
vergrendeld scherm**, met snooze (10 min) en "wekker uit".
- Het toestel mag daarvoor **mobiele data** verbruiken en taken op de
**achtergrond** uitvoeren.
## Installatie
1. Bouw de APK: `./gradlew :app:assembleDebug`
→ `app/build/outputs/apk/debug/app-debug.apk`
2. Zet de APK op de telefoon en installeer (sideload).
3. Open de app en vul bij **Instellingen** in:
- 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; zie de Obsidian-vault voor
de bestaande app-wachtwoorden — niet in de app hardcoden)
4. Druk op **Opslaan** en daarna op **Nu controleren** om te testen.
5. Verleen de gevraagde machtigingen via de knoppen onder **Machtigingen**:
meldingen, exacte alarmen, batterijoptimalisatie uitzonderen en (Android 14+)
volledig scherm.
## Machtigingen
| Machtiging | Doel |
|---|---|
| Internet / netwerk | Agenda ophalen over wifi én mobiele data |
| Meldingen (POST_NOTIFICATIONS) | Afgaande wekker 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 na herstart opnieuw inplannen |
| Batterijoptimalisatie uitzonderen | Achtergrondtaken betrouwbaar |
## Architectuur
```
MainActivity status + instellingen, knop "Nu controleren"
AlarmActivity volledig-scherm wekker (geluid, trillen, snooze/dismiss)
App kanalen + dagelijkse 12:00-controle bij opstart
daily check:
AlarmScheduler.scheduleNoonCheck → DailyCheckReceiver (12:00, exact, allowWhileIdle)
→ ShiftCheck.run agenda morgen ophalen → afronden → wekkers plannen
→ CalDavClient REPORT calendar-query (time-range, basic auth)
→ Ical iCal-parse: DTSTART (TZID/UTC), vroegste aanvang
→ ShiftPlanner afronding op kwartier + Opstaan/Wally/Vertrek
ring:
AlarmScheduler.scheduleAlarm → AlarmRingReceiver (exact, allowWhileIdle)
→ full-screen intent + directe start → AlarmActivity
→ Ringer wekkergeluid (loop) + vibratiepatroon
BootReceiver na reboot: 12:00-controle + geplande wekkers herstellen
Prefs instellingen/status, versleuteld (EncryptedSharedPreferences)
```
Details per bestand:
- `core/ShiftPlanner.kt` — pure logica: kwartier-afronding en wekkerplanning
(JVM-unit-tests, geen Android-afhankelijkheden).
- `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 volgens het recept van de
scrapedienst (zie vault-notitie `Homelab/Software ontwikkeling/scrapedienst.md`).
- `core/ShiftCheck.kt` — orchestrator: ophalen → filteren op morgen → vroegste
aanvang → plannen of annuleren; gedeeld door receiver en knop.
- `core/AlarmScheduler.kt` — exacte alarmen + 12:00-controle + snooze + testwekker.
- `receivers/DailyCheckReceiver.kt` — 12:00; plant zichzelf via AlarmScheduler
steeds opnieuw.
- `receivers/AlarmRingReceiver.kt` — afgaande wekker: wakelock, full-screen
intent (ook vergrendeld scherm), directe activity-start.
- `receivers/BootReceiver.kt` — herstart-herstel (BOOT_COMPLETED, TIME_SET,
TIMEZONE_CHANGED, MY_PACKAGE_REPLACED).
- `core/Notifier.kt`, `core/Ringer.kt` — meldingen (kanalen "wekkers"/"status")
en geluid/trillen.
## Keuzes
- **12:00-controle via exact AlarmManager-alarm**, niet WorkManager: precies om
12:00 (WorkManager periodiek is minimaal 15 min en onnauwkeurig).
- Bij **meerdere events** op één dag geldt de **vroegste aanvang** als
dienstbegintijd.
- **All-day-events** (bijv. "vrij") worden genegeerd; alleen getimede diensten
tellen.
- Na een fout (bv. offline) blijven eerder geplande wekkers staan en verschijnt
een statusmelding.
- Wachtwoord staat **versleuteld** op het toestel
(EncryptedSharedPreferences, AES256-GCM; bij falen plain fallback in de
app-sandbox).
## Build
- Gradle wrapper 8.13, AGP 8.7.3, Kotlin 2.0.21
- compileSdk/targetSdk 34, minSdk 27 (Android 8.1+)
- Tests: `./gradlew :app:testDebugUnitTest`
## Gerelateerd
- Scrapedienst die de EBS-Diensten-agenda vult:
`~/src/scrapedienst` + vault-notitie `scrapedienst.md`
- CalDAV-referentieclient: `~/src/caldav-mcp`