# Architecture: Wetter-Klon (Android, ohne Google) Basis: `recon.md`, `features.csv`. Schwerpunkt: **modernes Design und Homescreen-Widgets**. Es gibt keinen Server, keine Accounts und keine Zahlungen. Die App läuft komplett auf dem Gerät und spricht nur öffentliche, schlüsselfreie APIs an. ## Stack | Schicht | Wahl | warum | | --- | --- | --- | | Sprache/UI | Kotlin + Jetpack Compose, **Material 3 Expressive** | native Animationen, Shape-Morphing, Dynamic Color: genau der Look des Originals | | Hintergründe | Compose Canvas + **AGSL Runtime Shaders** (API 33+), Fallback auf Canvas-Partikel | flüssiger Regen/Schnee/Wolken/Sonne bei 60–120 fps, eigene Grafik statt Google-Assets | | Widgets | **Jetpack Glance** (+ RemoteViews nur wo nötig) | Compose-artige Widget-API, responsive Größen, Dynamic Color | | Persistenz | Room (SQLite) + DataStore | eine lokale DB, offline-first | | Netzwerk | Ktor Client + kotlinx.serialization | leicht, keine Google-Abhängigkeit | | Hintergrundjobs | WorkManager | Refresh, Warnungen, Widget-Updates ohne Play Services | | Standort | `android.location.LocationManager` (kein Fused Provider) | funktioniert ohne GMS (GrapheneOS, /e/, LineageOS) | | Karte | MapLibre Native + OSM-/OpenFreeMap-Tiles, RainViewer-Radarkacheln | Open Source, keine Google Maps | | DI | Hilt oder manuell (Koin) | Koin: weniger Magie, reicht für eine App | | Build/Verteilung | Gradle KTS, minSdk 29, targetSdk aktuell, F-Droid-tauglich (keine proprietären Libs) | | ## Datenquellen (offiziell, öffentlich) | Zweck | API | Hinweise | | --- | --- | --- | | Vorhersage aktuell/stündlich/10 Tage | Open-Meteo `/v1/forecast` (`models=best_match`, für DE `icon_seamless`) | kostenlos nichtkommerziell, CC BY 4.0, Attribution in den Einstellungen | | Minutengenauer Niederschlag | Open-Meteo `minutely_15` | Basis für „Regen in 20 Min“ | | Luftqualität + Pollen | Open-Meteo Air Quality API | Pollen nur in Europa | | Ortssuche | Open-Meteo Geocoding API | Name/PLZ | | Reverse Geocoding (GPS → Name) | Nominatim (OSM), max. 1 Req/s, eigener User-Agent | nur beim Standortwechsel | | Warnungen DE | DWD Warnungen (`warnings.json`/CAP) über Bright Sky API | | | Warnungen EU | MeteoAlarm CAP/Atom Feeds | | | Radar | RainViewer Weather Maps API (Vergangenheit + Nowcast) | Attribution Pflicht | ## Schema Siehe `schema.sql`: 6 Tabellen (`locations`, `card_layout`, `forecast_cache`, `alerts`, `widget_config`, `notification_pref`), die Einstellungen liegen in DataStore. Zugriffsregeln: Es gibt keinen Mehrbenutzerbetrieb, Row Level Security entfällt. Die Daten liegen app-privat im Sandbox-Speicher, Backup über Auto Backup ist optional (ausschaltbar). Harte Constraints: nur ein `is_current`-Ort (partieller Unique-Index), keine doppelten Orte (gerundete Koordinaten), Warnungen werden über die Provider-ID dedupliziert. ## Interne „API“ (Repositories / Use Cases) | Aufruf | macht | Input | Output | Flow | | --- | --- | --- | --- | --- | | `ForecastRepo.observe(locId)` | Flow aus Cache, löst Refresh aus, wenn älter als 30 min | locId | `Forecast` + Status (fresh/stale/offline) | F01 | | `ForecastRepo.refresh(locId, force)` | Open-Meteo forecast + air laden, Cache schreiben | locId | Result | F01 | | `LocationRepo.updateCurrent()` | GPS → Reverse Geocoding → `is_current`-Zeile | – | Location | F01 | | `LocationRepo.search(q)` | Geocoding, entprellt 300 ms | query | List | F02 | | `LocationRepo.add/delete/reorder` | CRUD + Standard-`card_layout` anlegen | Place / id / order | – | F02, F03, F04 | | `LayoutRepo.move(locId, card, toPos)` | Kartenreihenfolge pro Stadt | – | – | F07 | | `AlertRepo.sync(locId)` | DWD/MeteoAlarm abrufen, abgelaufene löschen | locId | List | F08 | | `RadarRepo.frames()` | RainViewer-Frame-Liste (−2 h … +30 min) | – | Frames | F05 | | `BriefGenerator.build(forecast, alerts)` | regelbasierter Satz („Ab 15 Uhr Regen, morgen 8° kühler“) | Forecast | String | S01 | | `SettingsRepo` | Einheiten, Theme, Animationen | – | Flow | F06 | ### Hintergrundjobs (WorkManager) | Job | Takt | macht | | --- | --- | --- | | `RefreshWorker` | periodisch 30 min (Netz vorhanden), Batterie nicht niedrig | alle Orte + Widgets aktualisieren | | `AlertWorker` | 15 min | Warnungen abrufen, neue severe/extreme → Notification | | `RainSoonWorker` | 15 min, nur Orte mit `rain_alerts` | `minutely_15` prüfen → „Regen in ~X Min“ | | `TomorrowWorker` | täglich zur eingestellten Uhrzeit | Morgen-Vorhersage als Notification | | Widget-Update | nach jedem Refresh + `updatePeriodMillis` als Fallback | `GlanceAppWidget.updateAll()` | ## Design-Architektur (Priorität) - **Theme:** Material 3 Expressive, Dynamic Color (Monet) **plus** eine eigene, zur Wetterlage passende Farbpalette (z. B. klar/Tag → Blau-Gelb, Gewitter → Violett-Grau). `WeatherTheme` liefert Tokens aus `replica-design`. - **Hintergrund-Engine:** `WeatherScene(condition, isDay, intensity, windDir)` → AGSL-Shader-Layer (Himmelsverlauf, Wolken, Niederschlagspartikel, Blitz, Nebel), mit Parallax beim Scrollen. Bei aktiviertem Battery Saver bzw. „Animationen reduzieren“ wird ein statischer Verlauf gezeigt. - **Karten:** wiederverwendbare `MetricCard` mit expressiven Formen (Cookie-/Pill-Shapes), eigene Gauges (UV-Bogen, Windkompass, Sonnenbogen, AQI-Skala) in Canvas, alles eigene Icons (z. B. Material Symbols, Apache 2.0, plus eigene Wettericons). - **Motion:** Shared-Element-Übergang von der Städteliste zur Stadt, Kartendrag mit Spring-Animationen, Haptik beim Einrasten. - **Edge-to-Edge**, prädiktive Zurück-Geste, Two-Pane-Layout ab Breite „medium“ (Tablet/Foldable). ## Widgets (Priorität) | Widget | Größen | Inhalt | | --- | --- | --- | | Jetzt | 2×1, 2×2 | Icon, Temperatur, H/T, Ort | | Stündlich | 4×2 | Jetzt + 5–6 Stunden | | Täglich | 4×2, 4×3 | Jetzt + 5 Tage mit Temperaturbalken | | Groß/Komplett | 4×4 | Jetzt, stündlich, täglich, Warnhinweis | | Pille/Glance | 4×1 | Datum + Wetter in einer Zeile (eigener At-a-Glance-Ersatz) | - Responsive `SizeMode.Responsive`, ein Widget-Typ pro Inhalt mit Größen-Breakpoints. - Stile: Dynamic Color, Wetterlage als Hintergrund (vorgerenderte Bitmaps, weil Widgets keine Shader können), transparent. - Konfigurations-Activity: Ort wählen (oder „aktueller Standort“) und den Stil, mit Live-Vorschau. - Tippen öffnet direkt die jeweilige Stadt, das Refresh-Icon stößt `RefreshWorker` an. ## Was beißt - **Zeitzonen/DST:** Stunden und Tage immer in der Zeitzone des Orts (`timezone=auto`) zeigen, nicht in der des Geräts. - **Doze/Akkuoptimierung:** WorkManager-Takte sind nicht garantiert. Bei OEMs mit aggressivem Energiesparen auf die Ausnahme hinweisen. - **Ohne Play Services:** Kein Fused Location. Für schnelle Ergebnisse `getCurrentLocation` des Netzwerk-Providers mit Timeout nutzen, sonst die letzte bekannte Position. - **Rate Limits:** Open-Meteo erlaubt 10k Aufrufe am Tag, kein Problem. Nominatim erlaubt 1 Req/s, also cachen. RainViewer-Tiles müssen gecacht werden. - **Widget-Grenzen:** RemoteViews können keine Animationen und keine Shader, daher Hintergründe vorrendern. Updates sind limitiert, also ereignisgesteuert aktualisieren. - **Offline:** Der Cache wird immer angezeigt, mit dem Stand „vor X Min“. Ab 3 h wird er als veraltet markiert. - **Lizenz/Attribution:** Open-Meteo, DWD, RainViewer und OSM kommen in die About-Seite und ins Kartenoverlay. - **Performance:** Shader auf alten Geräten drosseln (adaptive Qualität), Hintergrund pausieren, wenn er nicht sichtbar ist. - **DSGVO:** Es gibt kein Tracking und kein Analytics. Koordinaten gehen nur an die Wetter-APIs, auf 2 Nachkommastellen gerundet. ## Build-Reihenfolge 1. **Vertical Slice** (S01, S14): GPS → Open-Meteo → Room → Home mit Hero, stündlicher Leiste und 10-Tage-Liste, schlicht gestaltet. Tabellen `locations`, `forecast_cache`. Beweist den Stack ohne GMS. 2. **Design-Fundament** (S01): Theme, `WeatherScene`-Shader für die 6 Grundlagen (klar, bewölkt, Regen, Schnee, Gewitter, Nebel × Tag/Nacht), `MetricCard` und alle Must-Karten. 3. **Widgets** (S12): Jetzt, Stündlich, Täglich, Konfiguration, `RefreshWorker`. Tabelle `widget_config`. 4. **Orte** (S02, S03): Suche, Hinzufügen, Swipe-to-Delete, Pager zwischen Städten. 5. **Warnungen** (S11, S13): DWD/MeteoAlarm, Banner, `AlertWorker`. Tabelle `alerts`. 6. **Einstellungen** (S07–S09): Einheiten, Theme, Animationen. 7. **Should:** Radar (S06), Kartenreihenfolge (S10, `card_layout`), AQI, Druck, Sicht, Detailansichten (S04, S05), großes Widget und Pillen-Widget, Regen-Benachrichtigung. 8. **Could:** Brief, Pollen, Morgen-Benachrichtigung, Vibrationen, Tablet-Layout. 9. **Fixes** aus `/replica-entrepreneur`.