mirror of
https://github.com/FoggedLens/deflock-app.git
synced 2026-07-05 04:07:57 +02:00
lot of changes, got rid of custom cache stuff, now stepping in the way of http fetch instead of screwing with flutter map.
This commit is contained in:
@@ -144,4 +144,9 @@ class MapDataProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear any queued tile requests (call when map view changes significantly)
|
||||
void clearTileQueue() {
|
||||
clearOSMTileQueue();
|
||||
}
|
||||
}
|
||||
@@ -9,15 +9,10 @@ import '../network_status.dart';
|
||||
/// Global semaphore to limit simultaneous tile fetches
|
||||
final _tileFetchSemaphore = _SimpleSemaphore(4); // Max 4 concurrent
|
||||
|
||||
/// Cancellation token to invalidate all pending requests
|
||||
int _globalCancelToken = 0;
|
||||
|
||||
/// Clear queued tile requests and cancel all retries
|
||||
/// Clear queued tile requests when map view changes significantly
|
||||
void clearOSMTileQueue() {
|
||||
final oldToken = _globalCancelToken;
|
||||
_globalCancelToken++; // Invalidate all pending requests and retries
|
||||
final clearedCount = _tileFetchSemaphore.clearQueue();
|
||||
debugPrint('[OSMTiles] Cancel token: $oldToken -> $_globalCancelToken, cleared $clearedCount queued');
|
||||
debugPrint('[OSMTiles] Cleared $clearedCount queued tile requests');
|
||||
}
|
||||
|
||||
/// Fetches a tile from OSM, with in-memory retries/backoff, and global concurrency limit.
|
||||
@@ -37,17 +32,7 @@ Future<List<int>> fetchOSMTile({
|
||||
kTileFetchThirdDelayMs + random.nextInt(kTileFetchJitter3Ms),
|
||||
];
|
||||
|
||||
// Remember the cancel token when we start this request
|
||||
final requestCancelToken = _globalCancelToken;
|
||||
print('[fetchOSMTile] START $z/$x/$y with token $requestCancelToken (global: $_globalCancelToken)');
|
||||
|
||||
while (true) {
|
||||
// Check if this request was cancelled
|
||||
if (requestCancelToken != _globalCancelToken) {
|
||||
print('[fetchOSMTile] CANCELLED $z/$x/$y (token: $requestCancelToken vs $_globalCancelToken)');
|
||||
throw Exception('Tile request cancelled');
|
||||
}
|
||||
|
||||
await _tileFetchSemaphore.acquire();
|
||||
try {
|
||||
print('[fetchOSMTile] FETCH $z/$x/$y');
|
||||
@@ -55,12 +40,6 @@ Future<List<int>> fetchOSMTile({
|
||||
final resp = await http.get(Uri.parse(url));
|
||||
print('[fetchOSMTile] HTTP ${resp.statusCode} for $z/$x/$y, length=${resp.bodyBytes.length}');
|
||||
|
||||
// Check cancellation after HTTP request completes - this is the key check!
|
||||
if (requestCancelToken != _globalCancelToken) {
|
||||
print('[fetchOSMTile] CANCELLED $z/$x/$y after HTTP (token: $requestCancelToken vs $_globalCancelToken)');
|
||||
throw Exception('Tile request cancelled');
|
||||
}
|
||||
|
||||
if (resp.statusCode == 200 && resp.bodyBytes.isNotEmpty) {
|
||||
print('[fetchOSMTile] SUCCESS $z/$x/$y');
|
||||
NetworkStatus.instance.reportOsmTileSuccess();
|
||||
@@ -71,11 +50,6 @@ Future<List<int>> fetchOSMTile({
|
||||
throw HttpException('Failed to fetch tile $z/$x/$y: status ${resp.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
// Don't retry cancelled requests
|
||||
if (e.toString().contains('cancelled')) {
|
||||
rethrow;
|
||||
}
|
||||
|
||||
print('[fetchOSMTile] Exception $z/$x/$y: $e');
|
||||
|
||||
// Report network issues on connection errors
|
||||
@@ -92,19 +66,7 @@ Future<List<int>> fetchOSMTile({
|
||||
|
||||
final delay = delays[attempt - 1].clamp(0, 60000);
|
||||
print("[fetchOSMTile] Attempt $attempt for $z/$x/$y failed: $e. Retrying in ${delay}ms.");
|
||||
|
||||
// Check cancellation before and after delay
|
||||
if (requestCancelToken != _globalCancelToken) {
|
||||
print('[fetchOSMTile] CANCELLED $z/$x/$y before retry');
|
||||
throw Exception('Tile request cancelled');
|
||||
}
|
||||
|
||||
await Future.delayed(Duration(milliseconds: delay));
|
||||
|
||||
if (requestCancelToken != _globalCancelToken) {
|
||||
print('[fetchOSMTile] CANCELLED $z/$x/$y after retry delay');
|
||||
throw Exception('Tile request cancelled');
|
||||
}
|
||||
} finally {
|
||||
_tileFetchSemaphore.release();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'map_data_provider.dart';
|
||||
|
||||
/// Simple HTTP client that routes tile requests through the centralized MapDataProvider.
|
||||
/// This ensures all tile fetching (offline/online routing, retries, etc.) is in one place.
|
||||
class SimpleTileHttpClient extends http.BaseClient {
|
||||
final http.Client _inner = http.Client();
|
||||
final MapDataProvider _mapDataProvider = MapDataProvider();
|
||||
|
||||
@override
|
||||
Future<http.StreamedResponse> send(http.BaseRequest request) async {
|
||||
// Only intercept tile requests to OSM
|
||||
if (request.url.host == 'tile.openstreetmap.org') {
|
||||
return _handleTileRequest(request);
|
||||
}
|
||||
|
||||
// Pass through all other requests
|
||||
return _inner.send(request);
|
||||
}
|
||||
|
||||
Future<http.StreamedResponse> _handleTileRequest(http.BaseRequest request) async {
|
||||
final pathSegments = request.url.pathSegments;
|
||||
|
||||
// Parse z/x/y from URL like: /15/5242/12666.png
|
||||
if (pathSegments.length == 3) {
|
||||
final z = int.tryParse(pathSegments[0]);
|
||||
final x = int.tryParse(pathSegments[1]);
|
||||
final yPng = pathSegments[2];
|
||||
final y = int.tryParse(yPng.replaceAll('.png', ''));
|
||||
|
||||
if (z != null && x != null && y != null) {
|
||||
return _getTile(z, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Malformed tile URL - pass through to OSM
|
||||
return _inner.send(request);
|
||||
}
|
||||
|
||||
Future<http.StreamedResponse> _getTile(int z, int x, int y) async {
|
||||
try {
|
||||
// Use centralized tile fetching from MapDataProvider
|
||||
final tileBytes = await _mapDataProvider.getTile(z: z, x: x, y: y);
|
||||
debugPrint('[SimpleTileService] Serving tile $z/$x/$y via MapDataProvider');
|
||||
|
||||
return http.StreamedResponse(
|
||||
Stream.value(tileBytes),
|
||||
200,
|
||||
headers: {
|
||||
'Content-Type': 'image/png',
|
||||
'Cache-Control': 'public, max-age=604800', // 1 week cache
|
||||
'Expires': _httpDateFormat(DateTime.now().add(Duration(days: 7))),
|
||||
'Last-Modified': _httpDateFormat(DateTime.now().subtract(Duration(hours: 1))),
|
||||
},
|
||||
);
|
||||
|
||||
} catch (e) {
|
||||
debugPrint('[SimpleTileService] Tile fetch failed for $z/$x/$y: $e');
|
||||
|
||||
// Return 404 for any failure - let flutter_map handle gracefully
|
||||
return http.StreamedResponse(
|
||||
Stream.value(<int>[]),
|
||||
404,
|
||||
reasonPhrase: 'Tile not available: $e',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear any queued tile requests when map view changes
|
||||
void clearTileQueue() {
|
||||
_mapDataProvider.clearTileQueue();
|
||||
}
|
||||
|
||||
/// Format date for HTTP headers (RFC 7231)
|
||||
String _httpDateFormat(DateTime date) {
|
||||
final utc = date.toUtc();
|
||||
final weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
final months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
||||
|
||||
final weekday = weekdays[utc.weekday - 1];
|
||||
final day = utc.day.toString().padLeft(2, '0');
|
||||
final month = months[utc.month - 1];
|
||||
final year = utc.year;
|
||||
final hour = utc.hour.toString().padLeft(2, '0');
|
||||
final minute = utc.minute.toString().padLeft(2, '0');
|
||||
final second = utc.second.toString().padLeft(2, '0');
|
||||
|
||||
return '$weekday, $day $month $year $hour:$minute:$second GMT';
|
||||
}
|
||||
|
||||
@override
|
||||
void close() {
|
||||
_inner.close();
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user