first commit

This commit is contained in:
Hamza-Ayed
2026-10-04 00:19:45 +03:00
commit 05f1c9ec6b
93 changed files with 10782 additions and 0 deletions
@@ -0,0 +1,152 @@
import 'dart:async';
import 'package:get/get.dart';
import 'package:uuid/uuid.dart';
import '../models/gps_coordinate.dart';
import '../models/workout.dart';
import '../screens/tracking_screen.dart';
import '../services/database_service.dart';
import '../services/gps_service.dart';
import '../services/sync_service.dart';
import '../services/analytics_service.dart';
import '../utils/polyline_utility.dart';
import '../utils/geo_utils.dart';
/// المتحكم الرئيسي للتمرين (Main Workout Controller)
/// يدير حالة التتبع الحية، التفاعل مع الـ GPS، وحفظ البيانات.
class WorkoutController extends GetxController {
final GpsService _gps = Get.find<GpsService>();
final DatabaseService _db = Get.find<DatabaseService>();
final SyncService _sync = Get.find<SyncService>();
final AnalyticsService _analytics = Get.find<AnalyticsService>();
// حالة التتبع الحية
final Rxn<Workout> currentWorkout = Rxn<Workout>();
final RxList<GpsCoordinate> liveCoordinates = <GpsCoordinate>[].obs;
final RxInt elapsedSeconds = 0.obs;
final RxDouble liveDistance = 0.0.obs;
final RxDouble liveSpeed = 0.0.obs;
Timer? _timer;
// ─── التحكم في الجلسة ──────────────────────────────────────────
Future<void> startWorkout(WorkoutType type) async {
bool ok = await _gps.startTracking(distanceFilter: 5);
if (!ok) {
Get.snackbar("خطأ", "فشل الوصول إلى الموقع (GPS). تأكد من الصلاحيات.");
return;
}
final id = const Uuid().v4();
currentWorkout.value = Workout(id: id, workoutType: type);
currentWorkout.value!.start();
liveCoordinates.clear();
elapsedSeconds.value = 0;
liveDistance.value = 0;
liveSpeed.value = 0;
// ربط مستمع الإحداثيات
_gps.onNewCoordinate = _handleNewCoordinate;
// الانتقال لشاشة التتبع
Get.to(() => const TrackingScreen());
// بدء المؤقت الزمني
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (currentWorkout.value != null && !currentWorkout.value!.isPaused) {
elapsedSeconds.value = currentWorkout.value!.activeDurationSeconds;
}
});
}
void pauseWorkout() {
currentWorkout.value?.pause();
update();
}
void resumeWorkout() {
currentWorkout.value?.resume();
update();
}
Future<void> stopWorkout() async {
if (currentWorkout.value == null) return;
await _gps.stopTracking();
_timer?.cancel();
final workout = currentWorkout.value!;
workout.stop();
// إسناد الإحداثيات النهائية
workout.coordinates = List.from(liveCoordinates);
workout.distanceMeters = liveDistance.value;
// التحليل الذكي للمقاطع
workout.segments =
_analytics.analyzeSegments(workout.coordinates, workout.workoutType);
// تحويل المسار إلى Polyline مضغوط
workout.routePolyline = PolylineUtility.encode(workout.coordinates);
if (workout.coordinates.isNotEmpty) {
workout.startLat = workout.coordinates.first.latitude;
workout.startLng = workout.coordinates.first.longitude;
workout.endLat = workout.coordinates.last.latitude;
workout.endLng = workout.coordinates.last.longitude;
}
// حفظ في قاعدة البيانات المحلية
await _db.saveWorkout(workout);
await _db.saveSegments(workout.id, workout.segments);
// مسح الإحداثيات المؤقتة من جدول الـ GPS (لأنها حفظت في الـ Polyline)
// أو يمكن تركها إذا أردت دقة عالية جداً لاحقاً
// إضافة إلى طابور المزامنة
await _sync.queueWorkout(workout);
currentWorkout.value = null;
Get.snackbar("تم الحفظ", "تم حفظ التمرين بنجاح وجاري المزامنة...");
}
void _handleNewCoordinate(GpsCoordinate coord) {
if (currentWorkout.value == null || currentWorkout.value!.isPaused) return;
liveCoordinates.add(coord);
liveSpeed.value = coord.speed ?? 0;
// حفظ فوري في DB (للحماية من انهيار التطبيق)
_db.saveCoordinate(currentWorkout.value!.id, coord);
// حساب المسافة التراكمية حياً
if (liveCoordinates.length > 1) {
// تم إضافة المسافة في الموديل أو هنا مباشرة
// للتسهيل سنقوم بالتحديث هنا
}
// تحديث المسافة الإجمالية (تبسيطاً سنعتمد على المجموع)
_calculateLiveDistance();
}
void _calculateLiveDistance() {
if (liveCoordinates.length < 2) return;
double totalDist = 0;
for (int i = 1; i < liveCoordinates.length; i++) {
totalDist += GeoUtils.haversineDistance(
liveCoordinates[i - 1].latitude,
liveCoordinates[i - 1].longitude,
liveCoordinates[i].latitude,
liveCoordinates[i].longitude);
}
liveDistance.value = totalDist;
}
@override
void onClose() {
_timer?.cancel();
super.onClose();
}
}