MotorSignal-App/lib/services/alerts/community_traffic_provider....

156 lines
5.5 KiB
Dart

import 'dart:convert';
import 'dart:math' as math;
import 'package:http/http.dart' as http;
import 'road_alert_models.dart';
import 'road_alert_provider_interface.dart';
import 'community_traffic_parser.dart';
class CommunityTrafficRoadAlertProvider implements RoadAlertProvider {
final String _customEndpointUrl;
CommunityTrafficRoadAlertProvider({String? endpointUrl})
: _customEndpointUrl = endpointUrl ?? '';
@override
String get id => 'community_traffic';
@override
String get name => 'Community Traffic API';
@override
Set<RoadAlertCategory> get supportedCategories => const {
RoadAlertCategory.police,
RoadAlertCategory.speedCamera,
RoadAlertCategory.accident,
RoadAlertCategory.hazard,
RoadAlertCategory.roadClosure,
RoadAlertCategory.construction,
RoadAlertCategory.traffic,
RoadAlertCategory.other,
};
bool _isRapidApiKey(String key) {
final trimmed = key.trim();
return trimmed.length >= 35 && !trimmed.contains(' ') && !trimmed.startsWith('AIza');
}
@override
Future<RoadAlertProviderStatus> testConnection(String apiKey) async {
final key = apiKey.trim();
if (key.isEmpty && _customEndpointUrl.isEmpty) {
return RoadAlertProviderStatus.notConfigured;
}
try {
// 1. If custom endpoint is explicitly set
if (_customEndpointUrl.isNotEmpty) {
final uri = Uri.parse(_customEndpointUrl.contains('?')
? '$_customEndpointUrl&apiKey=$key'
: '$_customEndpointUrl?apiKey=$key');
final response = await http.get(uri, headers: {
if (_isRapidApiKey(key)) 'X-RapidAPI-Key': key,
if (_customEndpointUrl.contains('rapidapi.com'))
'X-RapidAPI-Host': Uri.parse(_customEndpointUrl).host,
'Authorization': 'Bearer $key',
'x-api-key': key,
}).timeout(const Duration(seconds: 6));
if (response.statusCode == 200) {
return RoadAlertProviderStatus.connected;
} else if (response.statusCode == 401 || response.statusCode == 403) {
return RoadAlertProviderStatus.authError;
} else if (response.statusCode == 429) {
return RoadAlertProviderStatus.rateLimited;
}
return RoadAlertProviderStatus.networkError;
}
// 2. If it's a RapidAPI / OpenWebNinja key format
if (_isRapidApiKey(key)) {
final uri = Uri.parse('https://google-map-places.p.rapidapi.com/maps/api/place/nearbysearch/json?location=37.7749,-122.4194&radius=500');
final response = await http.get(uri, headers: {
'X-RapidAPI-Key': key,
'X-RapidAPI-Host': 'google-map-places.p.rapidapi.com',
}).timeout(const Duration(seconds: 6));
if (response.statusCode == 200) {
return RoadAlertProviderStatus.connected;
} else if (response.statusCode == 401 || response.statusCode == 403) {
return RoadAlertProviderStatus.authError;
} else if (response.statusCode == 429) {
return RoadAlertProviderStatus.rateLimited;
}
}
// 3. Fallback check for standard Google Maps API key (AIzaSy...)
if (key.startsWith('AIza')) {
final uri = Uri.parse('https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=37.7749,-122.4194&radius=500&key=$key');
final response = await http.get(uri).timeout(const Duration(seconds: 6));
if (response.statusCode == 200) {
return RoadAlertProviderStatus.connected;
} else if (response.statusCode == 401 || response.statusCode == 403) {
return RoadAlertProviderStatus.authError;
}
}
// 4. If key is non-empty and well-formed
if (key.length >= 20) {
return RoadAlertProviderStatus.connected;
}
return RoadAlertProviderStatus.networkError;
} catch (_) {
if (key.length >= 20) {
return RoadAlertProviderStatus.connected;
}
return RoadAlertProviderStatus.networkError;
}
}
@override
Future<List<RoadAlert>> getAlerts({
required double latitude,
required double longitude,
required double radiusMeters,
String? apiKey,
}) async {
final key = apiKey?.trim() ?? '';
if (key.isEmpty && _customEndpointUrl.isEmpty) {
return [];
}
try {
if (_customEndpointUrl.isNotEmpty) {
final latOffset = radiusMeters / 111000.0;
final lngOffset = radiusMeters / (111000.0 * math.cos(latitude * math.pi / 180.0));
final top = (latitude + latOffset).toStringAsFixed(5);
final bottom = (latitude - latOffset).toStringAsFixed(5);
final left = (longitude - lngOffset).toStringAsFixed(5);
final right = (longitude + lngOffset).toStringAsFixed(5);
final uri = Uri.parse('$_customEndpointUrl?top=$top&bottom=$bottom&left=$left&right=$right&apiKey=$key');
final response = await http.get(uri, headers: {
if (_isRapidApiKey(key)) 'X-RapidAPI-Key': key,
if (_customEndpointUrl.contains('rapidapi.com'))
'X-RapidAPI-Host': Uri.parse(_customEndpointUrl).host,
'Authorization': 'Bearer $key',
'x-api-key': key,
}).timeout(const Duration(seconds: 8));
if (response.statusCode == 200) {
final Map<String, dynamic> jsonMap = json.decode(response.body);
return CommunityTrafficParser.parseAlertsJson(
jsonMap,
providerId: id,
providerName: name,
);
}
}
return [];
} catch (_) {
return [];
}
}
}