718 lines
23 KiB
Dart
718 lines
23 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) {
|
|
final authService = ref.watch(authServiceProvider);
|
|
return Georgia511RoadAlertProvider(
|
|
getAuthToken: () => authService.getIdToken(),
|
|
);
|
|
});
|
|
|
|
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.configured : 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();
|
|
});
|
|
|
|
/// Tactical map display horizon in meters (default 25 km / ~15.5 miles).
|
|
/// Decoupled from audio warning proximity radius.
|
|
class TacticalAlertRadiusMetersNotifier extends StateNotifier<double> {
|
|
static const double defaultRadius = 25000.0;
|
|
TacticalAlertRadiusMetersNotifier() : super(defaultRadius);
|
|
|
|
void setRadius(double radiusMeters) {
|
|
state = radiusMeters;
|
|
}
|
|
}
|
|
|
|
final tacticalAlertRadiusMetersProvider =
|
|
StateNotifierProvider<TacticalAlertRadiusMetersNotifier, double>((ref) {
|
|
return TacticalAlertRadiusMetersNotifier();
|
|
});
|
|
|
|
/// Dashboard alert presentation threshold in meters (default 2 miles / ~3218.7 meters).
|
|
/// Decoupled from tactical map horizon (25 km) and audio warning proximity (400 m).
|
|
class DashboardAlertRadiusMetersNotifier extends StateNotifier<double> {
|
|
static const double defaultRadius = 3218.688; // 2.0 miles
|
|
DashboardAlertRadiusMetersNotifier() : super(defaultRadius);
|
|
|
|
void setRadius(double radiusMeters) {
|
|
state = radiusMeters;
|
|
}
|
|
}
|
|
|
|
final dashboardAlertRadiusMetersProvider =
|
|
StateNotifierProvider<DashboardAlertRadiusMetersNotifier, double>((ref) {
|
|
return DashboardAlertRadiusMetersNotifier();
|
|
});
|
|
|
|
/// Live pipeline diagnostics state notifier
|
|
class RoadAlertDiagnosticsNotifier extends StateNotifier<RoadAlertDiagnostics> {
|
|
RoadAlertDiagnosticsNotifier() : super(const RoadAlertDiagnostics());
|
|
|
|
void updateDiagnostics(RoadAlertDiagnostics diagnostics) {
|
|
state = diagnostics;
|
|
}
|
|
|
|
void updateStatus({
|
|
required RoadAlertProviderStatus status,
|
|
String? message,
|
|
String? error,
|
|
}) {
|
|
state = state.copyWith(
|
|
providerStatus: status,
|
|
statusMessage: message,
|
|
lastError: error,
|
|
);
|
|
}
|
|
}
|
|
|
|
final roadAlertDiagnosticsProvider =
|
|
StateNotifierProvider<RoadAlertDiagnosticsNotifier, RoadAlertDiagnostics>((ref) {
|
|
return RoadAlertDiagnosticsNotifier();
|
|
});
|
|
|