Notrufnummern-Datenbasis, Ländererkennung, State-Management und Lokalisierung (AP1-AP7)

Implementiert den kompletten Kern-Flow der Emergency App gemäß docs/plan.md:
- Notrufnummern-Datenbasis für 199 Länder + Modelle/Repository (AP1)
- DialerService für tel:-Notrufe ohne Auto-Dial (AP2)
- Statisches UI mit Panik-Button-Fallback (AP3)
- Priorisierte Ländererkennung: SIM/Netz -> GPS/Geocoding -> Cache (AP4)
- EmergencyProvider verdrahtet Repository/LocationService mit der UI (AP5)
- Manuelle Länderauswahl als Fallback samt eigener Priorität (AP6)
- UI-Lokalisierung (de/en/fr/es), an erkanntes Land gekoppelt (AP7)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
root
2026-07-24 11:29:38 +02:00
co-authored by Claude Sonnet 5
parent 40e95cc495
commit e3aa088bc2
54 changed files with 7550 additions and 145 deletions
@@ -0,0 +1,64 @@
import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart';
import 'country_code_source.dart';
/// Ermittelt den ISO-Ländercode per GPS-Position + Reverse-Geocoding
/// (`docs/plan.md`, Abschnitt 6, Fallback-Quelle Nr. 2 insbesondere auf
/// iOS 16+ wichtig, da die SIM-Quelle dort eingeschränkt ist, siehe
/// Abschnitt 9).
///
/// Nutzt bewusst nur die in `geolocator` eingebaute Berechtigungs-API
/// (`checkPermission()` / `requestPermission()`) ein zusätzlicher Einsatz
/// von `permission_handler` wäre redundant (AP4-Vorgabe).
///
/// Liefert `null`, sobald irgendein Schritt scheitert: verweigerte/dauerhaft
/// verweigerte Berechtigung, deaktivierte Standortdienste, kein GPS-Fix
/// innerhalb des Zeitlimits, oder ein Netzwerkfehler beim Reverse-Geocoding
/// (`placemarkFromCoordinates` braucht ggf. Netz). Wirft dabei nie eine
/// Exception das ist im Plan als Edge Case vorgesehen (Abschnitt 7): Die
/// Kette fällt dann weiter auf den Cache zurück.
class GpsCountryCodeSource implements CountryCodeSource {
GpsCountryCodeSource({Geocoding? geocoding}) : _geocoding = geocoding ?? Geocoding();
final Geocoding _geocoding;
static const _timeout = Duration(seconds: 10);
@override
Future<String?> resolve() async {
try {
if (!await _hasPermission()) return null;
final position = await Geolocator.getCurrentPosition(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.low,
timeLimit: _timeout,
),
);
final placemarks = await _geocoding.placemarkFromCoordinates(
position.latitude,
position.longitude,
);
for (final placemark in placemarks) {
final code = placemark.isoCountryCode?.trim();
if (code != null && code.isNotEmpty) {
return code.toUpperCase();
}
}
return null;
} catch (_) {
return null;
}
}
Future<bool> _hasPermission() async {
var permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
}
return permission == LocationPermission.whileInUse || permission == LocationPermission.always;
}
}