Files
MobilApp/lib/services/phone_gps_source.dart
T

133 lines
4.4 KiB
Dart
Raw Normal View History

2026-05-10 02:31:27 +02:00
import 'dart:async';
import 'package:geolocator/geolocator.dart';
import 'location_source.dart';
/// Telefon beépített GPS-ét használó helymeghatározási forrás.
///
/// Android háttér-működéshez a [flutter_foreground_task] kezeli
/// az előtér-szolgáltatást (notification), ez az osztály csak
/// a Geolocator streamet konfigurálja.
class PhoneGpsSource implements LocationSource {
@override
String get displayName => 'Telefon GPS';
StreamController<SourcePosition>? _controller;
StreamSubscription<Position>? _positionSub;
/// Frissítési intervallum ms-ban.
final int intervalMs;
/// Minimális elmozdulás méterben új pont előtt.
final double distanceFilter;
/// Egymást követő sikertelen újraindítási kísérletek száma — véd a
/// végtelen, szoros hiba-ciklus ellen, ha valami tartósan elromlott.
int _retryCount = 0;
static const _maxRetries = 5;
Timer? _retryTimer;
2026-05-10 02:31:27 +02:00
PhoneGpsSource({
this.intervalMs = 1000,
this.distanceFilter = 1.0,
});
@override
bool get isAvailable => _controller != null && !(_controller!.isClosed);
@override
Stream<SourcePosition> get positionStream {
_controller = StreamController<SourcePosition>.broadcast();
_startListening();
return _controller!.stream;
}
Future<void> _startListening() async {
// Engedélyek ellenőrzése
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
}
if (permission == LocationPermission.deniedForever ||
permission == LocationPermission.denied) {
_controller?.addError(
Exception('Helymeghatározási engedély megtagadva. '
'Kérjük, engedélyezze a beállításokban.'),
);
return;
}
final settings = AndroidSettings(
accuracy: LocationAccuracy.high,
distanceFilter: distanceFilter.toInt(),
intervalDuration: Duration(milliseconds: intervalMs),
// Háttér-helymeghatározáshoz szükséges — a foreground_task
// notification biztosítja a jogszerű háttér-használatot.
foregroundNotificationConfig: const ForegroundNotificationConfig(
notificationText: 'Track rögzítése folyamatban',
notificationTitle: 'Terepi Segéd - Nyomvonal',
2026-05-10 02:31:27 +02:00
enableWakeLock: true,
),
);
await _positionSub?.cancel();
2026-05-10 02:31:27 +02:00
_positionSub = Geolocator.getPositionStream(
locationSettings: settings,
).listen((Position pos) {
_retryCount = 0;
_controller?.add(SourcePosition(
latitude: pos.latitude,
longitude: pos.longitude,
altitude: pos.altitude,
accuracy: pos.accuracy,
verticalAccuracy: pos.altitudeAccuracy,
speed: pos.speed,
heading: pos.heading,
timestamp: pos.timestamp,
source: displayName,
));
},
onError: (e) => _handleStreamError,
onDone: () =>
_handleStreamError(Exception('A GPS-stream váratlanul lezárult.')));
}
/// A Geolocator stream NEM öngyógyuló: ha egyszer hibát dob (terepen,
/// zötykölődő telefonnal ez elő szokott fordulni), magától többé NEM
/// küld pozíciót. Ezért itt — a korábbi "csak továbbadjuk és feladjuk"
/// helyett — manuálisan újraindítjuk a mögöttes streamet. Végleges,
/// nem-helyreállítható hibáknál (jogosultság/szolgáltatás kikapcsolva)
/// viszont TOVÁBBADJUK, hogy a TrackingController korrekten leállítsa
/// a rögzítést, ahogy eddig is.
void _handleStreamError(Object e) {
if (e is LocationServiceDisabledException ||
e is PermissionDeniedException) {
_controller?.addError(e);
return;
}
_retryCount++;
if (_retryCount > _maxRetries) {
// Tartósan hibás állapot — ezt már valóban jelezni kell.
_controller?.addError(e);
return;
}
// Rövid várakozás, majd friss stream indítása. A _controller
// (amit a TrackingController figyel) változatlan marad — onnan
// nézve ez csak egy pillanatnyi szünet a pozíciókban, nem egy
// végleges leállás.
_retryTimer?.cancel();
_retryTimer = Timer(const Duration(seconds: 1), _startListening);
2026-05-10 02:31:27 +02:00
}
@override
Future<void> dispose() async {
_retryTimer?.cancel();
2026-05-10 02:31:27 +02:00
await _positionSub?.cancel();
await _controller?.close();
_controller = null;
}
}