MotorSignal-App/lib/services/alerts/road_alert_providers.dart

657 lines
21 KiB
Dart

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<ZylaRoadAlertProvider>((ref) {
final authService = ref.watch(authServiceProvider);
return ZylaRoadAlertProvider(
getAuthToken: () => authService.getIdToken(),
);
});
final ga511ProviderInstance = Provider<Georgia511RoadAlertProvider>((ref) {
return Georgia511RoadAlertProvider();
});
final wazeProviderInstance = Provider<WazeRoadAlertProvider>((ref) {
return WazeRoadAlertProvider();
});
final wazeOsintProviderInstance = Provider<WazeOsintRoadAlertProvider>((ref) {
return WazeOsintRoadAlertProvider();
});
final swarmRoadAlertServiceProvider = Provider<SwarmRoadAlertService>((ref) {
return SwarmRoadAlertService(
minReportInterval: const Duration(seconds: 15),
);
});
final swarmRoadAlertProviderInstance = Provider<SwarmRoadAlertProvider>((ref) {
return SwarmRoadAlertProvider(ref.watch(swarmRoadAlertServiceProvider));
});
final hereProviderInstance = Provider<HereRoadAlertProvider>((ref) {
return HereRoadAlertProvider();
});
final osmRoadAlertProviderInstance = Provider<OsmRoadAlertProvider>((ref) {
return OsmRoadAlertProvider();
});
final tomtomProviderInstance = Provider<TomTomRoadAlertProvider>((ref) {
return TomTomRoadAlertProvider();
});
final communityProviderInstance = Provider<CommunityTrafficRoadAlertProvider>((ref) {
final endpoint = ref.watch(communityEndpointUrlProvider);
return CommunityTrafficRoadAlertProvider(endpointUrl: endpoint);
});
final mockProviderInstance = Provider<MockRoadAlertProvider>((ref) {
return MockRoadAlertProvider();
});
// Master Enable
class RoadAlertsEnabledNotifier extends StateNotifier<bool> {
static const _key = 'road_alerts_enabled';
RoadAlertsEnabledNotifier() : super(true) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
state = prefs.getBool(_key) ?? true;
}
Future<void> setEnabled(bool enabled) async {
state = enabled;
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_key, enabled);
}
}
final roadAlertsEnabledProvider =
StateNotifierProvider<RoadAlertsEnabledNotifier, bool>((ref) {
return RoadAlertsEnabledNotifier();
});
// Audio Alerts Toggle
class AudioAlertsEnabledNotifier extends StateNotifier<bool> {
static const _key = 'audio_alerts_enabled';
AudioAlertsEnabledNotifier() : super(true) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
state = prefs.getBool(_key) ?? true;
}
Future<void> setEnabled(bool enabled) async {
state = enabled;
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_key, enabled);
}
}
final audioAlertsEnabledProvider =
StateNotifierProvider<AudioAlertsEnabledNotifier, bool>((ref) {
return AudioAlertsEnabledNotifier();
});
// Alert Radius in Meters
class AlertRadiusMetersNotifier extends StateNotifier<double> {
static const _key = 'alert_radius_meters';
AlertRadiusMetersNotifier() : super(804.7) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
state = prefs.getDouble(_key) ?? 804.7;
}
Future<void> setRadius(double radiusMeters) async {
state = radiusMeters;
final prefs = await SharedPreferences.getInstance();
await prefs.setDouble(_key, radiusMeters);
}
}
final alertRadiusMetersProvider =
StateNotifierProvider<AlertRadiusMetersNotifier, double>((ref) {
return AlertRadiusMetersNotifier();
});
// Category Filtering Notifier
class EnabledCategoriesNotifier extends StateNotifier<Set<RoadAlertCategory>> {
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<RoadAlertCategory>.from(state);
if (enabled) {
current.add(category);
} else {
current.remove(category);
}
state = current;
}
}
final enabledCategoriesProvider =
StateNotifierProvider<EnabledCategoriesNotifier, Set<RoadAlertCategory>>((ref) {
return EnabledCategoriesNotifier();
});
// TomTom API Key
class TomTomApiKeyNotifier extends StateNotifier<String> {
static const _key = 'tomtom_api_key';
TomTomApiKeyNotifier() : super('') {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
state = prefs.getString(_key) ?? '';
}
Future<void> setApiKey(String apiKey) async {
state = apiKey.trim();
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_key, state);
}
}
final tomtomApiKeyProvider =
StateNotifierProvider<TomTomApiKeyNotifier, String>((ref) {
return TomTomApiKeyNotifier();
});
// Community Traffic API Key
class CommunityApiKeyNotifier extends StateNotifier<String> {
static const _key = 'community_api_key';
static const String defaultKey = 'wz_live_RqKTtZ10oK3db9hH-ypQhD0tOTLn199q';
CommunityApiKeyNotifier() : super(defaultKey) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
state = prefs.getString(_key) ?? defaultKey;
}
Future<void> setApiKey(String apiKey) async {
state = apiKey.trim().isEmpty ? defaultKey : apiKey.trim();
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_key, state);
}
}
final communityApiKeyProvider =
StateNotifierProvider<CommunityApiKeyNotifier, String>((ref) {
return CommunityApiKeyNotifier();
});
// Custom Community Endpoint URL
class CommunityEndpointUrlNotifier extends StateNotifier<String> {
static const _key = 'community_endpoint_url';
CommunityEndpointUrlNotifier() : super('') {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
state = prefs.getString(_key) ?? '';
}
Future<void> setEndpoint(String url) async {
state = url.trim();
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_key, state);
}
}
final communityEndpointUrlProvider =
StateNotifierProvider<CommunityEndpointUrlNotifier, String>((ref) {
return CommunityEndpointUrlNotifier();
});
final tomtomStatusProvider = StateProvider<RoadAlertProviderStatus>((ref) {
final key = ref.watch(tomtomApiKeyProvider);
return key.isEmpty ? RoadAlertProviderStatus.notConfigured : RoadAlertProviderStatus.configured;
});
final communityStatusProvider = StateProvider<RoadAlertProviderStatus>((ref) {
final key = ref.watch(communityApiKeyProvider);
return key.isEmpty ? RoadAlertProviderStatus.notConfigured : RoadAlertProviderStatus.configured;
});
// US Offline Speed Cameras Toggle
class UsSpeedCamerasEnabledNotifier extends StateNotifier<bool> {
static const _key = 'us_speed_cameras_enabled';
UsSpeedCamerasEnabledNotifier() : super(true) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
state = prefs.getBool(_key) ?? true;
}
Future<void> setEnabled(bool enabled) async {
state = enabled;
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_key, enabled);
}
}
final usSpeedCamerasEnabledProvider =
StateNotifierProvider<UsSpeedCamerasEnabledNotifier, bool>((ref) {
return UsSpeedCamerasEnabledNotifier();
});
final osmSpeedCameraStatusProvider = StateProvider<RoadAlertProviderStatus>((ref) {
final enabled = ref.watch(usSpeedCamerasEnabledProvider);
return enabled ? RoadAlertProviderStatus.connected : RoadAlertProviderStatus.notConfigured;
});
// MotorSignal Rider Swarm Alerts Toggle
class SwarmAlertsEnabledNotifier extends StateNotifier<bool> {
static const _key = 'swarm_alerts_enabled';
SwarmAlertsEnabledNotifier() : super(true) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
state = prefs.getBool(_key) ?? true;
}
Future<void> setEnabled(bool enabled) async {
state = enabled;
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_key, enabled);
}
}
final swarmAlertsEnabledProvider =
StateNotifierProvider<SwarmAlertsEnabledNotifier, bool>((ref) {
return SwarmAlertsEnabledNotifier();
});
final swarmStatusProvider = StateProvider<RoadAlertProviderStatus>((ref) {
final enabled = ref.watch(swarmAlertsEnabledProvider);
return enabled ? RoadAlertProviderStatus.connected : RoadAlertProviderStatus.notConfigured;
});
// HERE Technologies Safety Cameras API Key
class HereApiKeyNotifier extends StateNotifier<String> {
static const _key = 'here_api_key';
HereApiKeyNotifier() : super('') {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
final saved = prefs.getString(_key);
if (saved != null) {
state = saved;
}
}
Future<void> 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<HereApiKeyNotifier, String>((ref) {
return HereApiKeyNotifier();
});
// HERE Road Alerts Enabled Toggle
class HereRoadAlertsEnabledNotifier extends StateNotifier<bool> {
static const _key = 'here_alerts_enabled';
HereRoadAlertsEnabledNotifier() : super(true) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
final saved = prefs.getBool(_key);
if (saved != null) {
state = saved;
}
}
Future<void> setEnabled(bool enabled) async {
state = enabled;
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_key, enabled);
if (mounted) {
state = enabled;
}
}
}
final hereRoadAlertsEnabledProvider =
StateNotifierProvider<HereRoadAlertsEnabledNotifier, bool>((ref) {
return HereRoadAlertsEnabledNotifier();
});
final hereStatusProvider = StateProvider<RoadAlertProviderStatus>((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<String> {
static const _key = 'waze_api_key';
static const String defaultKey = String.fromEnvironment('WAZE_API_KEY', defaultValue: '');
WazeApiKeyNotifier() : super(defaultKey) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
final saved = prefs.getString(_key);
if (saved != null) {
state = saved;
}
}
Future<void> 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<WazeApiKeyNotifier, String>((ref) {
return WazeApiKeyNotifier();
});
// Waze Police Alerts Enabled Toggle
class WazeRoadAlertsEnabledNotifier extends StateNotifier<bool> {
static const _key = 'waze_alerts_enabled';
WazeRoadAlertsEnabledNotifier() : super(true) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
final saved = prefs.getBool(_key);
if (saved != null) {
state = saved;
}
}
Future<void> setEnabled(bool enabled) async {
state = enabled;
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_key, enabled);
if (mounted) {
state = enabled;
}
}
}
final wazeRoadAlertsEnabledProvider =
StateNotifierProvider<WazeRoadAlertsEnabledNotifier, bool>((ref) {
return WazeRoadAlertsEnabledNotifier();
});
final wazeStatusProvider = StateProvider<RoadAlertProviderStatus>((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<bool> {
static const _key = 'waze_osint_enabled';
WazeOsintEnabledNotifier() : super(false) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
final saved = prefs.getBool(_key);
if (saved != null) {
state = saved;
}
}
Future<void> setEnabled(bool enabled) async {
state = enabled;
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_key, enabled);
if (mounted) {
state = enabled;
}
}
}
final wazeOsintEnabledProvider =
StateNotifierProvider<WazeOsintEnabledNotifier, bool>((ref) {
return WazeOsintEnabledNotifier();
});
final wazeOsintStatusProvider = StateProvider<RoadAlertProviderStatus>((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<bool> {
static const _key = 'zyla_alerts_enabled';
ZylaRoadAlertsEnabledNotifier() : super(false) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
final saved = prefs.getBool(_key);
if (saved != null) {
state = saved;
}
}
Future<void> setEnabled(bool enabled) async {
state = enabled;
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_key, enabled);
if (mounted) {
state = enabled;
}
}
}
final zylaRoadAlertsEnabledProvider =
StateNotifierProvider<ZylaRoadAlertsEnabledNotifier, bool>((ref) {
return ZylaRoadAlertsEnabledNotifier();
});
final zylaStatusProvider = StateProvider<RoadAlertProviderStatus>((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<bool> {
static const _key = 'ga511_alerts_enabled';
Georgia511AlertsEnabledNotifier() : super(false) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
final saved = prefs.getBool(_key);
if (saved != null) {
state = saved;
}
}
Future<void> setEnabled(bool enabled) async {
state = enabled;
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_key, enabled);
if (mounted) {
state = enabled;
}
}
}
final ga511AlertsEnabledProvider =
StateNotifierProvider<Georgia511AlertsEnabledNotifier, bool>((ref) {
return Georgia511AlertsEnabledNotifier();
});
final ga511StatusProvider = StateProvider<RoadAlertProviderStatus>((ref) {
final enabled = ref.watch(ga511AlertsEnabledProvider);
return enabled ? RoadAlertProviderStatus.connected : RoadAlertProviderStatus.notConfigured;
});
// Active Providers List
final activeRoadAlertProviders = Provider<List<RoadAlertProvider>>((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 = <RoadAlertProvider>[];
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<RoadAlertAudioMode> {
static const _key = 'road_alert_audio_mode';
RoadAlertAudioModeNotifier() : super(RoadAlertAudioMode.voice) {
_load();
}
Future<void> _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<void> setMode(RoadAlertAudioMode mode) async {
state = mode;
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_key, mode.name);
if (mounted) state = mode;
}
}
final roadAlertAudioModeProvider =
StateNotifierProvider<RoadAlertAudioModeNotifier, RoadAlertAudioMode>((ref) {
return RoadAlertAudioModeNotifier();
});
/// Cornering Safety: suppresses audio announcements while bike lean angle > 15 degrees.
class LeanInterlockEnabledNotifier extends StateNotifier<bool> {
static const _key = 'lean_interlock_enabled';
LeanInterlockEnabledNotifier() : super(true) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
final saved = prefs.getBool(_key);
if (saved != null) state = saved;
}
Future<void> setEnabled(bool enabled) async {
state = enabled;
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_key, enabled);
if (mounted) state = enabled;
}
}
final leanInterlockEnabledProvider =
StateNotifierProvider<LeanInterlockEnabledNotifier, bool>((ref) {
return LeanInterlockEnabledNotifier();
});
/// Jurisdictional Geofencing: automatically adapts speed camera warnings per country regulations.
class JurisdictionComplianceEnabledNotifier extends StateNotifier<bool> {
static const _key = 'jurisdiction_compliance_enabled';
JurisdictionComplianceEnabledNotifier() : super(true) {
_load();
}
Future<void> _load() async {
final prefs = await SharedPreferences.getInstance();
if (!mounted) return;
final saved = prefs.getBool(_key);
if (saved != null) state = saved;
}
Future<void> setEnabled(bool enabled) async {
state = enabled;
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_key, enabled);
if (mounted) state = enabled;
}
}
final jurisdictionComplianceEnabledProvider =
StateNotifierProvider<JurisdictionComplianceEnabledNotifier, bool>((ref) {
return JurisdictionComplianceEnabledNotifier();
});