Files
fitness/mobile/lib/services/gps_service.dart
T
2026-10-04 00:19:45 +03:00

131 lines
3.8 KiB
Dart

import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:geolocator/geolocator.dart';
import '../models/gps_coordinate.dart';
/// خدمة GPS الحقيقي مع دعم التتبع المستمر
class GpsService extends GetxService {
StreamSubscription<Position>? _positionSub;
final _isTracking = false.obs;
final _currentPosition = Rxn<Position>();
final _lastError = ''.obs;
bool get isTracking => _isTracking.value;
Position? get currentPosition => _currentPosition.value;
String get lastError => _lastError.value;
Function(GpsCoordinate)? onNewCoordinate;
Future<bool> checkAndRequestPermissions() async {
if (!await Geolocator.isLocationServiceEnabled()) {
_lastError.value = 'Location services disabled';
return false;
}
var perm = await Geolocator.checkPermission();
if (perm == LocationPermission.denied) {
perm = await Geolocator.requestPermission();
if (perm == LocationPermission.denied) {
_lastError.value = 'Permission denied';
return false;
}
}
if (perm == LocationPermission.deniedForever) {
_lastError.value = 'Permission permanently denied';
return false;
}
return true;
}
Future<GpsCoordinate?> getCurrentPosition() async {
try {
if (!await checkAndRequestPermissions()) return null;
final pos = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
timeLimit: const Duration(seconds: 15),
);
_currentPosition.value = pos;
return _toCoord(pos);
} catch (e) {
_lastError.value = e.toString();
return null;
}
}
Future<bool> startTracking({int distanceFilter = 5}) async {
if (_isTracking.value) return true;
if (!await checkAndRequestPermissions()) return false;
try {
late LocationSettings settings;
if (defaultTargetPlatform == TargetPlatform.android) {
settings = AndroidSettings(
accuracy: LocationAccuracy.high,
distanceFilter: distanceFilter,
intervalDuration: const Duration(seconds: 3),
foregroundNotificationConfig: const ForegroundNotificationConfig(
notificationText: 'SportPath is tracking your workout',
notificationTitle: 'Workout Active',
enableWakeLock: true,
),
);
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
settings = AppleSettings(
accuracy: LocationAccuracy.high,
distanceFilter: distanceFilter,
activityType: ActivityType.fitness,
pauseLocationUpdatesAutomatically: false,
showBackgroundLocationIndicator: true,
);
} else {
settings = LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: distanceFilter,
);
}
_positionSub = Geolocator.getPositionStream(
locationSettings: settings,
).listen(_onPosition, onError: (e) {
_lastError.value = e.toString();
});
_isTracking.value = true;
return true;
} catch (e) {
_lastError.value = e.toString();
return false;
}
}
Future<void> stopTracking() async {
await _positionSub?.cancel();
_positionSub = null;
_isTracking.value = false;
}
void _onPosition(Position pos) {
_currentPosition.value = pos;
final coord = _toCoord(pos);
if (coord.isAccurate && coord.isValid) {
onNewCoordinate?.call(coord);
}
}
GpsCoordinate _toCoord(Position p) => GpsCoordinate(
latitude: p.latitude,
longitude: p.longitude,
altitude: p.altitude,
accuracy: p.accuracy,
speed: p.speed >= 0 ? p.speed : null,
heading: p.heading >= 0 ? p.heading : null,
timestamp: p.timestamp,
);
@override
void onClose() {
stopTracking();
super.onClose();
}
}