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
+57
View File
@@ -0,0 +1,57 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:emergency/models/country.dart';
import 'package:emergency/models/emergency_type.dart';
void main() {
group('Country.fromJson', () {
test('parst ein Land mit mehreren Notrufnummern korrekt', () {
final country = Country.fromJson('DE', {
'name_en': 'Germany',
'name_de': 'Deutschland',
'flag': '🇩🇪',
'numbers': [
{
'type': 'general',
'number': '112',
'label_de': 'Notruf',
'label_en': 'Emergency',
},
{
'type': 'police',
'number': '110',
'label_de': 'Polizei',
'label_en': 'Police',
},
],
});
expect(country.isoCode, 'DE');
expect(country.nameDe, 'Deutschland');
expect(country.nameEn, 'Germany');
expect(country.flag, '🇩🇪');
expect(country.numbers, hasLength(2));
expect(country.numbers[0].type, EmergencyType.general);
expect(country.numbers[1].type, EmergencyType.police);
});
test('parst ein Land mit nur einer Notrufnummer korrekt', () {
final country = Country.fromJson('US', {
'name_en': 'United States',
'name_de': 'Vereinigte Staaten',
'flag': '🇺🇸',
'numbers': [
{
'type': 'general',
'number': '911',
'label_de': 'Notruf',
'label_en': 'Emergency',
},
],
});
expect(country.numbers, hasLength(1));
expect(country.numbers.single.number, '911');
});
});
}
+40
View File
@@ -0,0 +1,40 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:emergency/models/emergency_number.dart';
import 'package:emergency/models/emergency_type.dart';
void main() {
group('EmergencyNumber.fromJson', () {
test('parst gültiges JSON korrekt', () {
final number = EmergencyNumber.fromJson({
'type': 'police',
'number': '110',
'label_de': 'Polizei',
'label_en': 'Police',
});
expect(number.type, EmergencyType.police);
expect(number.number, '110');
expect(number.labelDe, 'Polizei');
expect(number.labelEn, 'Police');
});
test('zwei inhaltsgleiche Instanzen sind gleich', () {
final a = EmergencyNumber.fromJson({
'type': 'general',
'number': '112',
'label_de': 'Notruf',
'label_en': 'Emergency',
});
final b = EmergencyNumber.fromJson({
'type': 'general',
'number': '112',
'label_de': 'Notruf',
'label_en': 'Emergency',
});
expect(a, equals(b));
expect(a.hashCode, equals(b.hashCode));
});
});
}
+26
View File
@@ -0,0 +1,26 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:emergency/models/emergency_type.dart';
void main() {
group('EmergencyType', () {
test('fromJson mappt alle bekannten Werte korrekt', () {
expect(EmergencyType.fromJson('general'), EmergencyType.general);
expect(EmergencyType.fromJson('police'), EmergencyType.police);
expect(EmergencyType.fromJson('fire'), EmergencyType.fire);
expect(EmergencyType.fromJson('ambulance'), EmergencyType.ambulance);
expect(EmergencyType.fromJson('sea_rescue'), EmergencyType.seaRescue);
expect(EmergencyType.fromJson('mountain_rescue'), EmergencyType.mountainRescue);
});
test('fromJson wirft FormatException bei unbekanntem Wert', () {
expect(() => EmergencyType.fromJson('unknown'), throwsFormatException);
});
test('toJson ist invers zu fromJson', () {
for (final type in EmergencyType.values) {
expect(EmergencyType.fromJson(type.toJson()), type);
}
});
});
}