# 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. - Per wekker is een **aan/uit-schakelaar** op het hoofdscherm: uitgezet 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. ## 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 → **Instellingen** → vul 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. **Opslaan**, terug op het hoofdscherm **Nu controleren** om te testen. 5. Verleen de meldingenmachtiging bij de eerste start (systeemdialoog). ## 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 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) ``` 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. 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`