Files
2026-10-17 14:56:22 +02:00

111 lines
8.6 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.
# 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<Place> | 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<Alert> | 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<Settings> | 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`.