import 'package:flutter/foundation.dart'; import 'package:geolocator/geolocator.dart'; class LocationService { static Future Function()? mockDeterminator; static Stream? mockStream; static Stream getPositionStream() { if (mockStream != null) { return mockStream!; } return Geolocator.getPositionStream( locationSettings: const LocationSettings( accuracy: LocationAccuracy.high, distanceFilter: 1, // update only when moved 1 meter ), ); } static Future determinePosition() async { if (mockDeterminator != null) { return mockDeterminator!(); } try { bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { debugPrint('Location services are disabled.'); return null; } LocationPermission permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { debugPrint('Location permission denied.'); return null; } } if (permission == LocationPermission.deniedForever) { debugPrint('Location permission denied forever.'); return null; } // Strategy 1: Try balanced/network-based location (fast, works indoors) try { final position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.medium, timeLimit: const Duration(seconds: 7), ); debugPrint( 'GPS balanced/indoor fix: ${position.latitude}, ${position.longitude} ' '(accuracy: ${position.accuracy.toStringAsFixed(1)}m)', ); return position; } catch (e) { debugPrint('getCurrentPosition balanced failed: $e. Trying high accuracy...'); } // Strategy 2: Try high accuracy (GPS satellites) with forceAndroidLocationManager to bypass cache try { final position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.high, forceAndroidLocationManager: true, timeLimit: const Duration(seconds: 10), ); debugPrint( 'GPS high fix: ${position.latitude}, ${position.longitude} ' '(accuracy: ${position.accuracy.toStringAsFixed(1)}m)', ); return position; } catch (e) { debugPrint('getCurrentPosition high failed: $e. Checking last known position...'); } // Strategy 3: Use last known position ONLY if it is super recent (within 2 minutes) final lastKnown = await Geolocator.getLastKnownPosition(); if (lastKnown != null) { final age = DateTime.now().difference(lastKnown.timestamp); if (age.inMinutes <= 2) { debugPrint( 'Using recent last-known position (${age.inSeconds}s old): ' '${lastKnown.latitude}, ${lastKnown.longitude}', ); return lastKnown; } debugPrint( 'Last-known position is too stale (${age.inMinutes}m old) — ' 'retrying low accuracy GPS fix...', ); } // Strategy 4: Last resort — bypass Google Fused Location and try low accuracy // forceAndroidLocationManager forces a real hardware GPS read if enabled try { final position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.low, forceAndroidLocationManager: true, timeLimit: const Duration(seconds: 5), ); debugPrint( 'GPS low fix via LocationManager: ${position.latitude}, ${position.longitude} ' '(accuracy: ${position.accuracy.toStringAsFixed(1)}m)', ); return position; } catch (e) { debugPrint('All GPS strategies failed: $e'); return null; } } catch (e) { debugPrint('Error in LocationService: $e'); return null; } } }