import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'road_alert_models.dart'; import 'road_alert_provider_interface.dart'; import 'tomtom_road_alert_provider.dart'; import 'community_traffic_provider.dart'; import 'mock_road_alert_provider.dart'; import 'osm_road_alert_provider.dart'; import 'swarm_road_alert_service.dart'; import 'swarm_road_alert_provider.dart'; import 'here_road_alert_provider.dart'; import 'waze_road_alert_provider.dart'; import 'waze_osint_road_alert_provider.dart'; import 'zyla_road_alert_provider.dart'; import 'ga511_road_alert_provider.dart'; import '../auth/auth_providers.dart'; final zylaProviderInstance = Provider((ref) { final authService = ref.watch(authServiceProvider); return ZylaRoadAlertProvider( getAuthToken: () => authService.getIdToken(), ); }); final ga511ProviderInstance = Provider((ref) { final authService = ref.watch(authServiceProvider); return Georgia511RoadAlertProvider( getAuthToken: () => authService.getIdToken(), ); }); final wazeProviderInstance = Provider((ref) { return WazeRoadAlertProvider(); }); final wazeOsintProviderInstance = Provider((ref) { return WazeOsintRoadAlertProvider(); }); final swarmRoadAlertServiceProvider = Provider((ref) { return SwarmRoadAlertService( minReportInterval: const Duration(seconds: 15), ); }); final swarmRoadAlertProviderInstance = Provider((ref) { return SwarmRoadAlertProvider(ref.watch(swarmRoadAlertServiceProvider)); }); final hereProviderInstance = Provider((ref) { return HereRoadAlertProvider(); }); final osmRoadAlertProviderInstance = Provider((ref) { return OsmRoadAlertProvider(); }); final tomtomProviderInstance = Provider((ref) { return TomTomRoadAlertProvider(); }); final communityProviderInstance = Provider((ref) { final endpoint = ref.watch(communityEndpointUrlProvider); return CommunityTrafficRoadAlertProvider(endpointUrl: endpoint); }); final mockProviderInstance = Provider((ref) { return MockRoadAlertProvider(); }); // Master Enable class RoadAlertsEnabledNotifier extends StateNotifier { static const _key = 'road_alerts_enabled'; RoadAlertsEnabledNotifier() : super(true) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; state = prefs.getBool(_key) ?? true; } Future setEnabled(bool enabled) async { state = enabled; final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_key, enabled); } } final roadAlertsEnabledProvider = StateNotifierProvider((ref) { return RoadAlertsEnabledNotifier(); }); // Audio Alerts Toggle class AudioAlertsEnabledNotifier extends StateNotifier { static const _key = 'audio_alerts_enabled'; AudioAlertsEnabledNotifier() : super(true) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; state = prefs.getBool(_key) ?? true; } Future setEnabled(bool enabled) async { state = enabled; final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_key, enabled); } } final audioAlertsEnabledProvider = StateNotifierProvider((ref) { return AudioAlertsEnabledNotifier(); }); // Alert Radius in Meters class AlertRadiusMetersNotifier extends StateNotifier { static const _key = 'alert_radius_meters'; AlertRadiusMetersNotifier() : super(804.7) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; state = prefs.getDouble(_key) ?? 804.7; } Future setRadius(double radiusMeters) async { state = radiusMeters; final prefs = await SharedPreferences.getInstance(); await prefs.setDouble(_key, radiusMeters); } } final alertRadiusMetersProvider = StateNotifierProvider((ref) { return AlertRadiusMetersNotifier(); }); // Category Filtering Notifier class EnabledCategoriesNotifier extends StateNotifier> { EnabledCategoriesNotifier() : super({ RoadAlertCategory.police, RoadAlertCategory.speedCamera, RoadAlertCategory.accident, RoadAlertCategory.hazard, RoadAlertCategory.pothole, RoadAlertCategory.debris, RoadAlertCategory.disabledVehicle, RoadAlertCategory.roadClosure, RoadAlertCategory.construction, RoadAlertCategory.traffic, RoadAlertCategory.weatherHazard, RoadAlertCategory.other, }); void toggleCategory(RoadAlertCategory category, bool enabled) { final current = Set.from(state); if (enabled) { current.add(category); } else { current.remove(category); } state = current; } } final enabledCategoriesProvider = StateNotifierProvider>((ref) { return EnabledCategoriesNotifier(); }); // TomTom API Key class TomTomApiKeyNotifier extends StateNotifier { static const _key = 'tomtom_api_key'; TomTomApiKeyNotifier() : super('') { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; state = prefs.getString(_key) ?? ''; } Future setApiKey(String apiKey) async { state = apiKey.trim(); final prefs = await SharedPreferences.getInstance(); await prefs.setString(_key, state); } } final tomtomApiKeyProvider = StateNotifierProvider((ref) { return TomTomApiKeyNotifier(); }); // Community Traffic API Key class CommunityApiKeyNotifier extends StateNotifier { static const _key = 'community_api_key'; static const String defaultKey = 'wz_live_RqKTtZ10oK3db9hH-ypQhD0tOTLn199q'; CommunityApiKeyNotifier() : super(defaultKey) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; state = prefs.getString(_key) ?? defaultKey; } Future setApiKey(String apiKey) async { state = apiKey.trim().isEmpty ? defaultKey : apiKey.trim(); final prefs = await SharedPreferences.getInstance(); await prefs.setString(_key, state); } } final communityApiKeyProvider = StateNotifierProvider((ref) { return CommunityApiKeyNotifier(); }); // Custom Community Endpoint URL class CommunityEndpointUrlNotifier extends StateNotifier { static const _key = 'community_endpoint_url'; CommunityEndpointUrlNotifier() : super('') { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; state = prefs.getString(_key) ?? ''; } Future setEndpoint(String url) async { state = url.trim(); final prefs = await SharedPreferences.getInstance(); await prefs.setString(_key, state); } } final communityEndpointUrlProvider = StateNotifierProvider((ref) { return CommunityEndpointUrlNotifier(); }); final tomtomStatusProvider = StateProvider((ref) { final key = ref.watch(tomtomApiKeyProvider); return key.isEmpty ? RoadAlertProviderStatus.notConfigured : RoadAlertProviderStatus.configured; }); final communityStatusProvider = StateProvider((ref) { final key = ref.watch(communityApiKeyProvider); return key.isEmpty ? RoadAlertProviderStatus.notConfigured : RoadAlertProviderStatus.configured; }); // US Offline Speed Cameras Toggle class UsSpeedCamerasEnabledNotifier extends StateNotifier { static const _key = 'us_speed_cameras_enabled'; UsSpeedCamerasEnabledNotifier() : super(true) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; state = prefs.getBool(_key) ?? true; } Future setEnabled(bool enabled) async { state = enabled; final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_key, enabled); } } final usSpeedCamerasEnabledProvider = StateNotifierProvider((ref) { return UsSpeedCamerasEnabledNotifier(); }); final osmSpeedCameraStatusProvider = StateProvider((ref) { final enabled = ref.watch(usSpeedCamerasEnabledProvider); return enabled ? RoadAlertProviderStatus.connected : RoadAlertProviderStatus.notConfigured; }); // MotorSignal Rider Swarm Alerts Toggle class SwarmAlertsEnabledNotifier extends StateNotifier { static const _key = 'swarm_alerts_enabled'; SwarmAlertsEnabledNotifier() : super(true) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; state = prefs.getBool(_key) ?? true; } Future setEnabled(bool enabled) async { state = enabled; final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_key, enabled); } } final swarmAlertsEnabledProvider = StateNotifierProvider((ref) { return SwarmAlertsEnabledNotifier(); }); final swarmStatusProvider = StateProvider((ref) { final enabled = ref.watch(swarmAlertsEnabledProvider); return enabled ? RoadAlertProviderStatus.connected : RoadAlertProviderStatus.notConfigured; }); // HERE Technologies Safety Cameras API Key class HereApiKeyNotifier extends StateNotifier { static const _key = 'here_api_key'; HereApiKeyNotifier() : super('') { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; final saved = prefs.getString(_key); if (saved != null) { state = saved; } } Future setApiKey(String apiKey) async { final val = apiKey.trim(); state = val; final prefs = await SharedPreferences.getInstance(); await prefs.setString(_key, val); if (mounted) { state = val; } } } final hereApiKeyProvider = StateNotifierProvider((ref) { return HereApiKeyNotifier(); }); // HERE Road Alerts Enabled Toggle class HereRoadAlertsEnabledNotifier extends StateNotifier { static const _key = 'here_alerts_enabled'; HereRoadAlertsEnabledNotifier() : super(true) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; final saved = prefs.getBool(_key); if (saved != null) { state = saved; } } Future setEnabled(bool enabled) async { state = enabled; final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_key, enabled); if (mounted) { state = enabled; } } } final hereRoadAlertsEnabledProvider = StateNotifierProvider((ref) { return HereRoadAlertsEnabledNotifier(); }); final hereStatusProvider = StateProvider((ref) { final key = ref.watch(hereApiKeyProvider); final enabled = ref.watch(hereRoadAlertsEnabledProvider); if (!enabled || key.isEmpty) return RoadAlertProviderStatus.notConfigured; return RoadAlertProviderStatus.configured; }); // Custom Waze Alerts API Key class WazeApiKeyNotifier extends StateNotifier { static const _key = 'waze_api_key'; static const String defaultKey = String.fromEnvironment('WAZE_API_KEY', defaultValue: ''); WazeApiKeyNotifier() : super(defaultKey) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; final saved = prefs.getString(_key); if (saved != null) { state = saved; } } Future setApiKey(String apiKey) async { final val = apiKey.trim(); state = val; final prefs = await SharedPreferences.getInstance(); await prefs.setString(_key, val); if (mounted) { state = val; } } } final wazeApiKeyProvider = StateNotifierProvider((ref) { return WazeApiKeyNotifier(); }); // Waze Police Alerts Enabled Toggle class WazeRoadAlertsEnabledNotifier extends StateNotifier { static const _key = 'waze_alerts_enabled'; WazeRoadAlertsEnabledNotifier() : super(true) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; final saved = prefs.getBool(_key); if (saved != null) { state = saved; } } Future setEnabled(bool enabled) async { state = enabled; final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_key, enabled); if (mounted) { state = enabled; } } } final wazeRoadAlertsEnabledProvider = StateNotifierProvider((ref) { return WazeRoadAlertsEnabledNotifier(); }); final wazeStatusProvider = StateProvider((ref) { final key = ref.watch(wazeApiKeyProvider); final enabled = ref.watch(wazeRoadAlertsEnabledProvider); if (!enabled || key.isEmpty) return RoadAlertProviderStatus.notConfigured; return RoadAlertProviderStatus.configured; }); // Experimental Waze OSINT Community Adapter Toggle (Disabled by default) class WazeOsintEnabledNotifier extends StateNotifier { static const _key = 'waze_osint_enabled'; WazeOsintEnabledNotifier() : super(false) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; final saved = prefs.getBool(_key); if (saved != null) { state = saved; } } Future setEnabled(bool enabled) async { state = enabled; final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_key, enabled); if (mounted) { state = enabled; } } } final wazeOsintEnabledProvider = StateNotifierProvider((ref) { return WazeOsintEnabledNotifier(); }); final wazeOsintStatusProvider = StateProvider((ref) { final enabled = ref.watch(wazeOsintEnabledProvider); return enabled ? RoadAlertProviderStatus.connected : RoadAlertProviderStatus.notConfigured; }); // Zyla Waze Data API #4814 Toggle (Disabled by default) class ZylaRoadAlertsEnabledNotifier extends StateNotifier { static const _key = 'zyla_alerts_enabled'; ZylaRoadAlertsEnabledNotifier() : super(false) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; final saved = prefs.getBool(_key); if (saved != null) { state = saved; } } Future setEnabled(bool enabled) async { state = enabled; final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_key, enabled); if (mounted) { state = enabled; } } } final zylaRoadAlertsEnabledProvider = StateNotifierProvider((ref) { return ZylaRoadAlertsEnabledNotifier(); }); final zylaStatusProvider = StateProvider((ref) { final enabled = ref.watch(zylaRoadAlertsEnabledProvider); return enabled ? RoadAlertProviderStatus.connected : RoadAlertProviderStatus.notConfigured; }); // Georgia 511 Official Traffic Feed Toggle (Disabled by default) class Georgia511AlertsEnabledNotifier extends StateNotifier { static const _key = 'ga511_alerts_enabled'; Georgia511AlertsEnabledNotifier() : super(false) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; final saved = prefs.getBool(_key); if (saved != null) { state = saved; } } Future setEnabled(bool enabled) async { state = enabled; final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_key, enabled); if (mounted) { state = enabled; } } } final ga511AlertsEnabledProvider = StateNotifierProvider((ref) { return Georgia511AlertsEnabledNotifier(); }); final ga511StatusProvider = StateProvider((ref) { final enabled = ref.watch(ga511AlertsEnabledProvider); return enabled ? RoadAlertProviderStatus.configured : RoadAlertProviderStatus.notConfigured; }); // Active Providers List final activeRoadAlertProviders = Provider>((ref) { final swarmEnabled = ref.watch(swarmAlertsEnabledProvider); final wazeOsintEnabled = ref.watch(wazeOsintEnabledProvider); final zylaEnabled = ref.watch(zylaRoadAlertsEnabledProvider); final ga511Enabled = ref.watch(ga511AlertsEnabledProvider); final usCamerasEnabled = ref.watch(usSpeedCamerasEnabledProvider); final tomtomKey = ref.watch(tomtomApiKeyProvider); final communityKey = ref.watch(communityApiKeyProvider); final hereKey = ref.watch(hereApiKeyProvider); final hereEnabled = ref.watch(hereRoadAlertsEnabledProvider); final wazeKey = ref.watch(wazeApiKeyProvider); final wazeEnabled = ref.watch(wazeRoadAlertsEnabledProvider); final list = []; if (swarmEnabled) { list.add(ref.watch(swarmRoadAlertProviderInstance)); } if (wazeOsintEnabled) { list.add(ref.watch(wazeOsintProviderInstance)); } if (zylaEnabled) { list.add(ref.watch(zylaProviderInstance)); } if (ga511Enabled) { list.add(ref.watch(ga511ProviderInstance)); } if (wazeEnabled && wazeKey.isNotEmpty) { list.add(ref.watch(wazeProviderInstance)); } if (usCamerasEnabled) { list.add(ref.watch(osmRoadAlertProviderInstance)); } if (hereEnabled && hereKey.isNotEmpty) { list.add(ref.watch(hereProviderInstance)); } if (tomtomKey.isNotEmpty) { list.add(ref.watch(tomtomProviderInstance)); } if (communityKey.isNotEmpty) { list.add(ref.watch(communityProviderInstance)); } return list; }); // Phase 4 Ergonomics & Compliance Providers /// Audio alert playback style: spoken voice vs. intercom-friendly tactical tone chirps. enum RoadAlertAudioMode { voice, tacticalTone, } class RoadAlertAudioModeNotifier extends StateNotifier { static const _key = 'road_alert_audio_mode'; RoadAlertAudioModeNotifier() : super(RoadAlertAudioMode.voice) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; final saved = prefs.getString(_key); if (saved != null) { state = saved == 'tacticalTone' ? RoadAlertAudioMode.tacticalTone : RoadAlertAudioMode.voice; } } Future setMode(RoadAlertAudioMode mode) async { state = mode; final prefs = await SharedPreferences.getInstance(); await prefs.setString(_key, mode.name); if (mounted) state = mode; } } final roadAlertAudioModeProvider = StateNotifierProvider((ref) { return RoadAlertAudioModeNotifier(); }); /// Cornering Safety: suppresses audio announcements while bike lean angle > 15 degrees. class LeanInterlockEnabledNotifier extends StateNotifier { static const _key = 'lean_interlock_enabled'; LeanInterlockEnabledNotifier() : super(true) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; final saved = prefs.getBool(_key); if (saved != null) state = saved; } Future setEnabled(bool enabled) async { state = enabled; final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_key, enabled); if (mounted) state = enabled; } } final leanInterlockEnabledProvider = StateNotifierProvider((ref) { return LeanInterlockEnabledNotifier(); }); /// Jurisdictional Geofencing: automatically adapts speed camera warnings per country regulations. class JurisdictionComplianceEnabledNotifier extends StateNotifier { static const _key = 'jurisdiction_compliance_enabled'; JurisdictionComplianceEnabledNotifier() : super(true) { _load(); } Future _load() async { final prefs = await SharedPreferences.getInstance(); if (!mounted) return; final saved = prefs.getBool(_key); if (saved != null) state = saved; } Future setEnabled(bool enabled) async { state = enabled; final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_key, enabled); if (mounted) state = enabled; } } final jurisdictionComplianceEnabledProvider = StateNotifierProvider((ref) { return JurisdictionComplianceEnabledNotifier(); });