From 7f470e4505856db343545006c4fbd065738023da Mon Sep 17 00:00:00 2001 From: eddy Date: Wed, 14 Oct 2026 20:54:38 +0200 Subject: [PATCH] README: volledige herschrijving (schermen, features, installatie, architectuur) --- README.md | 208 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 120 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index d53d366..04a900f 100644 --- a/README.md +++ b/README.md @@ -1,123 +1,155 @@ # 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**. +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** 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. -- Per wekker is een **aan/uit-schakelaar** op het hoofdscherm: uitgezet blijft uit +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. -- In **Instellingen → Wekkergeluiden** kun je per wekker een eigen geluid kiezen - via de systeem-ringtone-kiezer; standaard is het systeem-alarmgeluid. -- 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. + 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 de APK: `./gradlew :app:assembleDebug` +1. Bouw: `./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 → **Instellingen** → vul in: +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; zie de Obsidian-vault voor - de bestaande app-wachtwoorden — niet in de app hardcoden) -4. **Opslaan**, terug op het hoofdscherm **Nu controleren** om te testen. -5. Verleen de meldingenmachtiging bij de eerste start (systeemdialoog). + - 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 '' +``` ## 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 | +| 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 na herstart opnieuw inplannen | -| Batterijoptimalisatie uitzonderen | Achtergrondtaken betrouwbaar | +| Boot (`RECEIVE_BOOT_COMPLETED`) | Wekkers + 12:00-controle na herstart herstellen | ## Architectuur ``` -MainActivity hoofdscherm: klok + geplande wekkers + "Nu controleren" + link naar instellingen -SettingsActivity instellingen: CalDAV-url, gebruiker, app-wachtwoord (+ uitleg) -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) +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) ``` -Hoofdscherm en instellingen zijn bewust gescheiden; installatiehulp via -launch-intent-extra's (`--es url/user/pass`) slaat de config op en draait -direct een controle, zonder UI-interactie. +### Modules -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. +- `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 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). +- **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 +## Build & test -- Gradle wrapper 8.13, AGP 8.7.3, Kotlin 2.0.21 +- Gradle wrapper 8.13 · AGP 8.7.3 · Kotlin 2.0.21 - compileSdk/targetSdk 34, minSdk 27 (Android 8.1+) -- Tests: `./gradlew :app:testDebugUnitTest` +- `./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 die de EBS-Diensten-agenda vult: - `~/src/scrapedienst` + vault-notitie `scrapedienst.md` -- CalDAV-referentieclient: `~/src/caldav-mcp` \ No newline at end of file +- **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` \ No newline at end of file