// import 'dart:async'; // import 'package:Intaleq/services/offline_map_service.dart'; // import 'package:Intaleq/services/emergency_signal_service.dart'; // import 'package:Intaleq/views/widgets/mycircular.dart'; // import 'dart:convert'; // import 'dart:io'; // import 'dart:math' show Random, atan2, cos, max, min, pi, pow, sin, sqrt; // import 'dart:math' as math; // import 'dart:ui'; // import 'dart:typed_data'; // import 'package:image/image.dart' as img; // import 'package:Intaleq/services/ride_live_notification.dart'; // import 'package:crypto/crypto.dart'; // import 'package:Intaleq/views/Rate/rate_captain.dart'; // import 'package:Intaleq/views/Rate/rating_driver_bottom.dart'; // import 'package:device_info_plus/device_info_plus.dart'; // import 'package:flutter/foundation.dart'; // import 'package:flutter/services.dart'; // import 'package:http/http.dart' as http; // import 'package:Intaleq/constant/univeries_polygon.dart'; // import 'package:Intaleq/controller/firebase/local_notification.dart'; // import 'package:flutter/cupertino.dart'; // import 'package:flutter_confetti/flutter_confetti.dart' hide Circle; // import 'package:socket_io_client/socket_io_client.dart' as IO; // import 'package:vector_math/vector_math.dart' show radians; // import 'package:Intaleq/controller/functions/tts.dart'; // import 'package:Intaleq/views/home/map_page_passenger.dart'; // import 'package:Intaleq/views/widgets/my_textField.dart'; // import 'package:flutter/material.dart'; // import 'package:geolocator/geolocator.dart'; // import 'package:get/get.dart'; // import 'package:intaleq_maps/intaleq_maps.dart'; // // import 'package:google_polyline_algorithm/google_polyline_algorithm.dart'; // import 'package:intl/intl.dart'; // import 'package:location/location.dart'; // import 'package:Intaleq/constant/country_polygons.dart'; // import 'package:Intaleq/constant/links.dart'; // import 'package:Intaleq/constant/style.dart'; // import 'package:Intaleq/controller/home/points_for_rider_controller.dart'; // import 'package:Intaleq/views/home/map_widget.dart/form_serch_multiy_point.dart'; // import '../../constant/api_key.dart'; // import '../../constant/box_name.dart'; // import '../../constant/colors.dart'; // import '../../constant/info.dart'; // import '../../constant/table_names.dart'; // import '../../env/env.dart'; // import '../../main.dart'; // import '../../models/model/locations.dart'; // import '../../models/model/painter_copoun.dart'; // import '../../print.dart'; // import '../../services/pip_service.dart'; // import '../../views/home/map_widget.dart/cancel_raide_page.dart'; // import '../../views/home/map_widget.dart/car_details_widget_to_go.dart'; // import '../../views/home/map_widget.dart/select_driver_mishwari.dart'; // import '../../views/widgets/elevated_btn.dart'; // import '../../views/widgets/error_snakbar.dart'; // import '../../views/widgets/mydialoug.dart'; // import '../firebase/firbase_messge.dart'; // import '../firebase/notification_service.dart'; // import '../functions/audio_record1.dart'; // import '../functions/crud.dart'; // import '../functions/launch.dart'; // import '../functions/package_info.dart'; // import '../functions/secure_storage.dart'; // import '../payment/payment_controller.dart'; // import 'decode_polyline_isolate.dart'; // import 'deep_link_controller.dart'; // import 'device_performance.dart'; // import 'ios_live_activity_service.dart'; // import 'vip_waitting_page.dart'; // enum RideState { // noRide, // لا يوجد رحلة جارية، عرض واجهة البحث // cancelled, // تم إلغاء الرحلة // preCheckReview, // يوجد رحلة منتهية، تحقق من التقييم // searching, // جاري البحث عن كابتن // driverApplied, // تم قبول الطلب // driverArrived, // وصل السائق // inProgress, // الرحلة بدأت بالفعل // finished, // انتهت الرحلة (سيتم تحويلها إلى preCheckReview) // } // class MapPassengerController extends GetxController { // bool isLoading = true; // TextEditingController placeDestinationController = TextEditingController(); // TextEditingController increasFeeFromPassenger = TextEditingController(); // TextEditingController placeStartController = TextEditingController(); // TextEditingController wayPoint0Controller = TextEditingController(); // TextEditingController wayPoint1Controller = TextEditingController(); // TextEditingController wayPoint2Controller = TextEditingController(); // TextEditingController wayPoint3Controller = TextEditingController(); // TextEditingController wayPoint4Controller = TextEditingController(); // TextEditingController sosPhonePassengerProfile = TextEditingController(); // TextEditingController whatsAppLocationText = TextEditingController(); // TextEditingController messageToDriver = TextEditingController(); // final sosFormKey = GlobalKey(); // final promoFormKey = GlobalKey(); // final messagesFormKey = GlobalKey(); // final increaseFeeFormKey = GlobalKey(); // List data = []; // List bounds = []; // List placesStart = []; // List driversToken = []; // LatLng previousLocationOfDrivers = const LatLng(0, 0); // double angleDegrees = 0; // LatLng currentLocationOfDrivers = const LatLng(0, 0); // List allTextEditingPlaces = []; // List placesDestination = []; // List wayPoint0 = []; // List wayPoint1 = []; // List wayPoint2 = []; // List wayPoint3 = []; // List wayPoint4 = []; // final firebaseMessagesController = // Get.isRegistered() // ? Get.find() // : Get.put(FirebaseMessagesController()); // List> placeListResponseAll = []; // List placeListResponse = [ // formSearchPlaces(0), // formSearchPlaces(1), // formSearchPlaces(2), // formSearchPlaces(3), // ]; // IntaleqMapController? mapController; // bool isStyleLoaded = false; // Set markers = {}; // Set polyLines = {}; // Set polygons = {}; // Set circles = {}; // double speed = 0; // PermissionStatus? permissionGranted; // LatLngBounds? lastComputedBounds; // late LatLng passengerLocation = const LatLng(32, 34); // late LatLng newMyLocation = const LatLng(32.115295, 36.064773); // late LatLng newStartPointLocation = const LatLng(32.115295, 36.064773); // late LatLng newPointLocation0 = const LatLng(32.115295, 36.064773); // late LatLng newPointLocation1 = const LatLng(32.115295, 36.064773); // late LatLng newPointLocation2 = const LatLng(32.115295, 36.064773); // late LatLng newPointLocation3 = const LatLng(32.115295, 36.064773); // late LatLng newPointLocation4 = const LatLng(32.115295, 36.064773); // late LatLng myDestination; // List polylineCoordinates = []; // List polylineCoordinates0 = []; // List polylineCoordinates1 = []; // List polylineCoordinates2 = []; // List polylineCoordinates3 = []; // List polylineCoordinates4 = []; // List> polylineCoordinatesPointsAll = []; // List carsLocationByPassenger = []; // List driverCarsLocationToPassengerAfterApplied = []; // String markerIcon = "marker_icon"; // String tripIcon = "trip_icon"; // String startIcon = "start_icon"; // String endIcon = "end_icon"; // String carIcon = "car_icon"; // String motoIcon = "moto_icon"; // String ladyIcon = "lady_icon"; // double height = 150; // DateTime currentTime = DateTime.now(); // final location = Location(); // late LocationData currentLocation; // double heightMenu = 0; // double widthMenu = 0; // double heightPickerContainer = 90; // double heightPointsPageForRider = 0; // double mainBottomMenuMapHeight = Get.height * .2; // double wayPointSheetHeight = 0; // String stringRemainingTimeToPassenger = ''; // String stringRemainingTimeDriverWaitPassenger5Minute = ''; // bool isDriverInPassengerWay = false; // bool isDriverArrivePassenger = false; // bool startLocationFromMap = false; // bool isAnotherOreder = false; // bool isWhatsAppOrder = false; // bool passengerStartLocationFromMap = false; // bool workLocationFromMap = false; // bool homeLocationFromMap = false; // bool isPassengerRideLocationWidget = false; // bool startLocationFromMap0 = false; // bool startLocationFromMap1 = false; // bool startLocationFromMap2 = false; // bool startLocationFromMap3 = false; // bool startLocationFromMap4 = false; // List startLocationFromMapAll = []; // double latePrice = 0; // double fuelPrice = 0; // double heavyPrice = 0; // double naturePrice = 0; // bool heightMenuBool = false; // String statusRide = 'wait'; // String statusRideVip = 'wait'; // bool statusRideFromStart = false; // bool isPickerShown = false; // bool isPointsPageForRider = false; // bool isBottomSheetShown = false; // bool mapType = false; // bool reloadStartApp = false; // bool mapTrafficON = false; // bool isCancelRidePageShown = false; // bool isCashConfirmPageShown = false; // bool isPaymentMethodPageShown = false; // bool isRideFinished = false; // bool rideConfirm = false; // bool isMarkersShown = false; // bool isMainBottomMenuMap = true; // int durationToPassenger = 0; // bool isWayPointSheet = false; // bool isWayPointStopsSheet = false; // bool isWayPointStopsSheetUtilGetMap = false; // double heightBottomSheetShown = 0; // double cashConfirmPageShown = 250; // late String driverId = ''; // late String gender = ''; // double widthMapTypeAndTraffic = 50; // double paymentPageShown = Get.height * .6; // late LatLng southwest; // late LatLng northeast; // List carLocationsModels = []; // var dataCarsLocationByPassenger; // var datadriverCarsLocationToPassengerAfterApplied; // CarLocation? nearestCar; // bool shouldFetch = true; // Flag to determine if fetch should be executed // int selectedPassengerCount = 1; // double progress = 0; // double progressTimerToPassengerFromDriverAfterApplied = 0; // double progressTimerDriverWaitPassenger5Minute = 0; // int durationTimer = 9; // int durationToRide = 0; // int remainingTime = 25; // int remainingTimeToPassengerFromDriverAfterApplied = 60; // int remainingTimeDriverWaitPassenger5Minute = 60; // int timeToPassengerFromDriverAfterApplied = 0; // Timer? timerToPassengerFromDriverAfterApplied; // bool rideTimerBegin = false; // double progressTimerRideBegin = 0; // int remainingTimeTimerRideBegin = 60; // String stringRemainingTimeRideBegin = ''; // String hintTextStartPoint = 'Search for your Start point'.tr; // String hintTextwayPoint0 = 'Search for waypoint'.tr; // String hintTextwayPoint1 = 'Search for waypoint'.tr; // String hintTextwayPoint2 = 'Search for waypoint'.tr; // String hintTextwayPoint3 = 'Search for waypoint'.tr; // String hintTextwayPoint4 = 'Search for waypoint'.tr; // String currentLocationString = 'Current Location'.tr; // String currentLocationString0 = 'Current Location'.tr; // String currentLocationString1 = 'Add Location 1'.tr; // String currentLocationString2 = 'Add Location 2'.tr; // String currentLocationString3 = 'Add Location 3'.tr; // String currentLocationString4 = 'Add Location 4'.tr; // String placesCoordinate0 = ''.tr; // String placesCoordinate1 = ''.tr; // String placesCoordinate2 = ''.tr; // String placesCoordinate3 = ''.tr; // String placesCoordinate4 = ''.tr; // List currentLocationStringAll = []; // List hintTextwayPointStringAll = []; // var placesCoordinate = []; // String hintTextDestinationPoint = 'Select your destination'.tr; // late String rideId = 'yet'; // bool noCarString = false; // bool isCashSelectedBeforeConfirmRide = false; // bool isPassengerChosen = false; // bool isSearchingWindow = false; // bool currentLocationToFormPlaces = false; // bool currentLocationToFormPlaces0 = false; // bool currentLocationToFormPlaces1 = false; // bool currentLocationToFormPlaces2 = false; // bool currentLocationToFormPlaces3 = false; // bool currentLocationToFormPlaces4 = false; // List currentLocationToFormPlacesAll = []; // // ── Multi-Waypoint (max 2 stops) ────────────────────────────────────────── // List menuWaypoints = [null, null]; // List menuWaypointNames = ['', '']; // int activeMenuWaypointCount = 0; // bool isPickingWaypoint = false; // int pickingWaypointIndex = -1; // late String driverToken = ''; // int carsOrder = 0; // int wayPointIndex = 0; // late double kazan = 8; // String? mapAPIKEY; // late double totalME = 0; // late double tax = 0; // late double totalPassenger = 0; // late double totalCostPassenger = 0; // late double totalPassengerComfort = 0; // late double totalPassengerComfortDiscount = 0; // late double totalPassengerElectricDiscount = 0; // late double totalPassengerLadyDiscount = 0; // late double totalPassengerSpeedDiscount = 0; // late double totalPassengerBalashDiscount = 0; // late double totalPassengerRaihGaiDiscount = 0; // late double totalPassengerScooter = 0; // late double totalPassengerVan = 0; // late double totalDriver = 0; // late double averageDuration = 0; // late double costDuration = 0; // late double costDistance = 0; // late double distance = 0; // late double duration = 0; // bool _isDriverAppliedLogicExecuted = false; // فلاج لمنع التنفيذ المتكرر // bool _isDriverArrivedLogicExecuted = false; // bool _isRideBeginLogicExecuted = false; // DateTime? _searchStartTime; // لتتبع مدة البحث // DateTime? _lastDriversNotifyTime; // لتتبع آخر مرة تم إرسال إشعار للسائقين // final int _masterTimerIntervalSeconds = 5; // فاصل زمني ثابت للمؤقت الرئيسي // final int _searchTimeoutSeconds = 60; // مهلة البحث قبل عرض خيار زيادة السعر // final int _notifyDriversIntervalSeconds = // 25; // إرسال إشعار للسائقين كل 25 ثانية // // متغير لمنع أي عمليات تحديث أثناء التقييم // bool _isRatingScreenOpen = false; // // --- إضافة جديدة: متغيرات لإدارة البحث المتوسع --- // int _currentSearchPhase = 0; // لتتبع المرحلة الحالية للبحث // bool _isFetchingDriverLocation = false; // متغير لمنع تكرار الطلب // // === استبدل initSocket بالكامل === // late IO.Socket socket; // bool isSocketConnected = false; // int _reconnectAttempts = 0; // final int _maxReconnectAttempts = 5; // Timer? _reconnectTimer; // var currentRideId; // // لتخزين نقاط مسار السائق الحالية للمقارنة // List _currentDriverRoutePoints = []; // // متغير لتتبع مصدر القبول — Socket أم غيره // String _rideAcceptedViaSource = "Unknown"; // // عدّاد تحديثات الموقع المستلمة من السوكيت (لقياس الصحة) // int _socketLocationUpdatesCount = 0; // final Map _pollingIntervals = { // RideState.noRide: 6, // RideState.searching: 8, // RideState.driverApplied: 10, // RideState.driverArrived: 15, // RideState.inProgress: 15, // RideState.cancelled: 3600, // RideState.finished: 3600, // RideState.preCheckReview: 3600, // }; // // لمنع التكرار (عشان ما يعمل 100 طلب في نفس اللحظة) // bool _isRecalculatingRoute = false; // // متغير لمراقبة صحة السوكيت // DateTime? _lastSocketLocationTime; // // مسافة السماحية (مثلاً 150 متر) قبل اعتبار السائق "خارج المسار" // final double _deviationThresholdMeters = 150.0; // // ... (باقي الـ Imports) // // متغيرات التحكم // Timer? _locationPollingTimer; // تايمر مخصص للموقع فقط // // ============================================================================== // // 1. الدالة الرئيسية لتأسيس الاتصال (تستدعى عند بدء البحث startSearchingForDriver) // // ============================================================================== // Timer? _heartbeatTimer; // void initConnectionWithSocket() { // if (isSocketConnected && socket != null) return; // String passengerId = box.read(BoxName.passengerID).toString(); // Log.print("🔌 Initializing Socket for Passenger: $passengerId"); // socket = IO.io( // AppLink.serverSocket, // IO.OptionBuilder() // .setTransports(['websocket']) // .disableAutoConnect() // .setQuery({'id': passengerId}) // // ✅ [FIX] إعادة اتصال شبه-لانهائية (999 محاولة) بدلاً من 20 // .setReconnectionAttempts(20) // // ✅ [FIX] تأخير أقل (1.5 ثانية) مع حد أقصى (8 ثواني) للتسريع // .setReconnectionDelay(1500) // .setReconnectionDelayMax(8000) // .enableReconnection() // .setExtraHeaders({'Connection': 'Upgrade'}) // .build(), // ); // socket.connect(); // // ✅ معالج الاتصال الأول // socket.onConnect((_) { // Log.print("✅ Socket Connected Successfully"); // isSocketConnected = true; // _reconnectAttempts = 0; // _startHeartbeat(); // // ✅ [FIX] الاشتراك مجدداً في أحداث الرحلة عند كل اتصال // if (rideId != null && rideId != 'yet' && driverId.isNotEmpty) { // socket.emit('subscribe_driver_location', { // 'ride_id': rideId, // 'driver_id': driverId, // }); // Log.print("📡 Re-subscribed to driver location after connect"); // } // update(); // }); // // ⚠️ معالج الانقطاع // socket.onDisconnect((_) { // Log.print("⚠️ Socket Disconnected — Auto-Reconnect will handle it"); // isSocketConnected = false; // // تفعيل Polling أسرع كـ Fallback مؤقت (سيتم إيقافه عند عودة الاتصال) // if (_isActiveRideState()) { // Log.print("🔄 Enabling Fast Polling Fallback (4s) until reconnect..."); // _startMasterTimerWithInterval(4); // } // update(); // }); // // 🔁 [FIX] معالج إعادة الاتصال الناجحة // socket.onReconnect((_) { // Log.print("🔁 Socket Reconnected Successfully!"); // isSocketConnected = true; // _reconnectAttempts = 0; // // استئناف النبضة فوراً // _startHeartbeat(); // // إعادة الاشتراك في أحداث الرحلة // if (rideId != null && rideId != 'yet' && driverId.isNotEmpty) { // socket.emit('subscribe_driver_location', { // 'ride_id': rideId, // 'driver_id': driverId, // }); // Log.print("📡 Re-subscribed to driver location after reconnect"); // } // // ✅ [FIX] إيقاف الـ Fast Polling لأن السوكيت عاد // if (_isActiveRideState()) { // Log.print("✅ Socket back online — stopping Fast Polling Fallback"); // _masterTimer?.cancel(); // _masterTimer = null; // } // update(); // }); // // 🔄 [FIX] معالج محاولات إعادة الاتصال (للتشخيص) // socket.onReconnectAttempt((attemptNumber) { // Log.print("🔄 Socket Reconnect Attempt #$attemptNumber..."); // }); // // ❌ معالج الأخطاء // socket.onError((error) { // Log.print("❌ Socket Error: $error"); // isSocketConnected = false; // }); // // 📩 معالج تحديثات الحالة // socket.on('ride_status_change', (data) { // Log.print("📩 Socket Event: ride_status_change -> $data"); // _handleRideStatusChangeWithSocket(data); // }); // // 📍 معالج موقع السائق // socket.on('driver_location_update', (data) { // handleDriverLocationUpdate(data); // }); // } // void _startHeartbeat() { // _heartbeatTimer?.cancel(); // _heartbeatTimer = Timer.periodic(const Duration(seconds: 25), (timer) { // if (isSocketConnected && socket.connected) { // socket.emit('heartbeat', // {'passenger_id': box.read(BoxName.passengerID).toString()}); // } // }); // } // // دالة مساعدة // bool _isActiveRideState() { // return currentRideState.value == RideState.searching || // currentRideState.value == RideState.driverApplied || // currentRideState.value == RideState.driverArrived || // currentRideState.value == RideState.inProgress; // } // /// فحص سريع: هل السوكيت يعمل ويرسل بيانات؟ // bool _isSocketHealthy() { // if (!isSocketConnected) return false; // if (_lastSocketLocationTime == null) return false; // final diff = DateTime.now().difference(_lastSocketLocationTime!).inSeconds; // return diff < 20; // إذا آخر تحديث قبل أقل من 20 ثانية // } // /// 🧠 خوارزمية ذكية: حساب أقصر مسافة بين موقع السائق والـ Polyline (بدون API) // double _calculateDistanceToPolyline(LatLng point, List polyline) { // if (polyline.isEmpty) return 999.0; // double minDistance = double.infinity; // for (int i = 0; i < polyline.length - 1; i++) { // double d = _distToSegment(point, polyline[i], polyline[i + 1]); // if (d < minDistance) minDistance = d; // } // return minDistance; // } // double _distToSegment(LatLng p, LatLng v, LatLng w) { // double l2 = _dist2(v, w); // if (l2 == 0) return _distanceBetween(p, v); // double t = ((p.latitude - v.latitude) * (w.latitude - v.latitude) + // (p.longitude - v.longitude) * (w.longitude - v.longitude)) / // l2; // t = max(0, min(1, t)); // return _distanceBetween( // p, // LatLng(v.latitude + t * (w.latitude - v.latitude), // v.longitude + t * (w.longitude - v.longitude))); // } // double _dist2(LatLng v, LatLng w) { // return pow(v.latitude - w.latitude, 2).toDouble() + // pow(v.longitude - w.longitude, 2).toDouble(); // } // double _distanceBetween(LatLng p1, LatLng p2) { // return Geolocator.distanceBetween( // p1.latitude, p1.longitude, p2.latitude, p2.longitude); // } // // ============================================================================== // // 2. العقل المدبر: توجيه الحالات // // ============================================================================== // void _handleRideStatusChangeWithSocket(dynamic data) { // if (data == null || data['status'] == null) return; // String newStatus = data['status'].toString().toLowerCase(); // Log.print("🔔 Socket Status Update: $newStatus"); // // استخراج بيانات السائق إذا توفرت (تأتي من acceptRide.php) // Map? driverInfo; // if (data['driver_info'] != null && data['driver_info'] is Map) { // driverInfo = Map.from(data['driver_info']); // } // switch (newStatus) { // case 'accepted': // أو apply/applied حسب تسمية السيرفر // _onDriverAcceptedWithSocket(data, driverData: driverInfo); // break; // case 'arrived': // _onDriverArrivedWithSocket(); // break; // case 'started': // أو begin // _onRideStartedWithSocket(); // break; // case 'finished': // أو ended // _onRideFinishedWithSocket(data); // break; // case 'cancelled': // _onRideCancelledWithSocket(data); // break; // case 'no_drivers_found': // showNoDriverDialog(); // break; // } // } // // ============================================================================== // // 3. دوال المعالجة التفصيلية (Actions) // // ============================================================================== // void showNoDriverDialog() { // Get.defaultDialog( // title: "No Drivers Found".tr, // middleText: // "Sorry, there are no cars available of this type right now.".tr, // textConfirm: "Refresh Map".tr, // textCancel: "Cancel".tr, // confirmTextColor: Colors.white, // onConfirm: () { // Get.back(); // إغلاق الديالوج // restCounter(); // stopAllTimers(); // Get.offAll(() => MapPagePassenger()); // إعادة تحميل صفحة الخريطة // }, // ); // } // // أ) عند قبول السائق للرحلة // // أ) عند قبول السائق للرحلة (معدلة) // // دالة الاستقبال من السوكيت (تصبح مجرد محول) // void _onDriverAcceptedWithSocket(dynamic data, // {Map? driverData}) { // // استخراج البيانات وتمريرها للدالة الموحدة // Map? info = driverData; // // دعم الهيكلية الجديدة // if (info == null && data['driver_info'] != null) { // info = Map.from(data['driver_info']); // } // // دعم الهيكلية القديمة (إن وجدت) // else if (info == null && data['driverList'] != null) { // // تحويل driverList إلى map إذا لزم الأمر // } // processRideAcceptance(driverData: info, source: "Socket"); // } // void _fillDriverDataLocally(Map data) { // try { // // تعبئة المتغيرات بناءً على أسماء الحقول في acceptRide.php // driverId = data['driver_id']?.toString() ?? ''; // driverPhone = data['phone']?.toString() ?? ''; // String fName = data['first_name']?.toString() ?? ''; // String lName = data['last_name']?.toString() ?? ''; // passengerName = lName.isNotEmpty // ? "$fName $lName" // : fName; // (هنا المتغير اسمه passengerName لكنه يحمل اسم السائق في الكود لديك) // driverName = passengerName; // make = data['make']?.toString() ?? ''; // model = data['model']?.toString() ?? ''; // carColor = data['color']?.toString() ?? ''; // colorHex = data['color_hex']?.toString() ?? ''; // licensePlate = data['car_plate']?.toString() ?? ''; // carYear = data['year']?.toString() ?? ''; // driverRate = data['ratingDriver']?.toString() ?? '5.0'; // driverToken = data['token']?.toString() ?? ''; // // إذا كان هناك أي بيانات أخرى تحتاجها الواجهة // update(); // } catch (e) { // Log.print("Error parsing socket driver data: $e"); // } // } // // دالة موحدة: تجلب المسار + الوقت + المسافة + ترسم الخط + تضبط الكاميرا // Future calculateDriverToPassengerRoute( // LatLng driverPos, LatLng passengerPos) async { // // 1. تجهيز الرابط (نفس API الـ Direction) // // نستخدم overview=full للحصول على الرسمة، و steps=false لتخفيف البيانات // final Map queryParams = { // 'fromLat': driverPos.latitude.toString(), // 'fromLng': driverPos.longitude.toString(), // 'toLat': passengerPos.latitude.toString(), // 'toLng': passengerPos.longitude.toString(), // }; // final uri = // Uri.parse(AppLink.mapSaasRoute).replace(queryParameters: queryParams); // Log.print('📍 Calculating Driver Route: $uri'); // try { // final response = await http.get(uri, headers: { // 'x-api-key': Env.mapSaasKey, // }).timeout(const Duration(seconds: 20)); // if (response.statusCode == 200) { // final responseData = json.decode(response.body); // // Support both old format (routes[0]) and new SaaS format (top-level) // var routeData = responseData['routes'] != null // ? responseData['routes'][0] // : responseData; // // 2. تحديث المتغيرات (المسافة والوقت) // double durationSecondsRaw = (routeData['duration'] as num).toDouble(); // int finalDurationSeconds = // (durationSecondsRaw * kDurationScalar).toInt(); // double distanceMeters = (routeData['distance'] as num).toDouble(); // timeToPassengerFromDriverAfterApplied = finalDurationSeconds; // remainingTimeToPassengerFromDriverAfterApplied = finalDurationSeconds; // distanceByPassenger = distanceMeters.toStringAsFixed(0); // // تحديث نصوص الواجهة // int minutes = (finalDurationSeconds / 60).floor(); // int seconds = finalDurationSeconds % 60; // stringRemainingTimeToPassenger = // '$minutes:${seconds.toString().padLeft(2, '0')}'; // Log.print( // '✅ Driver Route Info: $minutes min, ${distanceMeters.toInt()} m'); // // 3. معالجة الرسم (Polyline) // // SaaS uses 'points', OSRM uses 'geometry' // String pointsString = // routeData['points'] ?? routeData['geometry'] ?? ""; // if (pointsString.isNotEmpty) { // List decodedPoints = // await compute(decodePolylineIsolate, pointsString); // // حفظ نسخة للمقارنة // _currentDriverRoutePoints = decodedPoints; // // إزالة خط مسار السائق القديم فقط // polyLines = polyLines // .where((p) => p.polylineId.value != 'driver_route') // .toSet(); // // إضافة الخط الجديد (بستايل مميز للسائق) // polyLines = { // ...polyLines, // Polyline( // polylineId: const PolylineId('driver_route'), // points: decodedPoints, // color: // const Color(0xFF333333), // لون مختلف عن مسار الرحلة الأساسي // width: 5, // ) // }; // } // // 4. ضبط الكاميرا لتشمل السائق والراكب // _fitCameraToPoints(driverPos, passengerPos); // update(); // تحديث واحد للكل // } // } catch (e) { // Log.print('❌ Error calculating driver route: $e'); // } // } // Future _checkAndRecalculateIfDeviated(LatLng driverPos) async { // // 1. شروط الخروج السريع // if (_isRecalculatingRoute || _currentDriverRoutePoints.isEmpty) return; // // 2. حساب المسافة لأقرب نقطة في المسار (خوارزمية سريعة) // // نستخدم مكتبة Geolocator أو حساب رياضي بسيط // double minDistance = 100000.0; // // لتقليل الحمل، لا نفحص كل النقاط، نفحص عينة (كل 5 نقاط مثلاً) أو الكل إذا المسار قصير // for (var point in _currentDriverRoutePoints) { // double dist = Geolocator.distanceBetween(driverPos.latitude, // driverPos.longitude, point.latitude, point.longitude); // if (dist < minDistance) minDistance = dist; // } // // 3. اتخاذ القرار // if (minDistance > _deviationThresholdMeters) { // Log.print("⚠️ Driver deviated ($minDistance m). Recalculating route..."); // _isRecalculatingRoute = true; // // إعادة حساب المسار من موقع السائق الجديد // await calculateDriverToPassengerRoute(driverPos, passengerLocation); // _isRecalculatingRoute = false; // } // } // // ب) عند وصول السائق // void _onDriverArrivedWithSocket() { // Log.print("🚖 Driver Arrived (Socket)"); // processDriverArrival("Socket"); // } // // ج) عند بدء الرحلة // void _onRideStartedWithSocket() { // Log.print("🚀 Ride Started (Socket)"); // processRideBegin(source: "Socket"); // } // // ربط السوكيت // // د) عند انتهاء الرحلة (Socket Listener) // void _onRideFinishedWithSocket(dynamic data) { // Log.print("🏁 Ride Finished (Socket)"); // // نحاول استخراج DriverList من البيلود القادم من PHP // // في finish_ride_updates.php أسميناه 'DriverList' // var rawList = data['DriverList']; // List listToSend = []; // if (rawList != null) { // if (rawList is List) { // listToSend = rawList; // } else if (rawList is String) { // // احتياطاً لو وصل كنص // try { // listToSend = jsonDecode(rawList); // } catch (e) { // Log.print("Error: $e"); // } // } // } // // إذا كانت القائمة فارغة، نحاول بناءها من البيانات المتفرقة (Fallback) // if (listToSend.isEmpty && data['price'] != null) { // listToSend = [ // driverId, // 0 // rideId, // 1 // driverToken, // 2 // data['price'].toString() // 3 // ]; // } // // استدعاء المعالج الموحد // processRideFinished(listToSend, source: "Socket"); // } // // هـ) عند الإلغاء // void _onRideCancelledWithSocket(dynamic data) { // processRideCancelledByDriver(data, source: "Socket"); // } // // ============================================================================== // // 4. إدارة تتبع الموقع (Polling) - مفصولة عن السوكيت // // ============================================================================== // // متغير لمنع التكرار (Race Condition Guard) // bool _isCancelProcessed = false; // /// **معالجة إلغاء الرحلة الموحدة (Gatekeeper)** // /// // /// تستدعى من [Socket] أو [FCM] عند قيام السائق بإلغاء الرحلة. // /// تضمن عدم تضارب الإشعارات وتوحد تجربة المستخدم. // Future processRideCancelledByDriver(dynamic data, // {String source = "Unknown"}) async { // if (_isCancelProcessed) return; // _isCancelProcessed = true; // stopAllTimers(); // if (Get.isDialogOpen == true) Get.back(); // await RideLiveNotification.cancel(); // IosLiveActivityService.endRideActivity(); // ✅ أضف هذا السطر // PipService.disablePip(); // ✅ إيقاف PiP عند انتهاء الرحلة // if (Get.isDialogOpen == true) Get.back(); // await RideLiveNotification.cancel(); // Get.defaultDialog( // title: "Sorry 😔".tr, // استخدام المفتاح الإنجليزي // titleStyle: // const TextStyle(color: Colors.red, fontWeight: FontWeight.bold), // barrierDismissible: false, // content: Column( // children: [ // const Icon(Icons.cancel_presentation, // size: 50, color: Colors.redAccent), // const SizedBox(height: 10), // Text( // "The driver cancelled the trip for an emergency reason.\nDo you want to search for another driver immediately?" // .tr, // textAlign: TextAlign.center, // ), // ], // ), // actions: [ // TextButton( // onPressed: () { // Get.back(); // handleNoDriverFound(); // }, // child: Text("Cancel Trip".tr, // style: const TextStyle(color: Colors.grey)), // ), // ElevatedButton.icon( // style: // ElevatedButton.styleFrom(backgroundColor: AppColor.primaryColor), // icon: const Icon(Icons.refresh, color: Colors.white), // label: Text("Search for another driver".tr, // style: const TextStyle(color: Colors.white)), // onPressed: () { // Get.back(); // retrySearchForDrivers(); // }, // ), // ], // ); // } // Future handleNoDriverFound() async { // stopAllTimers(); // await RideLiveNotification.cancel(); // IosLiveActivityService.endRideActivity(); // ✅ أضف هذا السطر // PipService.disablePip(); // ✅ إيقاف PiP // _isCancelProcessed = false; // currentRideState.value = RideState.noRide; // resetAllMapStates(); // Get.offAll(() => const MapPagePassenger()); // Get.defaultDialog( // title: "We apologize 😔".tr, // middleText: "No drivers found at the moment.\nPlease try again later.".tr, // confirm: ElevatedButton( // onPressed: () => Navigator.pop(Get.context!), // child: Text("Ok".tr), // ), // ); // } // /// **إعادة البحث عن سائقين (Retry Search)** // /// // /// تقوم باستدعاء السكربت لإعادة تفعيل الرحلة وبدء عداد البحث من جديد. // void retrySearchForDrivers() async { // _isCancelProcessed = false; // isSearchingWindow = true; // currentRideState.value = RideState.searching; // driversStatusForSearchWindow = 'Searching for nearby drivers...'.tr; // update(); // try { // Log.print("🔄 Retrying search for ride ID: $rideId"); // // تجهيز البيانات المخزنة للإرسال // var payload = { // "ride_id": rideId.toString(), // "passenger_id": box.read(BoxName.passengerID).toString(), // "passenger_name": box.read(BoxName.name).toString(), // "passenger_phone": box.read(BoxName.phone).toString(), // "passenger_email": box.read(BoxName.email).toString(), // "passenger_token": box.read(BoxName.tokenFCM).toString(), // "passenger_wallet": box.read(BoxName.passengerWalletTotal).toString(), // "passenger_rating": "5.0", // أو قراءة التقييم الحقيقي إن وجد // // قراءة البيانات من المتغيرات المحفوظة في الكنترولر أو الـ Box // "start_lat": startLocation.latitude.toString(), // "start_lng": startLocation.longitude.toString(), // "end_lat": endLocation.latitude.toString(), // "end_lng": endLocation.longitude.toString(), // "start_name": startNameAddress, // "end_name": endNameAddress, // "distance": distance.toString(), // "distance_text": distanceByPassenger ?? "", // "duration_text": durationToPassenger.toString(), // "price": totalPassenger.toString(), // "price_for_driver": costForDriver.toString(), // "car_type": box.read(BoxName.carType).toString(), // "is_wallet": Get.find().isWalletChecked.toString(), // // الخطوات (اختياري) // "has_steps": Get.find().wayPoints.length > 1 // ? "true" // : "false", // // يمكنك إضافة الخطوات إذا كانت لديك في مصفوفة // }; // var response = await CRUD().post( // link: "${AppLink.rideServerSide}/rides/retry_search_drivers.php", // payload: payload, // ); // if (response['status'] == 'success') { // Log.print("✅ Search reset successfully."); // startSearchingTimer(); // } else { // mySnackbarWarning("Failed to search, please try again later".tr); // handleNoDriverFound(); // } // } catch (e) { // Log.print("Error retrying search: $e"); // mySnackbarWarning("Please check your internet connection".tr); // handleNoDriverFound(); // } // } // Timer? _searchTimer; // /// **بدء مؤقت البحث (Search Timer)** // /// // /// يبدأ عداداً (مثلاً 90 ثانية). إذا لم يتم قبول الرحلة خلال هذه المدة، // /// يتم إنهاء البحث واستدعاء [handleNoDriverFound]. // Future startSearchingTimer() async { // _searchTimer?.cancel(); // int seconds = 0; // Log.print("⏳ Search Timer Started (90s)..."); // await RideLiveNotification.showSearching(driversStatusForSearchWindow); // _searchTimer = Timer.periodic(const Duration(seconds: 1), (timer) { // seconds++; // // إذا تغيرت الحالة (مثلاً سائق قبل الرحلة)، نوقف العداد // if (currentRideState.value != RideState.searching) { // timer.cancel(); // return; // } // // انتهاء الوقت (90 ثانية) // if (seconds >= 90) { // timer.cancel(); // handleNoDriverFound(); // ⏳ انتهى الوقت ولم نجد سائقاً // } // }); // } // // متغير لمنع التكرار (Race Condition Guard) // bool _isArrivalProcessed = false; // /// **معالجة وصول السائق الموحدة (Unified Driver Arrival Handler)** // /// // /// تقوم هذه الدالة بإدارة حدث وصول السائق إلى موقع الراكب، وتعمل كـ "حارس بوابة" (Gatekeeper) // /// لتوحيد المصادر سواء كانت من **WebSocket** أو **Firebase Notification**. // /// // /// **المهام التي تقوم بها:** // /// 1. **منع التضارب (Race Condition Guard):** تتأكد أن الحدث لم يتم تنفيذه مسبقاً لتجنب تكرار الإشعارات أو إعادة ضبط العدادات. // /// 2. **تحديث الحالة:** تغير حالة الرحلة فوراً إلى [RideState.driverArrived]. // /// 3. **تفعيل الواجهة:** تظهر ديالوج "السائق وصل" وتبدأ عداد الانتظار المجاني (5 دقائق). // /// // /// * [source] : نص يوضح مصدر الاستدعاء (مثل "Socket" أو "FCM") لأغراض التتبع (Logging). // Future processDriverArrival(String source) async { // // 1. الحارس: إذا تم التنفيذ سابقاً، توقف // if (currentRideState.value == RideState.driverArrived || // _isArrivalProcessed) { // Log.print("✋ Ignored Arrival from $source. Already processed."); // return; // } // _isArrivalProcessed = true; // Log.print("🚖 Driver Arrived via $source! Processing..."); // // 2. تحديث الحالة // currentRideState.value = RideState.driverArrived; // statusRide = 'Arrived'; // await RideLiveNotification.showDriverArrived(driverName ?? ''); // // 3. تشغيل واجهة الوصول والعداد // driverArrivePassengerDialoge(); // startTimerDriverWaitPassenger5Minute(); // // 4. إزالة مسار السائق واستعادة مسار الراكب للوجهة // if (polylineCoordinates.isNotEmpty) { // _playRouteAnimation(polylineCoordinates, lastComputedBounds); // } // update(); // } // // متغير لمنع التكرار // bool _isFinishProcessed = false; // /// **معالجة إنهاء الرحلة الموحدة (Unified Ride Finish Handler)** // /// // /// تستدعى عند استلام حدث النهاية من السوكيت أو FCM. // /// تقوم بإغلاق السوكيت، إيقاف التتبع، والانتقال لشاشة التقييم والدفع. // /// // /// * [driverList]: قائمة البيانات [driverId, rideId, token, price]. // Future processRideFinished(List driverList, // {String source = "Unknown"}) async { // // 1. الحارس: منع التكرار // if (currentRideState.value == RideState.finished || _isFinishProcessed) { // Log.print("✋ Ignored Finish Request from $source. Already Finished."); // return; // } // _isFinishProcessed = true; // Log.print( // "🏁 Ride Finished via $source. Price: ${driverList.length > 3 ? driverList[3] : 'N/A'}"); // // 2. تحديث الحالة // currentRideState.value = RideState.finished; // // 3. تنظيف الموارد // disposeRideSocket(); // إغلاق السوكيت // _stopDriverLocationPolling(); // إيقاف تتبع الموقع // if (Get.isRegistered()) { // Get.find().stopRecording(); // } // // إغلاق أي ديالوج سابق // if (Get.isDialogOpen == true) Get.back(); // // 4. إشعار "لا تنسى أغراضك" // NotificationController().showNotification( // 'Alert'.tr, // "Please make sure not to leave any personal belongings in the car.".tr, // 'tone1', // ); // IosLiveActivityService.endRideActivity(); // PipService.disablePip(); // ✅ إيقاف PiP // await RideLiveNotification.cancel(); // // 5. استخراج البيانات والانتقال // if (driverList.length >= 4) { // String price = driverList[3].toString(); // // الانتقال لصفحة التقييم // Get.offAll(() => RateDriverFromPassenger(), arguments: { // 'driverId': driverList[0].toString(), // 'rideId': driverList[1].toString(), // 'price': price // }); // } // } // void _startDriverLocationPollingWithTimer() { // Log.print("📍 Starting Driver Location Polling (6s interval)"); // _locationPollingTimer?.cancel(); // // استدعاء فوري لأول مرة // // getDriverCarsLocationToPassengerAfterApplied(); // _locationPollingTimer = Timer.periodic(Duration(seconds: 6), (timer) { // // شرط التوقف: إذا انتهت الرحلة أو ألغيت // if (currentRideState.value == RideState.finished || // currentRideState.value == RideState.cancelled || // currentRideState.value == RideState.noRide) { // timer.cancel(); // return; // } // // جلب الموقع وتحديث الماركر // getDriverCarsLocationToPassengerAfterApplied(); // }); // } // void _stopDriverLocationPolling() { // Log.print("🛑 Stopping Location Polling"); // _locationPollingTimer?.cancel(); // _locationPollingTimer = null; // } // // ============================================================================== // // 5. التنظيف والإغلاق // // ============================================================================== // void disposeRideSocket() { // if (socket != null) { // socket!.disconnect(); // socket!.dispose(); // // socket = null; // isSocketConnected = false; // Log.print("🔌 Socket Disposed"); // } // } // /// ============================================================================== // /// 6. معالجة تحديث موقع السائق من السوكيت // /// ============================================================================== // void handleDriverLocationUpdate(dynamic data) { // if (!isSocketConnected || data == null) return; // // 🔥 1. تسجيل وقت استلام البيانات (تغذية الـ Watchdog) // _lastSocketLocationTime = DateTime.now(); // _socketLocationUpdatesCount++; // // 🧠 إذا وصلتنا 3 تحديثات ناجحة من السوكيت، أوقف البولينج (إن كان يعمل) // if (_socketLocationUpdatesCount >= 3 && _locationPollingTimer != null) { // Log.print("✅ Socket delivering locations reliably. Stopping polling."); // _stopDriverLocationPolling(); // } // try { // double lat = double.tryParse(data['latitude']?.toString() ?? '0') ?? 0; // double lng = double.tryParse(data['longitude']?.toString() ?? '0') ?? 0; // double heading = double.tryParse(data['heading']?.toString() ?? '0') ?? 0; // if (lat == 0 || lng == 0) return; // LatLng newPos = LatLng(lat, lng); // // تحديث القائمة (نفس المنطق القديم) // if (driverCarsLocationToPassengerAfterApplied.isEmpty) { // driverCarsLocationToPassengerAfterApplied.add(newPos); // } else { // driverCarsLocationToPassengerAfterApplied[0] = newPos; // } // currentLocationOfDrivers = newPos; // // ------------------------------------------------------------------ // // 🔥 2. Smart Rerouting Logic ( deviation > 50m ) // // ------------------------------------------------------------------ // if (_currentDriverRoutePoints.isNotEmpty) { // double deviation = // _calculateDistanceToPolyline(newPos, _currentDriverRoutePoints); // if (deviation > 50.0) { // Log.print( // "⚠️ Driver deviated by ${deviation.toStringAsFixed(1)}m. Smart Rerouting triggered."); // // إعادة رسم المسار محلياً (لا يتم استدعاؤه إلا عند الانحراف الحقيقي) // calculateDriverToPassengerRoute(newPos, passengerLocation); // } // } // // 🔥 تحديث الكاميرا: ضمان بقاء السيارة في منتصف الخريطة // // ------------------------------------------------------------------ // // ملاحظة: تأكد من أن mapController قد تم تهيئته (initialized) // if (mapController != null) { // // نستخدم animateCamera لحركة ناعمة بدلاً من moveCamera القاسية // mapController!.animateCamera(CameraUpdate.newLatLng(newPos)); // } // // ------------------------------------------------------------------ // // تحديث الوقت المتبقي (إذا أرسله السيرفر) // if (data['eta_seconds'] != null) { // int etaSeconds = int.tryParse(data['eta_seconds'].toString()) ?? 0; // if (etaSeconds > 0) { // remainingTimeToPassengerFromDriverAfterApplied = etaSeconds; // int minutes = (etaSeconds / 60).floor(); // int seconds = etaSeconds % 60; // stringRemainingTimeToPassenger = // '$minutes:${seconds.toString().padLeft(2, '0')}'; // } // } // // تحديث الماركر // _updateDriverMarker(newPos, heading); // } catch (e) { // Log.print('Error in handleDriverLocationUpdate: $e'); // } // } // // دالة مساعدة لتحديث ماركر السائق // void _updateDriverMarker(LatLng position, double heading) { // const String markerId = 'driver_location'; // final mId = MarkerId(markerId); // final existingMarker = markers.cast().firstWhere( // (m) => m?.markerId == mId, // orElse: () => null, // ); // if (existingMarker != null) { // _smoothlyUpdateMarker(existingMarker, position, heading, carIcon); // } else { // markers = { // ...markers, // Marker( // markerId: mId, // position: position, // icon: InlqBitmap.fromStyleImage(carIcon), // rotation: heading, // anchor: const Offset(0.5, 0.5), // ), // }; // update(); // } // } // // === إضافة متغير للتحكم === // bool _isUsingFallback = false; // void _startPollingFallback() { // if (_isUsingFallback) return; // Log.print('🔄 Starting Polling Fallback Mode'); // _isUsingFallback = true; // // استخدام _handleRideState الموجود (كما كان) // _startMasterTimer(); // } // void handleActiveRideOnStartup(dynamic data) { // try { // if (data == null || data['has_active_ride'] != true) { // Log.print('[Startup] No active ride'); // currentRideState.value = RideState.noRide; // _startMasterTimer(); // return; // } // Log.print('[Startup] ✅ Active ride found!'); // var rideData = data['ride']; // rideId = rideData['ride_id'].toString(); // currentRideId = rideId; // driverId = rideData['driver_id']?.toString() ?? ''; // String status = rideData['status']?.toString().toLowerCase() ?? ''; // // تحديد الحالة // if (status == 'waiting' || status == 'searching') { // currentRideState.value = RideState.searching; // isSearchingWindow = true; // } else if (status == 'apply' || status == 'applied') { // currentRideState.value = RideState.driverApplied; // statusRide = 'Apply'; // // الاشتراك في موقع السائق // socket.emit('subscribe_driver_location', { // 'ride_id': rideId, // 'driver_id': driverId, // }); // // استعادة بيانات السائق // if (rideData['driver_info'] != null) { // var dInfo = rideData['driver_info']; // passengerName = dInfo['first_name']?.toString() ?? ''; // driverPhone = dInfo['phone']?.toString() ?? ''; // model = dInfo['model']?.toString() ?? ''; // licensePlate = dInfo['license_plate']?.toString() ?? ''; // } // } else if (status == 'arrived') { // currentRideState.value = RideState.driverArrived; // statusRide = 'Arrived'; // isDriverArrivePassenger = true; // } else if (status == 'begin' || status == 'started') { // currentRideState.value = RideState.inProgress; // statusRide = 'Begin'; // rideTimerBegin = true; // // استعادة Polyline // if (rideData['polyline'] != null) { // _restorePolyline(rideData['polyline']); // } // rideIsBeginPassengerTimer(); // } // update(); // _startMasterTimer(); // للـ Safety Checks // } catch (e) { // Log.print('[Startup] Error: $e'); // currentRideState.value = RideState.noRide; // _startMasterTimer(); // } // } // Future _restorePolyline(String polylineString) async { // try { // List points = // await compute(decodePolylineIsolate, polylineString); // polylineCoordinates.clear(); // polylineCoordinates.addAll(points); // polyLines.clear(); // polyLines = { // ...polyLines, // Polyline( // polylineId: const PolylineId('route_direct'), // points: polylineCoordinates, // color: const Color(0xFF2196F3), // width: 6, // ) // }; // update(); // } catch (e) { // Log.print('Error restoring polyline: $e'); // } // } // // متغير لمنع التكرار (Race Condition Guard) // bool _isAcceptanceProcessed = false; // // ============================================================================== // // الدالة الموحدة لمعالجة القبول (من السوكيت أو FCM) // // ============================================================================== // Future processRideAcceptance( // {Map? driverData, required String source}) async { // // 1. الحماية: إذا تم المعالجة مسبقاً، تجاهل // // نستثني حالة واحدة: إذا كنا في وضع البحث (Searching) نقبل الأمر // // إذا كنا في driverApplied، نتجاهل الأمر // if (currentRideState.value != RideState.searching && // _isAcceptanceProcessed) { // Log.print("✋ Ignored Acceptance from $source. Already processed."); // return; // } // _rideAcceptedViaSource = source; // _socketLocationUpdatesCount = 0; // _isAcceptanceProcessed = true; // قفل الباب // Log.print("🚀 Winner: $source triggered acceptance! Processing..."); // // 2. إيقاف جميع التايمرات القديمة فوراً // _masterTimer?.cancel(); // // stopSearchingTimer(); // إذا كان لديك تايمر للبحث // // 3. تحديث الحالة في الواجهة // currentRideState.value = RideState.driverApplied; // statusRide = 'Apply'; // isSearchingWindow = false; // // 4. معالجة البيانات (تعبئة المتغيرات) // if (driverData != null && driverData.isNotEmpty) { // Log.print("📥 Populating Data from $source payload..."); // _fillDriverDataLocally(driverData); // } else { // Log.print("⚠️ No Data in Payload. Fallback to API."); // await getUpdatedRideForDriverApply(rideId); // } // // أضف هذا بعد السطر الذي تستدعي فيه RideTrackingNative // await IosLiveActivityService.startRideActivity( // rideId: rideId, // driverName: driverName ?? 'السائق', // carDetails: '$make • $carColor', // etaText: stringRemainingTimeToPassenger, // progress: 0.0, // ); // // إشعارات (الأسعار، الأمان...) // _showRideStartNotifications(); // final etaText = stringRemainingTimeToPassenger; // مثال: "8 دقائق" // final carInfo = '$make • $model • $licensePlate'; // await RideLiveNotification.showDriverOnWay( // driverName: driverName, // etaText: etaText, // carInfo: carInfo, // ); // update(); // تحديث الواجهة لإظهار بيانات السائق // // 5. 🔥 العمليات الجغرافية (المسار والوقت) 🔥 // // أ) جلب موقع السائق الأولي // await getDriverCarsLocationToPassengerAfterApplied(); // _startSocketWatchdog(); // // ب) رسم المسار وحساب الوقت // if (driverCarsLocationToPassengerAfterApplied.isNotEmpty) { // LatLng driverPos = driverCarsLocationToPassengerAfterApplied.last; // // نستخدم الدالة الموحدة الجديدة للحساب والرسم // await calculateDriverToPassengerRoute(driverPos, passengerLocation); // // ج) تشغيل التايمر المحلي (للعد التنازلي فقط) // startTimerFromDriverToPassengerAfterApplied(); // } // final int timeToPassengerSeconds = // timeToPassengerFromDriverAfterApplied; // مثلاً من السيرفر // final double distanceDriverToPassengerMeters = // double.tryParse(distanceByPassenger.toString()) ?? 0.0; // // [PiP] تفعيل PiP عند بدء الرحلة (سيدخل وضع النافذة العائمة عند خروج المستخدم) // PipService.enablePip(); // // 6. 🔥 القرار الذكي: هل نبدأ البولينج أم نعتمد على السوكيت؟ 🔥 // if (source == "Socket" && isSocketConnected) { // // ✅ السوكيت هو من أخبرنا بالقبول — نثق به ولا نشغل البولينج // Log.print( // "🧠 Smart Mode: Socket accepted ride. Skipping polling, relying on WebSocket."); // // الـ watchdog وحده يكفي كشبكة أمان // } else { // // ⚠️ القبول من FCM أو Polling — نشغل البولينج كالمعتاد // Log.print("🔄 Fallback Mode: $source accepted ride. Starting polling."); // _startDriverLocationPollingWithTimer(); // } // } // Timer? _watchdogTimer; // void _startSocketWatchdog() { // _watchdogTimer?.cancel(); // Log.print("👀 Starting Socket Watchdog (Hybrid Mode)..."); // _watchdogTimer = Timer.periodic(const Duration(seconds: 5), (timer) async { // if (currentRideState.value != RideState.driverApplied && // currentRideState.value != RideState.driverArrived && // currentRideState.value != RideState.inProgress) { // timer.cancel(); // return; // } // final lastTime = _lastSocketLocationTime ?? // DateTime.now().subtract(const Duration(minutes: 1)); // final difference = DateTime.now().difference(lastTime).inSeconds; // if (difference < 15 && isSocketConnected) { // // ✅ السوكيت صحي — تأكد أن البولينج متوقف إذا كنا في وضع السوكيت // if (_locationPollingTimer != null && // _rideAcceptedViaSource == "Socket") { // Log.print("✅ Socket recovered. Stopping polling fallback."); // _stopDriverLocationPolling(); // } // } else if (difference >= 15 && difference < 30) { // // ⚠️ تأخر متوسط — جلب واحد فقط // Log.print("⚠️ Socket silent for ${difference}s. Single API Poll..."); // await getDriverCarsLocationToPassengerAfterApplied(); // } else if (difference >= 30) { // // 🔴 السوكيت ميت — تفعيل البولينج الكامل كـ fallback // if (_locationPollingTimer == null) { // Log.print( // "🔴 Socket dead for ${difference}s. Activating polling fallback!"); // _startDriverLocationPollingWithTimer(); // } else { // // البولينج يعمل بالفعل، فقط أكمل // await getDriverCarsLocationToPassengerAfterApplied(); // } // } // }); // } // // قائمة بأنصاف الأقطار (بالأمتار) لكل مرحلة // final List _searchRadii = [ // 2400, // 3000, // 3100 // ]; // 0 ثانية، 30 ثانية، 60 ثانية // // المدة الزمنية لكل مرحلة بحث (بالثواني) // final int _searchPhaseDurationSeconds = 30; // // المهلة الإجمالية للبحث قبل عرض خيار زيادة السعر // final int _totalSearchTimeoutSeconds = 90; // 90 ثانية // // --- noRide throttling --- // int _noRideSearchCount = 0; // final int _noRideMaxTries = 3; // نفذ البحث 6 مرات فقط // final int _noRideIntervalSec = 5; // بين كل محاولة وأخرى 5 ثواني // DateTime? _noRideNextAllowed; // متى نسمح بالمحاولة التالية // bool _noRideSearchCapped = false; // وصلنا للحد وتوقفنا // // ============== new design to manage ride state ============== // // === 1. حالة الرحلة والمؤقت الرئيسي (Single Source of Truth) === // Rx currentRideState = RideState.noRide.obs; // Timer? _masterTimer; // final int _pollingIntervalSeconds = 13; // فاصل زمني موحد للاستعلام // void _startMasterTimer() { // // نضمن أن مؤقت واحد فقط يعمل في أي وقت // _masterTimer?.cancel(); // _masterTimer = // Timer.periodic(Duration(seconds: _pollingIntervalSeconds), (_) { // _handleRideState(currentRideState.value); // }); // } // void stopAllTimers() { // Log.print('🛑 FORCE STOP: Stopping ALL Timers and Streams 🛑'); // // 1. إيقاف الماكينة الرئيسية // _masterTimer?.cancel(); // _masterTimer = null; // // 2. إيقاف مؤقتات تتبع السائق // timerToPassengerFromDriverAfterApplied?.cancel(); // _timer?.cancel(); // _uiCountdownTimer?.cancel(); // // 3. إيقاف مؤقتات الخريطة // _animationTimers.forEach((key, timer) => timer.cancel()); // _animationTimers.clear(); // // 4. إغلاق الستريمز // if (!_rideStatusStreamController.isClosed) // _rideStatusStreamController.close(); // if (!_beginRideStreamController.isClosed) // _beginRideStreamController.close(); // if (!timerController.isClosed) timerController.close(); // // 5. تصفير العدادات لمنع إعادة الدخول // isTimerRunning = false; // isBeginRideFromDriverRunning = false; // _isFetchingDriverLocation = false; // update(); // } // final int _maxNoRideSearch = 3; // عدد المرات القصوى // final int _noRideDelaySeconds = 6; // الفاصل الزمني بين كل بحث // // // // // // !!! يرجى استبدال الدالة القديمة بالكامل بهذه الدالة الجديدة !!! // // // // // bool _isStateProcessing = false; // Future _handleRideState(RideState state) async { // if (_isRatingScreenOpen) { // Log.print('⛔ Rating Screen is Open. Skipping Logic.'); // stopAllTimers(); // تأكيد إضافي للإيقاف // return; // } // Log.print('Handling state: $state'); // // int effectivePollingInterval = _pollingIntervalSeconds; // // الحصول على الفاصل الزمني من الخريطة // int effectivePollingInterval = // _pollingIntervals[state] ?? _pollingIntervalSeconds; // switch (state) { // case RideState.noRide: // final now = DateTime.now(); // if (_noRideSearchCount >= _noRideMaxTries) { // if (!_noRideSearchCapped) { // _noRideSearchCapped = true; // Log.print('[noRide] search capped at $_noRideMaxTries attempts'); // } // break; // } // if (_noRideNextAllowed != null && now.isBefore(_noRideNextAllowed!)) { // break; // } // _noRideSearchCount++; // Log.print('_noRideSearchCount: $_noRideSearchCount'); // _noRideNextAllowed = now.add(Duration(seconds: _noRideIntervalSec)); // String currentCarType = box.read(BoxName.carType) ?? 'yet'; // getCarsLocationByPassengerAndReloadMarker(); // getNearestDriverByPassengerLocation(); // break; // case RideState.cancelled: // Log.print('[handleRideState] Ride cancelled. Stopping polling.'); // stopAllTimers(); // // effectivePollingInterval = 3600; // break; // case RideState.preCheckReview: // stopAllTimers(); // _checkLastRideForReview(); // break; // case RideState.searching: // // Guard: Don't poll if ride is not registered yet // if (rideId == 'yet' || rideId.isEmpty) break; // // 1. التحقق من حالة الطلب (هل قبله أحد؟) عبر البولينج كشبكة أمان // try { // String statusFromServer = await getRideStatus(rideId); // if (statusFromServer == 'Apply' || statusFromServer == 'Applied') { // await processRideAcceptance(source: "Polling"); // break; // } // } catch (e) { // Log.print('Error polling getRideStatus: $e'); // } // final now = DateTime.now(); // final int elapsedSeconds = now.difference(_searchStartTime!).inSeconds; // // انتهاء وقت البحث الكلي // if (elapsedSeconds > _totalSearchTimeoutSeconds) { // stopAllTimers(); // currentRideState.value = RideState.noRide; // isSearchingWindow = false; // update(); // _showIncreaseFeeDialog(); // break; // } // // 2. إدارة مراحل البحث (توسيع النطاق) // // السيناريو الجديد: لا نقوم بالقصف العشوائي، نرسل بناء على المرحلة أو مرور وقت كافٍ لدخول سائقين جدد // int targetPhase = // (elapsedSeconds / _searchPhaseDurationSeconds).floor(); // if (targetPhase >= _searchRadii.length) { // targetPhase = _searchRadii.length - 1; // } // // هل تغيرت المرحلة (توسع النطاق)؟ أو هل مر 10 ثواني منذ آخر محاولة إرسال؟ // // هذا يمنع إرسال الإشعار في كل دورة (كل 5 ثواني) ويقلل الازعاج // bool isNewPhase = targetPhase > _currentSearchPhase; // bool timeToScanForNewDrivers = // (elapsedSeconds % 15 == 0); // كل 15 ثانية نفحص الدخول الجديد // if (isNewPhase || timeToScanForNewDrivers || elapsedSeconds < 5) { // _currentSearchPhase = targetPhase; // int currentRadius = _searchRadii[_currentSearchPhase]; // Log.print( // '[Search Logic] Scanning for drivers. Phase: $_currentSearchPhase, Radius: $currentRadius'); // // استدعاء دالة الإشعار الذكية // // _findAndNotifyNearestDrivers(currentRadius); // } // // تحديث نصوص الواجهة // if (elapsedSeconds < 5) { // driversStatusForSearchWindow = 'Your order is being prepared'.tr; // } else if (elapsedSeconds < 15) { // driversStatusForSearchWindow = 'Your order sent to drivers'.tr; // } else { // driversStatusForSearchWindow = // 'The drivers are reviewing your request'.tr; // } // update(); // break; // case RideState.driverApplied: // // effectivePollingInterval = 10; // if (!_isDriverAppliedLogicExecuted) { // Log.print('[handleRideState] Execution driverApplied logic.'); // rideAppliedFromDriver(true); // _isDriverAppliedLogicExecuted = true; // } // if (!isSocketConnected) { // try { // String statusFromServer = await getRideStatus(rideId); // if (statusFromServer == 'Arrived') { // currentRideState.value = RideState.driverArrived; // break; // } else if (statusFromServer == 'Begin' || // statusFromServer == 'inProgress') { // processRideBegin(); // break; // } // } catch (e) { // Log.print('Error polling for Arrived/Begin status: $e'); // } // } // // 🧠 جلب الموقع فقط إذا السوكيت غير صحي // if (!_isSocketHealthy()) { // getDriverCarsLocationToPassengerAfterApplied(); // } // break; // case RideState.driverArrived: // if (!_isDriverArrivedLogicExecuted) { // _isDriverArrivedLogicExecuted = true; // startTimerDriverWaitPassenger5Minute(); // driverArrivePassengerDialoge(); // } // // effectivePollingInterval = 8; // break; // case RideState.inProgress: // // effectivePollingInterval = 6; // if (!isSocketConnected) { // try { // String statusFromServer = await getRideStatus(rideId); // // !!! هنا التغيير الجذري !!! // if (statusFromServer == 'Finished' || // statusFromServer == 'finished') { // Log.print( // '🏁 DETECTED FINISHED: Killing processes and forcing Review.'); // // 1. قتل العمليات فوراً // stopAllTimers(); // // 2. تغيير الحالة الداخلية لمنع أي كود آخر من العمل // currentRideState.value = RideState.preCheckReview; // // 3. تنظيف الواجهة // tripFinishedFromDriver(); // // 4. استدعاء شاشة التقييم فوراً // _checkLastRideForReview(); // return; // خروج نهائي من الدالة لمنع أي كود بالأسفل من التنفيذ // } // } catch (e) { // Log.print('Error polling status: $e'); // } // } // // بقية كود التتبع العادي (لن يتم الوصول إليه إذا انتهت الرحلة) // if (!_isRideBeginLogicExecuted) { // _isRideBeginLogicExecuted = true; // _executeBeginRideLogic(); // } // // 🧠 جلب الموقع فقط إذا السوكيت غير صحي // if (!_isSocketHealthy()) { // getDriverCarsLocationToPassengerAfterApplied(); // } // break; // case RideState.finished: // tripFinishedFromDriver(); // stopAllTimers(); // effectivePollingInterval = 3600; // break; // } // // تحديث الماكينة الرئيسية إذا تغير الفاصل الزمني // _startMasterTimerWithInterval(effectivePollingInterval); // } // int _masterIntervalSeconds = -1; // void _startMasterTimerWithInterval(int seconds) { // // نفس الانترفَل؟ لا تعمل شيء // if (_masterTimer != null && _masterIntervalSeconds == seconds) return; // _masterIntervalSeconds = seconds; // _masterTimer?.cancel(); // _masterTimer = Timer.periodic(Duration(seconds: seconds), (_) { // _handleRideState(currentRideState.value); // }); // } // Future _checkInitialRideStatus() async { // // 1. جلب الحالة من السيرفر (باستخدام getRideStatusFromStartApp) // await getRideStatusFromStartApp(); // String _status = rideStatusFromStartApp['data']['status']; // // Log.print('rideStatusFromStartApp: ${rideStatusFromStartApp}'); // // Log.print('_status: ${_status}'); // if (_status == 'waiting' || _status == 'Apply' || _status == 'Begin') { // // رحلة جارية // rideId = rideStatusFromStartApp['data']['rideId'].toString(); // currentRideState.value = _status == 'waiting' // ? RideState.searching // : _status == 'Apply' // ? RideState.driverApplied // : _status == 'Begin' // ? RideState.inProgress // : _status == 'Cancel' // ? RideState.cancelled // : RideState.noRide; // } else if (_status == 'Finished') { // // رحلة منتهية/ملغاة // if (rideStatusFromStartApp['data']['needsReview'] == 1) { // currentRideState.value = RideState.preCheckReview; // } else { // currentRideState.value = RideState.noRide; // } // } else { // currentRideState.value = RideState.noRide; // } // // بدء المعالجة الفورية // _handleRideState(currentRideState.value); // } // Future _checkLastRideForReview() async { // Log.print('⭐ FORCE OPEN RATING PAGE (Get.to mode)'); // // جلب البيانات // await getRideStatusFromStartApp(); // if (rideStatusFromStartApp['data'] == null) { // currentRideState.value = RideState.noRide; // _startMasterTimer(); // return; // } // String needsReview = // rideStatusFromStartApp['data']['needsReview'].toString(); // if (needsReview == '1') { // _isRatingScreenOpen = true; // // 1. تجهيز البيانات (Arguments) // var args = { // 'driverId': rideStatusFromStartApp['data']['driver_id'].toString(), // 'rideId': rideStatusFromStartApp['data']['rideId'].toString(), // 'driverName': rideStatusFromStartApp['data']['driverName'], // 'price': rideStatusFromStartApp['data']['price'], // }; // // 2. استخدام Get.to مع await (هذا هو الحل الجذري) // // الكود سيتوقف هنا ولن يكمل التنفيذ حتى يتم إغلاق صفحة التقييم // await Get.to( // () => RatingDriverBottomSheet(), // arguments: args, // تمرير البيانات بالطريقة التي تريدها // preventDuplicates: true, // لمنع فتح الصفحة مرتين // popGesture: false, // لمنع السحب للرجوع (في iOS) // ); // // 3. هذا الكود لن يتنفذ إلا بعد أن يضغط المستخدم "تم" في التقييم ويغلق الصفحة // Log.print('✅ Rating Page Closed. Resetting App.'); // _isRatingScreenOpen = false; // restCounter(); // currentRideState.value = RideState.noRide; // _startMasterTimer(); // إعادة تشغيل البحث الآن فقط // } else { // currentRideState.value = RideState.noRide; // _startMasterTimer(); // } // } // void startSearchingForDriver() async { // // ✅ منع الضغط المزدوج // if (currentRideState.value == RideState.searching) { // return; // } // // 1. تحديث الحالة الأولية // isSearchingWindow = true; // currentRideState.value = RideState.searching; // driversStatusForSearchWindow = 'Searching for nearby drivers...'.tr; // update(); // // 2. إرسال الطلب للسيرفر (add_ride.php) // bool rideCreated = await postRideDetailsToServer(); // if (!rideCreated) { // // فشل الإنشاء // isSearchingWindow = false; // currentRideState.value = RideState.noRide; // mySnackbarWarning("Could not create ride. Please try again.".tr); // update(); // return; // } // // 3. نجاح الإنشاء: إضافة لجدول الانتظار المحلي (اختياري حسب منطقك) // _addRideToWaitingTable(); // // 4. 🔥 الاتصال بالسوكيت فوراً وانتظار الرد الحقيقي 🔥 // // نغلق أي اتصال سابق ونبدأ اتصالاً جديداً مخصصاً لهذه الرحلة // initConnectionWithSocket(); // // تشغيل الماكينة الرئيسية للمراقبة (كحماية إضافية) // // _startMasterTimer(); // } // /// دالة لإظهار النافذة المنبثقة لزيادة السعر // void _showIncreaseFeeDialog() { // Get.dialog( // CupertinoAlertDialog( // title: Text("No drivers accepted your request yet".tr), // content: Text( // "Increasing the fare might attract more drivers. Would you like to increase the price?" // .tr), // actions: [ // CupertinoDialogAction( // child: Text("Cancel Ride".tr, // style: TextStyle(color: AppColor.redColor)), // onPressed: () { // Get.back(); // changeCancelRidePageShow(); // // cancelRide(); // دالة إلغاء الرحلة // }, // ), // CupertinoDialogAction( // child: Text("Increase Fare".tr, // style: TextStyle(color: AppColor.greenColor)), // onPressed: () { // Get.back(); // // هنا يمكنك عرض نافذة أخرى لإدخال السعر الجديد // // وبعدها استدعاء الدالة التالية // // كمثال، سنزيد السعر بنسبة 10% // double newPrice = totalPassenger * 1.10; // increasePriceAndRestartSearch(newPrice); // }, // ), // ], // ), // barrierDismissible: false, // ); // } // /// دالة لتحديث السعر وإعادة بدء البحث // Future increasePriceAndRestartSearch(double newPrice) async { // totalPassenger = newPrice; // update(); // // await CRUD().post(link: AppLink.updateRides, payload: { // // "id": rideId, // // "price": newPrice.toStringAsFixed(2), // // }); // CRUD().post(link: "${AppLink.server}/ride/rides/update.php", payload: { // "id": rideId, // "price": newPrice.toStringAsFixed(2), // }); // // تصفير القائمة لأن السعر تغير، ويجب إبلاغ الجميع (حتى من وصله الإشعار سابقاً) // Log.print( // '[increasePrice] Price changed. Clearing notified list to resend.'); // notifiedDrivers.clear(); // _searchStartTime = DateTime.now(); // _currentSearchPhase = 0; // isSearchingWindow = true; // update(); // _startMasterTimer(); // } // /// (دالة جديدة موحدة) // /// هذه هي الدالة الوحيدة المسؤولة عن بدء عملية قبول الرحلة. // /// يتم استدعاؤها إما من إشعار Firebase أو من البحث الدوري (Polling). // /// هي تحتوي على "حارس البوابة" لمنع تضارب السباق. // /// (دالة موحدة وصارمة) // /// تستدعى من FCM أو من Polling عند اكتشاف قبول السائق // /// تمنع تضارب السباق وتضمن تنفيذ المنطق مرة واحدة فقط // /// متغير لمنع التكرار (Race Condition Guard) // Future driverArrivePassengerDialoge() { // return Get.defaultDialog( // barrierDismissible: false, // title: 'Hi ,I Arrive your site'.tr, // titleStyle: AppStyle.title, // middleText: 'Please go to Car Driver'.tr, // middleTextStyle: AppStyle.title, // confirm: MyElevatedButton( // title: 'Ok I will go now.'.tr, // onPressed: () { // NotificationService.sendNotification( // target: driverToken.toString(), // title: 'Hi ,I will go now'.tr, // body: 'I will go now'.tr, // isTopic: false, // Important: this is a token // tone: 'ding', // driverList: [], // category: 'Hi ,I will go now', // ); // Get.back(); // remainingTime = 0; // update(); // })); // } // /// (دالة خاصة جديدة) // /// تحتوي على كل المنطق الفعلي لبدء الرحلة. // /// // Timer? _waitPassengerTimer; // static const int _waitPassengerTotalSeconds = 300; // int _waitPassengerElapsedSeconds = 0; // /// **إيقاف عداد انتظار الراكب (Stop Wait Timer)** // /// // /// تقوم هذه الدالة بإلغاء التايمر النشط فوراً لتحرير الموارد ومنع تسريب الذاكرة. // /// // /// * [resetUI]: (اختياري) عند وضعه `true`، يتم تصفير العدادات وتحديث الواجهة لإخفاء التوقيت القديم. // void _stopWaitPassengerTimer({bool resetUI = false}) { // // 1. الإلغاء الآمن للتايمر (Safe Cancellation) // _waitPassengerTimer?.cancel(); // _waitPassengerTimer = null; // // 2. تصفير قيم الواجهة (Reset State) // if (resetUI) { // progressTimerDriverWaitPassenger5Minute = 0.0; // remainingTimeDriverWaitPassenger5Minute = 0; // stringRemainingTimeDriverWaitPassenger5Minute = '00:00'; // // ✅ تحديث الواجهة فوراً (GetX) // update(); // } // } // void _executeBeginRideLogic() { // Log.print('[executeBeginRideLogic] تنفيذ منطق بدء الرحلة...'); // _stopWaitPassengerTimer(resetUI: true); // <-- إضافة // // 1. تصفير كل عدادات ما قبل الرحلة // timeToPassengerFromDriverAfterApplied = 0; // remainingTime = 0; // remainingTimeToPassengerFromDriverAfterApplied = 0; // remainingTimeDriverWaitPassenger5Minute = 0; // // 2. تحديث الحالة والواجهة // rideTimerBegin = true; // statusRide = 'Begin'; // isDriverInPassengerWay = false; // isDriverArrivePassenger = false; // لإخفاء واجهة "السائق وصل" // // 3. (من كود الإشعار الخاص بك) // box.write(BoxName.passengerWalletTotal, '0'); // update(); // تحديث الواجهة قبل بدء المؤقتات // // 4. بدء مؤقتات الرحلة الفعلية // rideIsBeginPassengerTimer(); // مؤقت عداد مدة الرحلة // // runWhenRideIsBegin(); // مؤقت تتبع موقع السائق أثناء الرحلة // // 5. إشعار الراكب (من كود الإشعار الخاص بك) // NotificationController().showNotification( // 'Trip is Begin'.tr, // 'The trip has started! Feel free to contact emergency numbers, share your trip, or activate voice recording for the journey' // .tr, // 'start'); // } // // متغير لمنع التكرار // bool _isRideStartedProcessed = false; // /// **معالجة بدء الرحلة الموحدة (Unified Ride Start Handler)** // /// // /// تستدعى عند استلام حدث بدء الرحلة سواء من السوكيت أو FCM. // /// تضمن انتقال التطبيق لحالة [RideState.inProgress] مرة واحدة فقط. // Future processRideBegin({String source = "Unknown"}) async { // // منطقك الحالي // if (currentRideState.value == RideState.inProgress || // _isRideStartedProcessed) { // return; // } // _isRideStartedProcessed = true; // currentRideState.value = RideState.inProgress; // statusRide = 'Begin'; // // إيقاف مؤقت الانتظار // remainingTimeDriverWaitPassenger5Minute = 0; // _stopWaitPassengerTimer(); // // 1) بيانات السائق والرحلة // rideIsBeginPassengerTimer(); // update(); // } // late Duration durationToAdd; // late DateTime newTime = DateTime.now(); // int hours = 0; // int minutes = 0; // // --- إضافة جديدة: للوصول إلى وحدة التحكم بالروابط --- // final DeepLinkController _deepLinkController = // Get.isRegistered() // ? Get.find() // : Get.put(DeepLinkController()); // // ------------------------------------------------ // void onChangedPassengerCount(int newValue) { // selectedPassengerCount = newValue; // update(); // } // void onChangedPassengersChoose() { // isPassengerChosen = true; // update(); // } // void getCurrentLocationFormString() async { // currentLocationToFormPlaces = true; // currentLocationString = 'Waiting for your location'.tr; // await getLocation(); // currentLocationString = passengerLocation.toString(); // newStartPointLocation = passengerLocation; // update(); // } // List coordinatesWithoutEmpty = []; // void getMapPointsForAllMethods() async { // clearPolyline(); // isMarkersShown = false; // isWayPointStopsSheetUtilGetMap = false; // isWayPointSheet = false; // durationToRide = 0; // distanceOfDestination = 0; // wayPointSheetHeight = 0; // remainingTime = 25; // haveSteps = true; // // Filter out empty value // coordinatesWithoutEmpty = // placesCoordinate.where((coord) => coord.isNotEmpty).toList(); // latestPosition = LatLng( // double.parse(coordinatesWithoutEmpty.last.split(',')[0]), // double.parse(coordinatesWithoutEmpty.last.split(',')[1])); // for (var i = 0; i < coordinatesWithoutEmpty.length; i++) { // if ((i + 1) < coordinatesWithoutEmpty.length) { // await getMapPoints( // coordinatesWithoutEmpty[i].toString(), // coordinatesWithoutEmpty[i + 1].toString(), // i, // ); // if (i == 0) { // startNameAddress = data[0]['start_address']; // } // if (i == coordinatesWithoutEmpty.length) { // endNameAddress = data[0]['end_address']; // } // } // } // // isWayPointStopsSheet = false; // if (haveSteps) { // String latestWaypoint = // placesCoordinate.lastWhere((coord) => coord.isNotEmpty); // latestPosition = LatLng( // double.parse(latestWaypoint.split(',')[0]), // double.parse(latestWaypoint.split(',')[1]), // ); // } // updateCameraForDistanceAfterGetMap(); // changeWayPointStopsSheet(); // bottomSheet(); // showBottomSheet1(); // update(); // } // void convertHintTextPlaces(int index, var res) { // if (placeListResponseAll[index].isEmpty) { // placeListResponseAll[index] = res; // hintTextwayPointStringAll[index] = 'Search for your Start point'.tr; // update(); // } else { // hintTextwayPointStringAll[index] = res['name']; // currentLocationStringAll[index] = res['name']; // placesCoordinate[index] = // '${res['geometry']['location']['lat']},${res['geometry']['location']['lng']}'; // placeListResponseAll[index] = []; // allTextEditingPlaces[index].clear(); // // double lat = wayPoint0[index]['geometry']['location']['lat']; // // double lng = wayPoint0[index]['geometry']['location']['lng']; // // newPointLocation0 = LatLng(lat, lng); // update(); // Get.back(); // } // } // void convertHintTextDestinationNewPlaces(int index) { // if (placesDestination.isEmpty) { // hintTextDestinationPoint = 'Search for your destination'.tr; // update(); // } else { // var res = placesDestination[index]; // hintTextDestinationPoint = res['displayName']?['text'] ?? // res['formattedAddress'] ?? // 'Unknown Place'; // double? lat = res['location']?['latitude']; // double? lng = res['location']?['longitude']; // if (lat != null && lng != null) { // newMyLocation = LatLng(lat, lng); // // 🔥 الحل: تحريك الكاميرا فوراً للهدف حتى لا يتم مسحه عند إغلاق الكيبورد 🔥 // mapController // ?.animateCamera(CameraUpdate.newLatLngZoom(newMyLocation, 16)); // } // update(); // } // } // void convertHintTextDestinationNewPlacesFromRecent( // List recentLocations, int index) { // hintTextDestinationPoint = recentLocations[index]['name']; // double lat = recentLocations[index]['latitude']; // double lng = recentLocations[index]['longitude']; // newMyLocation = LatLng(lat, lng); // // 🔥 تحريك الكاميرا فوراً 🔥 // mapController?.animateCamera(CameraUpdate.newLatLngZoom(newMyLocation, 16)); // update(); // } // // final mainBottomMenuMap = GlobalKey(); // void changeBottomSheetShown({bool? forceValue}) { // if (forceValue != null) { // isBottomSheetShown = forceValue; // } else { // isBottomSheetShown = !isBottomSheetShown; // } // heightBottomSheetShown = isBottomSheetShown == true ? 250 : 0; // update(); // } // void changeCashConfirmPageShown() { // isCashConfirmPageShown = !isCashConfirmPageShown; // isCashSelectedBeforeConfirmRide = true; // cashConfirmPageShown = isCashConfirmPageShown == true ? 250 : 0; // // to get or sure picker point for origin //todo // // isPickerShown = true; // // clickPointPosition(); // update(); // } // void changePaymentMethodPageShown() { // isPaymentMethodPageShown = !isPaymentMethodPageShown; // paymentPageShown = isPaymentMethodPageShown == true ? Get.height * .6 : 0; // update(); // } // void changeMapType() { // mapType = !mapType; // // heightButtomSheetShown = isButtomSheetShown == true ? 240 : 0; // update(); // } // void changeMapTraffic() { // mapTrafficON = !mapTrafficON; // update(); // } // void changeisAnotherOreder(bool val) { // isAnotherOreder = val; // update(); // } // void changeIsWhatsAppOrder(bool val) { // isWhatsAppOrder = val; // update(); // } // void sendSMS(String to) async { // // Get the driver's phone number. // String driverPhone = // (dataCarsLocationByPassenger['message'][carsOrder]['phone'].toString()); // // Format the message. // String message = // 'Hi! This is ${(box.read(BoxName.name).toString().split(' ')[0]).toString()}.\n I am using ${box.read(AppInformation.appName)} to ride with $passengerName as the driver. $passengerName \nis driving a $model\n with license plate $licensePlate.\n I am currently located at $passengerLocation.\n If you need to reach me, please contact the driver directly at\n\n $driverPhone.'; // // Launch the URL to send the SMS. // launchCommunication('sms', to, message); // } // String formatSyrianPhone(String phone) { // // Remove spaces and + // phone = phone.replaceAll(' ', '').replaceAll('+', ''); // // If starts with 00963 → remove 00 → 963 // if (phone.startsWith('00963')) { // phone = phone.replaceFirst('00963', '963'); // } // // If starts with 0963 (common mistake) → fix it // if (phone.startsWith('0963')) { // phone = phone.replaceFirst('0963', '963'); // } // // If starts with 963 (already correct) // if (phone.startsWith('963')) { // return phone; // nothing to do // } // // If starts with 09 → remove leading 0 → add 963 // if (phone.startsWith('09')) { // return '963' + phone.substring(1); // 9xxxxxxxxx // } // // If starts with 9xxxxxxxxx (no country code) // if (phone.startsWith('9') && phone.length == 9) { // return '963' + phone; // } // // Otherwise return raw phone // return phone; // } // void sendWhatsapp(String to) async { // // Normalize phone number before sending // String formattedPhone = formatSyrianPhone(to); // // Message body // String message = // '${'${'Hi! This is'.tr} ${(box.read(BoxName.name).toString().split(' ')[0]).toString()}.\n${' I am using'.tr}'} ${AppInformation.appName}${' to ride with'.tr} $passengerName${' as the driver.'.tr} $passengerName \n${'is driving a '.tr}$model\n${' with license plate '.tr}$licensePlate.\n${' I am currently located at '.tr} https://www.google.com/maps/place/${passengerLocation.latitude},${passengerLocation.longitude}.\n${' If you need to reach me, please contact the driver directly at'.tr}\n\n $driverPhone.'; // // Send WhatsApp message // launchCommunication('whatsapp', formattedPhone, message); // } // void changeCancelRidePageShow() { // showCancelRideBottomSheet(); // isCancelRidePageShown = !isCancelRidePageShown; // // : cancelRide(); // update(); // } // void getDrawerMenu() { // heightMenuBool = !heightMenuBool; // widthMapTypeAndTraffic = heightMenuBool == true ? 0 : 50; // heightMenu = heightMenuBool == true ? 80 : 0; // widthMenu = heightMenuBool == true ? 110 : 0; // update(); // } // calcualateDistsanceInMetet(LatLng prev, current) async { // double distance2 = Geolocator.distanceBetween( // prev.latitude, // prev.longitude, // current.latitude, // current.longitude, // ); // return distance2; // } // StreamController _timerStreamController = StreamController(); // Stream get timerStream => _timerStreamController.stream; // bool isTimerFromDriverToPassengerAfterAppliedRunning = true; // bool isTimerRunning = false; // Flag to track if the timer is running // int beginRideInterval = 10; // Interval in seconds for getBeginRideFromDriver // void startTimerFromDriverToPassengerAfterApplied() { // stopTimerFromDriverToPassengerAfterApplied(); // if (isTimerRunning) return; // isTimerRunning = true; // isTimerFromDriverToPassengerAfterAppliedRunning = true; // int secondsElapsed = 0; // // استدعاء فوري لأول مرة // // getDriverCarsLocationToPassengerAfterApplied(); // Timer.periodic(const Duration(seconds: 1), (timer) { // // --- التغيير الجوهري هنا --- // // شرط الإيقاف: نتوقف فقط إذا انتهت الرحلة أو ألغيت، أو تم إيقاف التايمر يدوياً // // لم نعد نعتمد على تجاوز الوقت المقدر (timeToPassenger) كشرط للإيقاف // bool isRideActive = (statusRide == 'Apply' || // statusRide == 'Arrived' || // statusRide == 'Begin' || // currentRideState.value == RideState.driverApplied || // currentRideState.value == RideState.driverArrived || // currentRideState.value == RideState.inProgress); // if (!isRideActive || !isTimerFromDriverToPassengerAfterAppliedRunning) { // timer.cancel(); // isTimerRunning = false; // if (!_timerStreamController.isClosed) { // _timerStreamController.close(); // } // return; // } // secondsElapsed++; // if (!_timerStreamController.isClosed) { // _timerStreamController.add(secondsElapsed); // } // // تحديث الواجهة للوقت المتبقي (شكلياً فقط للراكب) // // حتى لو أصبح الوقت سالباً (تأخر السائق)، سنظهره كـ 00:00 أو نتركه سالباً // remainingTimeToPassengerFromDriverAfterApplied = // timeToPassengerFromDriverAfterApplied - secondsElapsed; // if (remainingTimeToPassengerFromDriverAfterApplied < 0) { // remainingTimeToPassengerFromDriverAfterApplied = 0; // } // int minutes = // (remainingTimeToPassengerFromDriverAfterApplied / 60).floor(); // int seconds = remainingTimeToPassengerFromDriverAfterApplied % 60; // stringRemainingTimeToPassenger = // '$minutes:${seconds.toString().padLeft(2, '0')}'; // // تحويل الوقت أو المسافة إلى نسبة من 0.0 إلى 1.0 // double currentProgress = 1 - // (remainingTimeToPassengerFromDriverAfterApplied / // timeToPassengerFromDriverAfterApplied); // // 🔴 التعديل هنا: نحدث الآيفون كل 5 ثواني فقط للحفاظ على البطارية وتجنب حظر أبل // if (secondsElapsed % 5 == 0) { // double currentProgress = 1 - // (remainingTimeToPassengerFromDriverAfterApplied / // (timeToPassengerFromDriverAfterApplied == 0 // ? 1 // : timeToPassengerFromDriverAfterApplied)); // IosLiveActivityService.updateRideActivity( // status: 'waiting', // driverName: driverName ?? 'السائق', // carDetails: // '$make • $model • $carColor', // من الأفضل إظهار اللون أيضاً // etaText: stringRemainingTimeToPassenger, // progress: currentProgress.clamp(0.0, 1.0), // ); // } // // جلب موقع السائق كل 4 ثواني (Polling) ما دامت الرحلة نشطة // if (secondsElapsed % beginRideInterval == 0) { // // 2. تحديث موقع الراكب للسائق // uploadPassengerLocation(); // } else { // update(); // } // }); // } // // Function to stop the timer // void stopTimerFromDriverToPassengerAfterApplied() { // isTimerFromDriverToPassengerAfterAppliedRunning = false; // update(); // } // void startTimerDriverWaitPassenger5Minute() { // // لا تبدأ إلا إذا فعلاً وصلنا // if (currentRideState.value != RideState.driverArrived) return; // // 1) أوقف أي عداد سابق (تتبع وصول السائق) // stopTimerFromDriverToPassengerAfterApplied(); // isTimerRunning = false; // // 2) أوقف عداد الانتظار إن كان شغال من قبل (منع تكرار) // _stopWaitPassengerTimer(); // // 3) جهّز UI الانتظار // isDriverArrivePassenger = true; // isDriverInPassengerWay = false; // timeToPassengerFromDriverAfterApplied = 0; // _waitPassengerElapsedSeconds = 0; // remainingTimeDriverWaitPassenger5Minute = _waitPassengerTotalSeconds; // progressTimerDriverWaitPassenger5Minute = 0; // int m = (remainingTimeDriverWaitPassenger5Minute / 60).floor(); // int s = remainingTimeDriverWaitPassenger5Minute % 60; // stringRemainingTimeDriverWaitPassenger5Minute = // '$m:${s.toString().padLeft(2, '0')}'; // update(); // // 4) ابدأ Timer.periodic (يمكن إلغاؤه فوراً) // _waitPassengerTimer = Timer.periodic(const Duration(seconds: 1), (t) { // // أول ما تتحول إلى inProgress (أو أي حالة غير arrived) أوقف فوراً // if (currentRideState.value != RideState.driverArrived) { // _stopWaitPassengerTimer(resetUI: true); // // إخفاء واجهة "السائق وصل" إذا بدأت الرحلة // if (currentRideState.value == RideState.inProgress) { // isDriverArrivePassenger = false; // } // update(); // return; // } // _waitPassengerElapsedSeconds++; // int remaining = _waitPassengerTotalSeconds - _waitPassengerElapsedSeconds; // if (remaining < 0) remaining = 0; // remainingTimeDriverWaitPassenger5Minute = remaining; // progressTimerDriverWaitPassenger5Minute = // _waitPassengerElapsedSeconds / _waitPassengerTotalSeconds; // int minutes = (remaining / 60).floor(); // int seconds = remaining % 60; // stringRemainingTimeDriverWaitPassenger5Minute = // '$minutes:${seconds.toString().padLeft(2, '0')}'; // update(); // if (remaining == 0) { // _stopWaitPassengerTimer(); // // هنا إذا بدك: طبّق غرامة انتظار / اعرض رسالة / إلخ // } // }); // } // // Create a StreamController to manage the timer values // final timerController = StreamController(); // // Start the timer when the ride begins // void beginRideTimer() { // // Set up the timer to run every second // Timer.periodic(const Duration(seconds: 1), (timer) { // // Update the timer value and notify listeners // timerController.add(timer.tick); // update(); // }); // } // // Stop the timer when the ride ends // void stopRideTimer() { // timerController.close(); // update(); // } // Timer? _rideProgressTimer; // bool _hasShownSpeedWarning = false; // متغير لحالة التنبيه // /// **بدء مؤقت الرحلة للراكب (Passenger Ride Timer)** // /// // /// تقوم هذه الدالة بإدارة العداد الزمني للرحلة بمجرد بدئها (حالة [RideState.inProgress]). // /// // /// **المهام الرئيسية:** // /// 1. **دقة التوقيت:** تعتمد على فرق الوقت الحقيقي (`DateTime.difference`) لضمان دقة العداد حتى لو خرج المستخدم من التطبيق وعاد. // /// 2. **مراقبة السرعة:** تفحص سرعة المركبة كل ثانية، وتطلق تحذيراً [_triggerSpeedWarning] إذا تجاوزت 100 كم/س. // /// 3. **تحديث الواجهة:** تقوم بتحديث شريط التقدم والوقت المتبقي لحظياً. // /// 4. **الإيقاف التلقائي:** تتوقف تلقائياً عند انتهاء الوقت أو تغير حالة الرحلة. // void rideIsBeginPassengerTimer() { // // 1. تنظيف أي تايمر سابق // _rideProgressTimer?.cancel(); // _hasShownSpeedWarning = false; // تصفير حالة التنبيه // // 2. تحديد وقت الوصول المتوقع بدقة // DateTime now = DateTime.now(); // DateTime expectedArrivalTime = now.add(Duration(seconds: durationToRide)); // // تنسيق وقت الوصول للعرض // var arrivalTime = DateFormat('hh:mm a').format(expectedArrivalTime); // box.write(BoxName.arrivalTime, arrivalTime); // Log.print("⏳ Ride Timer Started. Duration: $durationToRide sec"); // // 3. بدء التايمر الدوري // _rideProgressTimer = // Timer.periodic(const Duration(seconds: 1), (timer) async { // // أ) شرط الإيقاف الحاسم: إذا انتهت الرحلة أو ألغيت // if (currentRideState.value != RideState.inProgress) { // timer.cancel(); // return; // } // // ب) حساب الوقت المتبقي بناءً على الساعة الحالية (أدق من العد) // DateTime currentNow = DateTime.now(); // int remainingSeconds = // expectedArrivalTime.difference(currentNow).inSeconds; // if (remainingSeconds < 0) remainingSeconds = 0; // // تحديث المتغيرات // remainingTimeTimerRideBegin = remainingSeconds; // // حساب النسبة المئوية (حماية من القسمة على صفر) // progressTimerRideBegin = // durationToRide > 0 ? 1 - (remainingSeconds / durationToRide) : 1.0; // // ج) تنسيق الوقت للعرض // int minutes = (remainingSeconds / 60).floor(); // int seconds = remainingSeconds % 60; // stringRemainingTimeRideBegin = // '$minutes:${seconds.toString().padLeft(2, '0')}'; // // نحول progressTimerRideBegin (0..1) إلى نسبة (0..100) // final percent = (progressTimerRideBegin * 100).clamp(0, 100).toInt(); // // ============================================================== // // 🔔 د) تحديث الإشعارات (هنا تم حل مشكلة الإزعاج) // // ============================================================== // // 1. تحديث الآيفون (Live Activity): يمكن تحديثه كل 5 ثواني لأنه "تحديث صامت" للشاشة فقط ولا يصدر صوتاً // if (remainingSeconds % 5 == 0 || remainingSeconds == 0) { // IosLiveActivityService.updateRideActivity( // status: 'ongoing', // ['waiting', 'ongoing'] // driverName: driverName ?? '', // carDetails: '$make • $model • $carColor', // etaText: stringRemainingTimeRideBegin, // progress: progressTimerRideBegin.clamp(0.0, 1.0), // ); // } // // 2. تحديث إشعار الهاتف العادي (RideLiveNotification): // // نحدثه كل دقيقة (60 ثانية) بدلاً من 5 ثواني حتى لا يزعج الراكب بالرنين المستمر! // if (remainingSeconds % 60 == 0 || remainingSeconds == 0) { // await RideLiveNotification.showTripInProgress( // percentage: percent, // etaText: stringRemainingTimeRideBegin, // ); // } // // ============================================================== // // هـ) منطق الإشعارات لمنتصف الرحلة (يصدر تنبيه مرة واحدة فقط) // if (progressTimerRideBegin >= 0.25 && // progressTimerRideBegin < 0.26 && // !_hasShownSpeedWarning) { // // يمكن إضافة منطق إشعار منتصف الرحلة هنا // } // // و) مراقبة السرعة (Speed Check) // if (speed > 100 && !_hasShownSpeedWarning) { // _hasShownSpeedWarning = true; // ✅ قفل التنبيه حتى لا يتكرر // _triggerSpeedWarning(); // } // // إعادة تفعيل التنبيه إذا انخفضت السرعة (إعادة ضبط الأمان) // if (speed < 80 && _hasShownSpeedWarning) { // _hasShownSpeedWarning = false; // } // // ز) إنهاء التايمر إذا انتهى الوقت // if (remainingSeconds <= 0) { // timer.cancel(); // } // update(); // }); // } // /// **عرض تحذير السرعة الزائدة (Speed Warning Trigger)** // /// // /// تظهر نافذة منبثقة (Dialog) وإشعاراً محلياً لتحذير الراكب عند اكتشاف سرعة عالية (> 100 كم/س). // /// // /// **الخيارات المتاحة للمستخدم:** // /// * **مشاركة التفاصيل:** لإرسال رسالة استغاثة عبر واتساب. // /// * **أنا بخير:** لإغلاق التنبيه والاستمرار في الرحلة. // void _triggerSpeedWarning() { // NotificationController().showNotification("Warning: Speeding detected!".tr, // 'You can call or record audio of this trip'.tr, 'tone1'); // Get.defaultDialog( // barrierDismissible: false, // title: "Warning: Speeding detected!".tr, // titleStyle: AppStyle.title.copyWith(color: AppColor.redColor), // content: Column( // children: [ // Icon(Icons.speed, size: 50, color: AppColor.redColor), // const SizedBox(height: 10), // Text( // "We noticed the speed is exceeding 100 km/h. Please slow down for your safety..." // .tr, // textAlign: TextAlign.center, // style: AppStyle.title, // ), // ], // ), // confirm: MyElevatedButton( // title: "Share Trip Details".tr, // kolor: AppColor.redColor, // onPressed: () { // Get.back(); // _shareTripDetailsSOS(); // }, // ), // cancel: MyElevatedButton( // title: "I'm Safe".tr, // kolor: AppColor.greenColor, // onPressed: () { // Get.back(); // }, // ), // ); // } // /// **تفعيل وضع الطوارئ للمركبة (sosPassenger)** // /// // /// تقوم بإظهار حوار تأكيدي للمستخدم لسؤاله عما إذا كان يرغب في إرسال // /// إشارة استغاثة عبر واتساب. // void sosPassenger() { // Get.defaultDialog( // barrierDismissible: false, // title: "Emergency SOS".tr, // titleStyle: AppStyle.title.copyWith(color: AppColor.redColor), // content: Column( // children: [ // Icon(Icons.warning_amber_rounded, size: 50, color: AppColor.redColor), // const SizedBox(height: 10), // Text( // "Do you want to send an emergency message to your SOS contact?".tr, // textAlign: TextAlign.center, // style: AppStyle.title, // ), // ], // ), // confirm: MyElevatedButton( // title: "Send SOS".tr, // kolor: AppColor.redColor, // onPressed: () { // Get.back(); // _shareTripDetailsSOS(); // }, // ), // cancel: MyElevatedButton( // title: "I'm Safe".tr, // kolor: AppColor.greenColor, // onPressed: () { // Get.back(); // }, // ), // ); // } // /// **مشاركة تفاصيل الرحلة للطوارئ (SOS Share)** // /// // /// تقوم بتجهيز رسالة نصية مفصلة تحتوي على بيانات الرحلة الحالية وإرسالها // /// عبر تطبيق WhatsApp لرقم الطوارئ المحفوظ. // /// // /// **البيانات المرسلة:** // /// * موقع الانطلاق والوصول. // /// * اسم السائق، رقم الهاتف، ونوع السيارة. // /// * رابط مباشر للموقع الحالي على خرائط جوجل. // void _shareTripDetailsSOS() { // String message = "**Emergency SOS from Passenger:**\n"; // String origin = startNameAddress; // String destination = endNameAddress; // message += "* ${'Origin'.tr}: $origin\n"; // message += "* ${'Destination'.tr}: $destination\n"; // message += "* ${'Driver Name'.tr}: $driverName\n"; // message += "* ${'Car'.tr}: $make - $model - $licensePlate\n"; // message += "* ${'Phone'.tr}: $driverPhone\n\n"; // // رابط جوجل مابس صحيح // message += // "${'Location'.tr}: https://www.google.com/maps/search/?api=1&query=${passengerLocation.latitude},${passengerLocation.longitude}\n"; // message += "Please help! Contact me as soon as possible.".tr; // launchCommunication( // 'whatsapp', box.read(BoxName.sosPhonePassenger), message); // } // int progressTimerRideBeginVip = 0; // int elapsedTimeInSeconds = 0; // Timer starts from 0 // String stringElapsedTimeRideBegin = '0:00'; // String stringElapsedTimeRideBeginVip = '0:00'; // bool rideInProgress = true; // To control when to stop the timer // void rideIsBeginPassengerTimerVIP() async { // rideInProgress = true; // Start the ride timer // bool sendSOS = false; // while (rideInProgress) { // await Future.delayed(const Duration(seconds: 1)); // // Increment elapsed time // elapsedTimeInSeconds++; // // Update the time display // int minutes = (elapsedTimeInSeconds / 60).floor(); // int seconds = elapsedTimeInSeconds % 60; // stringElapsedTimeRideBeginVip = // '$minutes:${seconds.toString().padLeft(2, '0')}'; // // Check for speed and SOS conditions // if (speed > 100 && !sendSOS) { // Get.defaultDialog( // barrierDismissible: false, // title: "Warning: Speeding detected!".tr, // titleStyle: AppStyle.title, // content: Text( // "We noticed the speed is exceeding 100 km/h. Please slow down for your safety. If you feel unsafe, you can share your trip details with a contact or call the police using the red SOS button." // .tr, // style: AppStyle.title, // ), // confirm: MyElevatedButton( // title: "Share Trip Details".tr, // onPressed: () { // Get.back(); // // Implement sharing trip details logic here // String message = "**Emergency SOS from Passenger:**\n"; // // Get trip details from GetX or relevant provider // String origin = passengerLocation.toString(); // String destination = myDestination.toString(); // String driverName = passengerName; // String driverCarPlate = licensePlate; // // Add trip details to the message // message += "* ${'Origin'.tr}: $origin\n"; // message += "* ${'Destination'.tr}: $destination\n"; // message += "* ${'Driver Name'.tr}: $driverName\n"; // message += "* ${'Driver Car Plate'.tr}: $driverCarPlate\n\n"; // message += "* ${'Driver Phone'.tr}: $driverPhone\n\n"; // // Add current location // message += // "${'Current Location'.tr}:https://www.google.com/maps/place/${passengerLocation.latitude},${passengerLocation.longitude} \n"; // // Append a call to action // message += "Please help! Contact me as soon as possible.".tr; // // Launch WhatsApp communication // launchCommunication( // 'whatsapp', box.read(BoxName.sosPhonePassenger), message); // sendSOS = true; // }, // kolor: AppColor.redColor, // ), // cancel: MyElevatedButton( // title: "Cancel".tr, // onPressed: () { // Get.back(); // }, // kolor: AppColor.greenColor, // ), // ); // } // // Update the UI // update(); // } // } // Future tripFinishedFromDriver() async { // Log.print('🧹 Cleaning UI for Finish'); // // إغلاق أي ديالوج مفتوح // if (Get.isDialogOpen == true) Get.back(); // if (Get.isBottomSheetOpen == true) Get.back(); // statusRide = 'Finished'; // currentRideState.value = RideState.finished; // تثبيت الحالة // // إيقاف البحث والعدادات // isSearchingWindow = false; // rideTimerBegin = false; // shouldFetch = false; // // إيقاف التايمرات // stopAllTimers(); // resetAllMapStates(); // clearPolyline(); // clearMarkersExceptStartEnd(); // markers.clear(); // update(); // } // StreamController _beginRideStreamController = // StreamController.broadcast(); // Stream get beginRideStream => _beginRideStreamController.stream; // bool isBeginRideFromDriverRunning = false; // // Call this method to listen to the stream // void listenToBeginRideStream() { // beginRideStream.listen((status) { // Log.print("Ride status: $status"); // // Perform additional actions based on the status // }, onError: (error) { // Log.print("Error in Begin Ride Stream: $error"); // }); // } // begiVIPTripFromPassenger() async { // timeToPassengerFromDriverAfterApplied = 0; // remainingTime = 0; // isBottomSheetShown = false; // remainingTimeToPassengerFromDriverAfterApplied = 0; // remainingTimeDriverWaitPassenger5Minute = 0; // rideTimerBegin = true; // statusRideVip = 'Begin'; // isDriverInPassengerWay = false; // isDriverArrivePassenger = false; // update(); // // isCancelRidePageShown = true; // rideIsBeginPassengerTimerVIP(); // runWhenRideIsBegin(); // } // Map rideStatusFromStartApp = {}; // bool isStartAppHasRide = false; // getRideStatusFromStartApp() async { // try { // var res = await CRUD().get( // link: AppLink.getRideStatusFromStartApp, // payload: {'passenger_id': box.read(BoxName.passengerID)}); // // Log.print(res); // Log.print('rideStatusFromStartApp: $res'); // // Log.print('1070'); // if (res == 'failure') { // rideStatusFromStartApp = { // 'data': {'status': 'NoRide', 'needsReview': false} // }; // isStartAppHasRide = false; // Log.print( // "No rides found for the given passenger ID within the last hour."); // } else { // var decoded = jsonDecode(res); // if (decoded['status'] == 'failure') { // rideStatusFromStartApp = { // 'data': {'status': 'NoRide', 'needsReview': false} // }; // isStartAppHasRide = false; // } else { // rideStatusFromStartApp = decoded; // } // } // if (rideStatusFromStartApp['data']['status'] == 'Begin' || // rideStatusFromStartApp['data']['status'] == 'Apply' || // rideStatusFromStartApp['data']['status'] == 'Applied') { // statusRide = rideStatusFromStartApp['data']['status']; // isStartAppHasRide = true; // RideState.inProgress; // driverId = rideStatusFromStartApp['data']['driver_id']; // passengerName = rideStatusFromStartApp['data']['driverName']; // driverRate = rideStatusFromStartApp['data']['rateDriver'].toString(); // statusRideFromStart = true; // update(); // Map tripData = // box.read(BoxName.tripData) as Map; // final String pointsString = tripData['polyline']; // List decodedPoints = // await compute(decodePolylineIsolate, pointsString); // // decodePolyline(response["routes"][0]["overview_polyline"]["points"]); // for (int i = 0; i < decodedPoints.length; i++) { // polylineCoordinates.add(decodedPoints[i]); // } // var polyline = Polyline( // polylineId: const PolylineId('main_route'), // points: polylineCoordinates, // width: 6, // color: const Color(0xFF2196F3), // ); // polyLines = {...polyLines, polyline}; // timeToPassengerFromDriverAfterApplied = 0; // remainingTime = 0; // remainingTimeToPassengerFromDriverAfterApplied = 0; // remainingTimeDriverWaitPassenger5Minute = 0; // rideTimerBegin = true; // isDriverInPassengerWay = false; // isDriverArrivePassenger = false; // // update(); // // isCancelRidePageShown = true; // durationToAdd = tripData['distance_m']; // rideIsBeginPassengerTimer(); // runWhenRideIsBegin(); // update(); // } // } catch (e) { // // Handle the error or perform any necessary actions // } // } // void driverArrivePassenger() { // timeToPassengerFromDriverAfterApplied = 0; // remainingTime = 0; // // isCancelRidePageShown = true; // update(); // rideIsBeginPassengerTimer(); // // runWhenRideIsBegin(); // } // void cancelTimerToPassengerFromDriverAfterApplied() { // timerToPassengerFromDriverAfterApplied?.cancel(); // } // void clearPlacesDestination() { // placesDestination = []; // hintTextDestinationPoint = 'Search for your destination'.tr; // update(); // } // void clearPlacesStart() { // placesStart = []; // hintTextStartPoint = 'Search for your Start point'.tr; // update(); // } // void clearPlaces(int index) { // placeListResponseAll[index] = []; // hintTextwayPointStringAll[index] = 'Search for waypoint'.tr; // update(); // } // int selectedReason = -1; // String? cancelNote; // void selectReason0(int index, String note) { // selectedReason = index; // cancelNote = note; // update(); // } // void getDialog(String title, String? midTitle, VoidCallback onPressed) { // final textToSpeechController = Get.find(); // Get.defaultDialog( // title: title, // titleStyle: AppStyle.title, // middleTextStyle: AppStyle.title, // content: Column( // children: [ // IconButton( // onPressed: () async { // await textToSpeechController.speakText(title ?? midTitle!); // }, // icon: const Icon(Icons.headphones)), // Text( // midTitle!, // style: AppStyle.title, // ) // ], // ), // confirm: MyElevatedButton( // title: 'Ok'.tr, // onPressed: onPressed, // kolor: AppColor.greenColor, // ), // cancel: MyElevatedButton( // title: 'Cancel', // kolor: AppColor.redColor, // onPressed: () { // Get.back(); // })); // } // Future?> extractCoordinatesFromLinkAsync( // String link) async { // try { // // 1. معالجة روابط الخرائط المباشرة (geo: و google.navigation:) // if (link.startsWith('geo:') || link.startsWith('google.navigation:')) { // RegExp regex = RegExp(r'(-?\d+\.\d+)[,/~=](-?\d+\.\d+)'); // var match = regex.firstMatch(link); // if (match != null) { // double lat = double.parse(match.group(1)!); // double lng = double.parse(match.group(2)!); // if (lat > 40 && lat > lng) { // double temp = lat; // lat = lng; // lng = temp; // } // return {'latitude': lat, 'longitude': lng}; // } // } // // 2. معالجة الروابط العادية (http/https) // int urlStartIndex = link.indexOf(RegExp(r'https?://')); // if (urlStartIndex == -1) return null; // String cleanLink = link.substring(urlStartIndex).trim(); // Uri uri = Uri.parse(cleanLink); // String finalUrl = cleanLink; // // فك التوجيه للروابط المختصرة // if (cleanLink.contains('goo.gl') || // cleanLink.contains('maps.google.com')) { // try { // var response = // await http.get(uri).timeout(const Duration(seconds: 5)); // finalUrl = response.request?.url.toString() ?? cleanLink; // } catch (e) { // Log.print('Redirect logic failed, using original: $e'); // } // } // // الأنماط المشتركة لخرائط جوجل (تكون دائماً Lat ثم Lng) // RegExp regex = RegExp(r'(-?\d+\.\d+)[,/~](-?\d+\.\d+)'); // var match = regex.firstMatch(finalUrl); // if (match != null) { // double lat = double.parse(match.group(1)!); // double lng = double.parse(match.group(2)!); // // 🔥 منطق التصحيح الذاتي (Smart Swap) للمنطقة (سوريا/الأردن/مصر) // // إذا كان الرقم الأول أكبر من الرقم الثاني بشكل واضح، فهذا يعني أن الرابط مقلوب أو أننا نحتاج للتأكد // // في منطقتنا Latitude حوالي 30-35 و Longitude حوالي 36-44 // if (lat > 40 && lat > lng) { // Log.print("⚠️ Detected Swapped Coordinates in Link. Correcting..."); // double temp = lat; // lat = lng; // lng = temp; // } // return { // 'latitude': lat, // 'longitude': lng, // }; // } // } catch (e) { // Log.print('Error parsing location link: $e'); // } // return null; // } // double latitudeWhatsApp = 0; // double longitudeWhatsApp = 0; // void handleWhatsAppLink(String link) async { // Map? coordinates = // await extractCoordinatesFromLinkAsync(link); // if (coordinates != null) { // latitudeWhatsApp = coordinates['latitude']!; // longitudeWhatsApp = coordinates['longitude']!; // Log.print( // 'Extracted coordinates: Lat: $latitudeWhatsApp, Long: $longitudeWhatsApp'); // // Use these coordinates in your app as needed // } else { // Log.print('Failed to extract coordinates from the link'); // } // } // void goToWhatappLocation() async { // if (sosFormKey.currentState!.validate()) { // // 1. استخراج الإحداثيات أولاً بشكل محلي لضمان عدم حدوث سباق بيانات (Race Condition) // Map? coordinates = // await extractCoordinatesFromLinkAsync(whatsAppLocationText.text); // if (coordinates != null) { // latitudeWhatsApp = coordinates['latitude']!; // longitudeWhatsApp = coordinates['longitude']!; // Log.print( // '📍 Final Coordinates for OSM: Lat: $latitudeWhatsApp, Lng: $longitudeWhatsApp'); // changeIsWhatsAppOrder(true); // Get.back(); // // إعداد الوجهة // myDestination = LatLng(latitudeWhatsApp, longitudeWhatsApp); // // تحريك الكاميرا لموقع الراكب (البداية) وليس الوجهة فوراً لضمان تحميل الخريطة // if (passengerLocation != null) { // await mapController?.animateCamera(CameraUpdate.newLatLng( // LatLng(passengerLocation.latitude, passengerLocation.longitude))); // } // changeMainBottomMenuMap(); // passengerStartLocationFromMap = true; // isPickerShown = true; // update(); // } else { // mySnackbarWarning('لم نتمكن من استخراج الموقع من الرابط'); // } // } // } // int currentTimeSearchingCaptainWindow = 0; // late String driverPhone = ''; // late String driverRate = ''; // late String passengerName = ''; // late String carColor = ''; // late String colorHex = ''; // late String carYear = ''; // late String model = ''; // late String make = ''; // late String licensePlate = ''; // String driverOrderStatus = 'yet'; // bool isDriversTokensSend = false; // Set notifiedDrivers = {}; // /// [إضافة جديدة] // /// دالة مخصصة لإضافة الرحلة إلى جدول الانتظار (waiting_ride) // Future _addRideToWaitingTable() async { // try { // await CRUD().post(link: AppLink.addWaitingRide, payload: { // 'id': rideId.toString(), // "start_location": // '${startLocation.latitude},${startLocation.longitude}', // "end_location": '${endLocation.latitude},${endLocation.longitude}', // "date": DateTime.now().toString(), // "time": DateTime.now().toString(), // "price": totalPassenger.toStringAsFixed(2), // 'passenger_id': box.read(BoxName.passengerID).toString(), // 'status': 'waiting', // الحالة الرئيسية لجدول الانتظار // 'carType': box.read(BoxName.carType), // 'passengerRate': passengerRate.toStringAsFixed(2), // 'price_for_passenger': totalME.toStringAsFixed(2), // 'distance': distance.toStringAsFixed(1), // 'duration': duration.toStringAsFixed(1), // 'payment_method': // Get.find().isWalletChecked ? 'wallet' : 'cash', // "passenger_wallet": box.read(BoxName.passengerWalletTotal).toString(), // }); // Log.print('[WaitingTable] Ride $rideId added to waiting_ride table.'); // } catch (e) { // Log.print('Error adding ride to waiting_ride table: $e'); // } // } // String driversStatusForSearchWindow = ''; // bool isDriversDataValid() { // return dataCarsLocationByPassenger != 'failure' && // dataCarsLocationByPassenger != null && // dataCarsLocationByPassenger.containsKey('message') && // dataCarsLocationByPassenger['message'] != null; // } // void showNoDriversDialog() { // Get.dialog( // BackdropFilter( // filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), // child: CupertinoAlertDialog( // title: Text("No Car or Driver Found in your area.".tr, // style: AppStyle.title // .copyWith(fontSize: 20, fontWeight: FontWeight.bold)), // content: Text("No Car or Driver Found in your area.".tr, // style: AppStyle.title.copyWith(fontSize: 16)), // actions: [ // CupertinoDialogAction( // onPressed: () { // Get.back(); // Get.offAll(() => const MapPagePassenger()); // }, // child: // Text('OK'.tr, style: TextStyle(color: AppColor.greenColor)), // ), // ], // ), // ), // barrierDismissible: false, // ); // } // Future postRideDetailsToServer() async { // // التأكد من وجود مسار // if (polylineCoordinates.isEmpty) return false; // startLocation = polylineCoordinates.first; // endLocation = polylineCoordinates.last; // // تجهيز البيانات الكاملة (Data Enrichment) لإرسالها للـ PHP // Map payload = { // // 1. البيانات الأساسية // "start_location": '${startLocation.latitude},${startLocation.longitude}', // "end_location": '${endLocation.latitude},${endLocation.longitude}', // "date": DateTime.now().toString(), // "time": DateTime.now().toString(), // "endtime": "00:00:00", // أو حسب حساباتك // "price": totalPassenger.toStringAsFixed(2), // "passenger_id": box.read(BoxName.passengerID).toString(), // "driver_id": "0", // لم يحدد بعد // "status": "waiting", // "carType": box.read(BoxName.carType), // "price_for_driver": totalPassenger.toString(), // أو المعادلة الخاصة بك // "price_for_passenger": totalME.toString(), // "distance": distance.toString(), // // 2. بيانات الراكب (ليستخدمها PHP لبناء الـ Payload دون استعلام) // "passenger_name": box.read(BoxName.name).toString(), // "passenger_phone": box.read(BoxName.phone).toString(), // "passenger_token": box.read(BoxName.tokenFCM).toString(), // "passenger_email": box.read(BoxName.email).toString(), // "passenger_wallet": box.read(BoxName.passengerWalletTotal).toString(), // "passenger_rating": (passengerRate ?? 5.0).toString(), // // 3. بيانات الواجهة الإضافية // "start_name": startNameAddress, // "end_name": endNameAddress, // "duration_text": "${(durationToRide / 60).floor()}", // نص الوقت // "distance_text": "$distance", // نص المسافة // "is_wallet": Get.find().isWalletChecked.toString(), // "has_steps": Get.find().wayPoints.length > 1 // ? 'true' // : 'false', // // نقاط التوقف (إذا وجدت) // "step0": placesCoordinate.length > 0 ? placesCoordinate[0] : "", // "step1": placesCoordinate.length > 1 ? placesCoordinate[1] : "", // "step2": placesCoordinate.length > 2 ? placesCoordinate[2] : "", // "step3": placesCoordinate.length > 3 ? placesCoordinate[3] : "", // "step4": placesCoordinate.length > 4 ? placesCoordinate[4] : "", // }; // Log.print( // '🏁 Ride Registration Detail: $startNameAddress -> $endNameAddress'); // Log.print(' 📦 Payload: $payload'); // try { // // الاتصال بـ add_ride.php // var response = await CRUD().post( // link: "${AppLink.server}/ride/rides/add_ride.php", // تأكد من المسار // payload: payload); // var jsonResponse = (response is String) ? jsonDecode(response) : response; // if (jsonResponse['status'] == 'success') { // rideId = jsonResponse['message'].toString(); // حفظ ID الرحلة // Log.print("✅ Ride Created ID: $rideId"); // return true; // } else { // Log.print("❌ Ride Creation Failed: $response"); // return false; // } // } catch (e) { // Log.print("❌ Exception in postRide: $e"); // return false; // } // } // late LatLng endLocation; // late LatLng startLocation; // StreamController _rideStatusStreamController = // StreamController.broadcast(); // Stream get rideStatusStream => _rideStatusStreamController.stream; // int maxAttempts = 28; // Future rideAppliedFromDriver(bool isApplied) async { // Log.print('[rideAppliedFromDriver] 🚀 Starting logic...'); // // 1. جلب بيانات السائق والسيارة المحدثة من السيرفر // await getUpdatedRideForDriverApply(rideId); // // تنبيهات الأسعار حسب نوع السيارة // if (['Speed', 'Awfar Car'].contains(box.read(BoxName.carType))) { // NotificationController().showNotification('Fixed Price'.tr, // 'The captain is responsible for the route.'.tr, 'ding'); // } else if (['Comfort', 'Lady'].contains(box.read(BoxName.carType))) { // NotificationController().showNotification('Attention'.tr, // 'The price may increase if the route changes.'.tr, 'ding'); // } // isApplied = true; // statusRide = 'Apply'; // rideConfirm = false; // isSearchingWindow = false; // _isDriverAppliedLogicExecuted = true; // ضمان عدم التكرار // update(); // تحديث أولي // // 2. جلب موقع السائق الأولي فوراً (Blocking await) // await getDriverCarsLocationToPassengerAfterApplied(); // // 3. إذا توفر الموقع: حساب المسافة/الزمن ورسم المسار // if (driverCarsLocationToPassengerAfterApplied.isNotEmpty) { // LatLng driverPos = driverCarsLocationToPassengerAfterApplied.last; // Log.print( // '[rideAppliedFromDriver] 📍 Driver at: $driverPos, Passenger at: $passengerLocation'); // // أ) استدعاء API لحساب المسافة والزمن الدقيق (بدون رسم) // await getInitialDriverDistanceAndDuration(driverPos, passengerLocation); // // ب) رسم خط المسار (Visual only) // await drawDriverPathOnly(driverPos, passengerLocation); // // ج) ضبط الكاميرا لتشمل السائق والراكب // _fitCameraToPoints(driverPos, passengerLocation); // } else { // Log.print( // '[rideAppliedFromDriver] ⚠️ Warning: Driver location not found yet.'); // } // // 4. تشغيل تايمر التتبع المستمر (الذي سيقوم بتناقص الوقت الذي جلبناه من API) // startTimerFromDriverToPassengerAfterApplied(); // // إغلاق الستريم القديم // if (!_rideStatusStreamController.isClosed) // _rideStatusStreamController.close(); // } // /// دالة لجلب المسافة والزمن بين السائق والراكب عند قبول الطلب // /// تستخدم API سريع (overview=false) // Future getInitialDriverDistanceAndDuration( // LatLng driverPos, LatLng passengerPos) async { // final String apiUrl = 'https://routec.intaleq.xyz/route'; // final String apiKey = Env.mapKeyOsm; // final String origin = '${driverPos.latitude},${driverPos.longitude}'; // final String dest = '${passengerPos.latitude},${passengerPos.longitude}'; // // الرابط المطلوب: steps=false&overview=false (سريع جداً للبيانات فقط) // final Uri uri = Uri.parse( // '$apiUrl?origin=$origin&destination=$dest&steps=false&overview=false'); // try { // Log.print('[InitialCalc] Fetching distance/duration from: $uri'); // final response = await http.get(uri, headers: {'X-API-KEY': apiKey}); // if (response.statusCode == 200) { // final data = jsonDecode(response.body); // if (data['status'] == 'ok') { // // 1. استخراج الزمن (بالثواني) // // نستخدم المعامل 1.5348 أو 1.4 حسب منطقك السابق لتقدير الوقت الواقعي // double durationSecondsRaw = (data['duration_s'] as num).toDouble(); // int finalDurationSeconds = (durationSecondsRaw * kDurationScalar) // .toInt(); // kDurationScalar = 1.5348 // // 2. استخراج المسافة (بالأمتار) // double distanceMeters = (data['distance_m'] as num).toDouble(); // // 3. تحديث المتغيرات في الكنترولر // timeToPassengerFromDriverAfterApplied = finalDurationSeconds; // remainingTimeToPassengerFromDriverAfterApplied = finalDurationSeconds; // distanceByPassenger = // (distanceMeters).toStringAsFixed(0); // المسافة نصاً // // يمكنك أيضاً تحديث durationToPassenger إذا كنت تستخدمها // durationToPassenger = finalDurationSeconds; // Log.print( // '[InitialCalc] ✅ Success: Duration=${finalDurationSeconds}s, Distance=${distanceMeters}m'); // update(); // تحديث الواجهة لعرض الوقت الجديد فوراً // } // } else { // Log.print('[InitialCalc] ❌ API Error: ${response.statusCode}'); // } // } catch (e) { // Log.print('[InitialCalc] 💥 Exception: $e'); // } // } // // دالة خفيفة وسريعة لرسم خط المسار فقط (بدون أسعار أو خطوات) // Future drawDriverPathOnly(LatLng driverPos, LatLng passengerPos) async { // final String apiUrl = 'https://routec.intaleq.xyz/route'; // final String apiKey = Env.mapKeyOsm; // final String origin = '${driverPos.latitude},${driverPos.longitude}'; // final String dest = '${passengerPos.latitude},${passengerPos.longitude}'; // // استخدام overview=full للدقة، و steps=false للسرعة // final Uri uri = Uri.parse( // '$apiUrl?origin=$origin&destination=$dest&steps=false&overview=full'); // try { // final response = await http.get(uri, headers: {'X-API-KEY': apiKey}); // if (response.statusCode == 200) { // final data = jsonDecode(response.body); // if (data['status'] == 'ok' && data['polyline'] != null) { // final String pointsString = data['polyline']; // // فك التشفير // List decodedPoints = // await compute(decodePolylineIsolate, pointsString); // // إزالة خط مسار السائق القديم فقط // polyLines = polyLines // .where((p) => p.polylineId.value != 'driver_route') // .toSet(); // // إضافة الخط الجديد // polyLines = { // ...polyLines, // Polyline( // polylineId: const PolylineId('driver_route'), // points: decodedPoints, // color: const Color(0xFF333333), // لون مميز لمسار السائق // width: 5, // ) // }; // // لا تستدعي update هنا، سيتم استدعاؤها في الدالة الأب (getDriverCars...) لتقليل عدد التحديثات // } // } // } catch (e) { // Log.print('Error drawing driver path: $e'); // } // } // // دالة مساعدة لضبط الكاميرا // void _fitCameraToPoints(LatLng p1, LatLng p2) async { // if (mapController == null) return; // // 1. معالجة حالة النقاط المتطابقة (تمنع الكراش في Android) // if (p1.latitude == p2.latitude && p1.longitude == p2.longitude) { // try { // mapController?.animateCamera(CameraUpdate.newLatLngZoom(p1, 17)); // } catch (e) { // Log.print("Error animating to single point: $e"); // } // return; // } // // 2. حساب الحدود // double minLat = min(p1.latitude, p2.latitude); // double maxLat = max(p1.latitude, p2.latitude); // double minLng = min(p1.longitude, p2.longitude); // double maxLng = max(p1.longitude, p2.longitude); // // 3. تقليل الهوامش لتجنب خطأ "View size too small" // // نستخدم 50 بدلاً من 100 ليكون آمناً مع الخرائط الصغيرة // double padding = 50.0; // try { // await mapController?.animateCamera( // CameraUpdate.newLatLngBounds( // LatLngBounds( // southwest: LatLng(minLat, minLng), // northeast: LatLng(maxLat, maxLng), // ), // left: padding, // top: padding, // right: padding, // bottom: padding, // ), // ); // } catch (e) { // Log.print("Error animating bounds (Map might be resizing): $e"); // // محاولة بديلة آمنة: تحريك الكاميرا للمنتصف فقط دون Bounds // try { // LatLng center = LatLng((minLat + maxLat) / 2, (minLng + maxLng) / 2); // mapController?.animateCamera(CameraUpdate.newLatLngZoom(center, 14)); // } catch (e) { // Log.print("Error: $e"); // } // } // } // // Listening to the Stream // void listenToRideStatusStream() { // rideStatusStream.listen((rideStatus) { // Log.print("Ride Status: $rideStatus"); // // Handle updates based on the ride status // }, onError: (error) { // Log.print("Error in Ride Status Stream: $error"); // // Handle stream errors // }, onDone: () { // Log.print("Ride status stream closed."); // }); // } // void start15SecondTimer(String rideId) { // Timer(const Duration(seconds: 15), () { // // delayAndFetchRideStatusForAllDriverAvailable(rideId); // }); // } // // Replaces void startTimer() // Timer? // _uiCountdownTimer; // Add this variable to your class to manage lifecycle // void startUiCountdown() { // // Cancel any existing timer to avoid duplicates // _uiCountdownTimer?.cancel(); // // Reset variables // progress = 0; // remainingTime = durationTimer; // _uiCountdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) { // // Logic from your loop, but non-blocking // int i = timer.tick; // current tick // progress = i / durationTimer; // remainingTime = durationTimer - i; // if (remainingTime <= 0) { // timer.cancel(); // Stop this specific timer // rideConfirm = false; // // Add the duration to the tracking time logic // timeToPassengerFromDriverAfterApplied += durationToPassenger; // // Note: We do NOT call startTimerFromDriverToPassengerAfterApplied() here // // because we already started it in rideAppliedFromDriver! // timerEnded(); // Call your existing completion logic // } // update(); // Update the UI progress bar // }); // } // void timerEnded() async { // runEvery30SecondsUntilConditionMet(); // isCancelRidePageShown = false; // Log.print('isCancelRidePageShown: $isCancelRidePageShown'); // update(); // } // Future getRideStatus(String rideId) async { // final response = await CRUD().get( // link: "${AppLink.rideServerSide}/ride/rides/getRideStatus.php", // payload: {'id': rideId}); // Log.print(response); // Log.print('2176'); // return jsonDecode(response)['data']; // } // late String driverCarModel, // driverCarMake, // driverLicensePlate, // driverName = ''; // Future getUpdatedRideForDriverApply(String rideId) async { // // حماية مبدئية: إذا كان المعرف غير صالح لا تكمل // if (rideId == 'yet' || rideId.isEmpty) return; // try { // final res = await CRUD().get( // link: "${AppLink.server}/ride/rides/getRideOrderID.php", // payload: {'passengerID': box.read(BoxName.passengerID).toString()}); // if (res != 'failure') { // var response = jsonDecode(res); // Log.print('getUpdatedRideForDriverApply Response: $response'); // // [هام] التحقق من أن data عبارة عن Map وليست false أو null // // هذا يمنع الخطأ: Class 'bool' has no instance method '[]' // if (response['status'] == 'success' && // response['data'] != null && // response['data'] is Map) { // var data = response['data']; // // استخدام ?.toString() ?? '' للحماية من القيم الفارغة (Null Safety) // driverId = data['driver_id']?.toString() ?? ''; // driverPhone = data['phone']?.toString() ?? ''; // driverCarMake = data['make']?.toString() ?? ''; // model = data['model']?.toString() ?? ''; // colorHex = data['color_hex']?.toString() ?? ''; // carColor = data['color']?.toString() ?? ''; // make = data['make']?.toString() ?? ''; // licensePlate = data['car_plate']?.toString() ?? ''; // // دمج الاسم الأول والأخير للراكب // String firstName = data['passengerName']?.toString() ?? ''; // String lastName = data['last_name']?.toString() ?? ''; // passengerName = // lastName.isNotEmpty ? "$firstName $lastName" : firstName; // driverName = data['driverName']?.toString() ?? ''; // // [هام] التوكن ضروري للإشعارات // driverToken = data['token']?.toString() ?? ''; // carYear = data['year']?.toString() ?? ''; // driverRate = data['ratingDriver']?.toString() ?? '5.0'; // update(); // تحديث الواجهة بالبيانات الجديدة // } else { // Log.print( // "Warning: Ride data not found or invalid (data is false/null)"); // // اختياري: يمكنك هنا التعامل مع حالة عدم العثور على السائق بعد // } // } // } catch (e) { // Log.print("Error in getUpdatedRideForDriverApply: $e"); // } // } // late LatLng currentDriverLocation; // late double headingList; // Map _animationTimers = {}; // final int updateIntervalMs = 100; // Update every 100ms // final double minMovementThreshold = // 10; // Minimum movement in meters to trigger update // Future getCarForFirstConfirm(String carType) async { // bool foundCars = false; // int attempt = 0; // // Set up the periodic timer // Timer? timer = Timer.periodic(const Duration(seconds: 4), (Timer t) async { // // Attempt to get car location // foundCars = await getCarsLocationByPassengerAndReloadMarker(); // Log.print('foundCars: $foundCars'); // if (foundCars) { // // If cars are found, cancel the timer and exit the search // t.cancel(); // } else if (attempt >= 4) { // // After 4 attempts, stop the search // t.cancel(); // if (!foundCars) { // noCarString = true; // dataCarsLocationByPassenger = 'failure'; // } // update(); // } // attempt++; // Increment attempt // }); // } // void startCarLocationSearch(String carType) { // int searchInterval = 5; // Interval in seconds // Log.print('searchInterval: $searchInterval'); // int boundIncreaseStep = 2500; // Initial bounds in meters // Log.print('boundIncreaseStep: $boundIncreaseStep'); // int maxAttempts = 3; // Maximum attempts to increase bounds // int maxBoundIncreaseStep = 6000; // Maximum bounds increase step // int attempt = 0; // Current attempt // Log.print('initial attempt: $attempt'); // Timer.periodic(Duration(seconds: searchInterval), (Timer timer) async { // Log.print('Current attempt: $attempt'); // Log current attempt // bool foundCars = false; // if (attempt >= maxAttempts) { // timer.cancel(); // if (foundCars == false) { // noCarString = true; // // dataCarsLocationByPassenger = 'failure'; // update(); // } // // return; // } else if (reloadStartApp == true) { // Log.print('reloadStartApp: $reloadStartApp'); // foundCars = await getCarsLocationByPassengerAndReloadMarker(); // Log.print('foundCars: $foundCars'); // if (foundCars) { // timer.cancel(); // } else { // attempt++; // Log.print( // 'Incrementing attempt to: $attempt'); // Log incremented attempt // if (boundIncreaseStep < maxBoundIncreaseStep) { // boundIncreaseStep += 1500; // Increase bounds // if (boundIncreaseStep > maxBoundIncreaseStep) { // boundIncreaseStep = // maxBoundIncreaseStep; // Ensure it does not exceed the maximum // } // Log.print( // 'New boundIncreaseStep: $boundIncreaseStep'); // Log new bounds // } // } // } // }); // } // String getLocationArea(double latitude, double longitude) { // LatLng passengerPoint = LatLng(latitude, longitude); // // 1. فحص الأردن // if (isPointInPolygon(passengerPoint, CountryPolygons.jordanBoundary)) { // box.write(BoxName.countryCode, 'Jordan'); // // يمكنك تعيين AppLink.endPoint هنا إذا كان منطقك الداخلي لا يزال يعتمد عليه // box.write(BoxName.serverChosen, // AppLink.server); // مثال: اختر سيرفر سوريا للبيانات // return 'Jordan'; // } // // 2. فحص سوريا // if (isPointInPolygon(passengerPoint, CountryPolygons.syriaBoundary)) { // box.write(BoxName.countryCode, 'Syria'); // box.write(BoxName.serverChosen, AppLink.server); // return 'Syria'; // } // // 3. فحص مصر // if (isPointInPolygon(passengerPoint, CountryPolygons.egyptBoundary)) { // box.write(BoxName.countryCode, 'Egypt'); // box.write(BoxName.serverChosen, AppLink.server); // return 'Egypt'; // } // // 4. الافتراضي (إذا كان خارج المناطق المخدومة) // box.write(BoxName.countryCode, 'Jordan'); // box.write(BoxName.serverChosen, AppLink.server); // return 'Unknown Location (Defaulting to Jordan)'; // } // Future getCarsLocationByPassengerAndReloadMarker() async { // // 1. تنظيف القائمة والماركرز // carsLocationByPassenger = []; // if (passengerLocation.latitude == 0 && passengerLocation.longitude == 0) { // return false; // لا يوجد موقع للراكب // } // // 2. طلب بسيط ومباشر (أنا هنا، أعطني السائقين حولي) // var res = await CRUD().get( // link: AppLink.getCarsLocationByPassenger, // payload: { // 'lat': passengerLocation.latitude.toString(), // 'lng': passengerLocation.longitude.toString(), // 'radius': '5', // نصف القطر ثابت (مثلاً 5 كم) أو يمكنك جعله ديناميكياً // 'limit': '50', // أقصى عدد سيارات للعرض // }, // ); // if (res == 'failure') { // noCarString = true; // update(); // return false; // } // // 3. معالجة البيانات // noCarString = false; // var responseData = jsonDecode(res); // // دعم التنسيقين (data أو message) لضمان عدم حدوث كراش // List driversList = []; // if (responseData['status'] == true && responseData['data'] != null) { // driversList = responseData['data']; // } else if (responseData['message'] != null) { // driversList = responseData['message']; // للكود القديم احتياطاً // } // if (driversList.isEmpty) { // carsLocationByPassenger.clear(); // update(); // return false; // } // carsLocationByPassenger.clear(); // تنظيف الماركرز القديمة // // 4. رسم السيارات على الخريطة // for (var i = 0; i < driversList.length; i++) { // var carData = driversList[i]; // // التحقق من الإحداثيات لضمان عدم رسم سيارة في المحيط // double lat = double.tryParse(carData['latitude'].toString()) ?? 0.0; // double lng = double.tryParse(carData['longitude'].toString()) ?? 0.0; // double heading = double.tryParse(carData['heading'].toString()) ?? 0.0; // if (lat == 0.0 || lng == 0.0) continue; // _updateOrCreateMarker( // carData['id'].toString(), // LatLng(lat, lng), // heading, // // الدالة هذه تقرر شكل الأيقونة بناءً على نوع السيارة القادم من السيرفر // _getIconForCar(carData), // ); // } // update(); // return true; // } // final List> fakeCarData = []; // void _addFakeCarMarkers(LatLng center, int count) { // if (fakeCarData.isEmpty) { // Random random = Random(); // double radiusInKm = 2.5; // 3 km diameter, so 1.5 km radius // for (int i = 0; i < count; i++) { // // Generate a random angle and distance within the circle // double angle = random.nextDouble() * 2 * pi; // double distance = sqrt(random.nextDouble()) * radiusInKm; // // Convert distance to latitude and longitude offsets // double latOffset = (distance / 111.32); // 1 degree lat ≈ 111.32 km // double lonOffset = // (distance / (111.32 * cos(radians(center.latitude)))); // // Calculate new position // double lat = center.latitude + (latOffset * cos(angle)); // double lon = center.longitude + (lonOffset * sin(angle)); // double heading = random.nextDouble() * 360; // fakeCarData.add({ // 'id': 'fake_$i', // 'latitude': lat, // 'longitude': lon, // 'heading': heading, // 'gender': 'Male', // Randomize gender // }); // } // } // for (var carData in fakeCarData) { // _updateOrCreateMarker( // carData['id'].toString(), // LatLng(carData['latitude'], carData['longitude']), // carData['heading'], // _getIconForCar(carData), // ); // } // } // String _getIconForCar(Map carData) { // if (carData['model'].toString().contains('دراجة')) { // return motoIcon; // } else if (carData['gender'] == 'Female') { // return ladyIcon; // } else { // return carIcon; // } // } // void _updateOrCreateMarker( // String markerId, LatLng newPosition, double newHeading, String icon) { // final mId = MarkerId(markerId); // final existingMarker = markers.cast().firstWhere( // (m) => m?.markerId == mId, // orElse: () => null, // ); // if (existingMarker == null) { // markers = { // ...markers, // Marker( // markerId: mId, // position: newPosition, // rotation: newHeading, // icon: InlqBitmap.fromStyleImage(icon), // anchor: const Offset(0.5, 0.5), // ), // }; // update(); // } else { // double distance = // _calculateDistance(existingMarker.position, newPosition); // if (distance >= minMovementThreshold) { // _smoothlyUpdateMarker(existingMarker, newPosition, newHeading, icon); // } // } // } // double _calculateDistance(LatLng start, LatLng end) { // // Implement distance calculation (e.g., Haversine formula) // // For simplicity, this is a placeholder. Replace with actual implementation. // return 1000 * // sqrt(pow(start.latitude - end.latitude, 2) + // pow(start.longitude - end.longitude, 2)); // } // String formatSyrianPhoneNumber(String phoneNumber) { // // Trim any whitespace from the input. // String trimmedPhone = phoneNumber.trim(); // // If the number starts with '09', remove the leading '0' and prepend '963'. // if (trimmedPhone.startsWith('09')) { // return '963${trimmedPhone.substring(1)}'; // } // // If the number already starts with '963', return it as is to avoid duplication. // if (trimmedPhone.startsWith('963')) { // return trimmedPhone; // } // // For any other case (e.g., number starts with '9' without a '0'), // // prepend '963' to ensure the correct format. // return '963$trimmedPhone'; // } // String generateTrackingLink(String rideId, String driverId) { // String cleanRideId = rideId.toString().trim(); // String cleanDriverId = driverId.toString().trim(); // // الكلمة السرية للمطابقة مع السيرفر // const String secretSalt = "Intaleq_Secure_Track_2025"; // // الدمج والتشفير // String rawString = "$cleanRideId$cleanDriverId$secretSalt"; // var bytes = utf8.encode(rawString); // var digest = md5.convert(bytes); // String token = digest.toString(); // // الرابط المباشر لصفحة التتبع // return "https://intaleqapp.com/track/index.php?id=$cleanRideId&token=$token"; // } // // 2. الدالة الرئيسية (تم تعديلها لإرسال واتساب بدلاً من الإشعارات) // Future shareTripWithFamily() async { // // التحقق أولاً: هل الرقم موجود؟ // String? storedPhone = box.read(BoxName.sosPhonePassenger); // if (storedPhone == null) { // // --- (نفس المنطق القديم: فتح ديالوج لإضافة الرقم) --- // Get.defaultDialog( // title: 'Add SOS Phone'.tr, // titleStyle: AppStyle.title, // content: Form( // key: sosFormKey, // child: MyTextForm( // controller: sosPhonePassengerProfile, // label: 'insert sos phone'.tr, // hint: 'e.g. 0912345678'.tr, // type: TextInputType.phone, // ), // ), // confirm: MyElevatedButton( // title: 'Add SOS Phone'.tr, // onPressed: () async { // if (sosFormKey.currentState!.validate()) { // Get.back(); // // تنسيق الرقم // var numberPhone = // formatSyrianPhoneNumber(sosPhonePassengerProfile.text); // // حفظ في السيرفر // await CRUD().post( // link: AppLink.updateprofile, // payload: { // 'id': box.read(BoxName.passengerID), // 'sosPhone': numberPhone, // }, // ); // // حفظ محلياً // box.write(BoxName.sosPhonePassenger, numberPhone); // // استدعاء الدالة مرة أخرى للمتابعة // shareTripWithFamily(); // } // })); // return; // } // // --- (المنطق الجديد: إرسال واتساب مباشرة) --- // // 1. التأكد من وجود بيانات للرحلة // if (rideId == 'yet' || driverId.isEmpty) { // Get.snackbar("Alert".tr, "Wait for the trip to start first".tr); // return; // } // // 2. تنسيق الرقم // var numberPhone = formatSyrianPhoneNumber(storedPhone); // // 3. توليد الرابط // String trackingLink = generateTrackingLink(rideId, driverId); // // 4. تجهيز الرسالة (بالإنجليزية وجاهزة للترجمة) // // لاحظ: استخدمت المتغيرات الموجودة في الكنترولر (passengerName هنا عادة يحمل اسم السائق في الكنترولر الخاص بك حسب الكود السابق) // String message = """ // مرحباً، تابع رحلتي مباشرة على تطبيق انطلق 🚗 // يمكنك تتبع مسار الرحلة من هنا: // $trackingLink // السائق: $passengerName // السيارة: $model - $licensePlate // شكراً لاستخدامك انطلق! // """ // .tr; // String messageEn = """Hello, follow my trip live on Intaleq 🚗 // Track my ride here: // $trackingLink // Driver: $passengerName // Car: $model - $licensePlate // Thank you for using Intaleq! // """; // // اختر الرسالة بناءً على اللغة المفضلة (مثال بسيط) // String userLanguage = box.read(BoxName.lang) ?? 'ar'; // message = (userLanguage == 'ar') ? message : messageEn; // // وضعنا .tr لكي تتمكن من ترجمتها للعربية في ملفات اللغة إذا أردت، أو تركها إنجليزية // Log.print("Sending WhatsApp to: $numberPhone"); // // 5. فتح واتساب // launchCommunication('whatsapp', numberPhone, message); // // (اختياري) حفظ أن التتبع مفعل لتغيير حالة الأيقونة في الواجهة // box.write(BoxName.parentTripSelected, true); // update(); // } // Future getTokenForParent() async { // // 1. التحقق أولاً: هل الرقم موجود؟ // String? storedPhone = box.read(BoxName.sosPhonePassenger); // if (storedPhone == null) { // // --- حالة الرقم غير موجود: نفتح الديالوج فقط --- // Get.defaultDialog( // title: 'Add SOS Phone'.tr, // titleStyle: AppStyle.title, // content: Form( // key: sosFormKey, // child: MyTextForm( // controller: sosPhonePassengerProfile, // label: 'insert sos phone'.tr, // hint: 'e.g. 0912345678'.tr, // type: TextInputType.phone, // ), // ), // confirm: MyElevatedButton( // title: 'Add SOS Phone'.tr, // onPressed: () async { // if (sosFormKey.currentState!.validate()) { // // إغلاق الديالوج الحالي // Get.back(); // // تنسيق الرقم (تأكد أن هذا التنسيق يطابق ما تم تخزينه عند تسجيل الراكب) // var numberPhone = // formatSyrianPhoneNumber(sosPhonePassengerProfile.text); // // حفظ الرقم في السيرفر (تحديث البروفايل) // await CRUD().post( // link: AppLink.updateprofile, // payload: { // 'id': box.read(BoxName.passengerID), // 'sosPhone': numberPhone, // }, // ); // // حفظ الرقم محلياً // box.write(BoxName.sosPhonePassenger, numberPhone); // // استدعاء الدالة مرة أخرى // getTokenForParent(); // } // })); // return; // } // generateTrackingLink(rideId, driverId); // // --- حالة الرقم موجود: نكمل التنفيذ --- // var numberPhone = formatSyrianPhoneNumber(storedPhone); // Log.print("Searching for Parent Token with Phone: $numberPhone"); // // استدعاء السكريبت (استخدم POST بدلاً من GET) // var res = await CRUD() // .post(link: AppLink.getTokenParent, payload: {'phone': numberPhone}); // // التعامل مع الاستجابة // if (res is Map) { // handleResponse(res); // } else { // try { // // var jsonRes = jsonDecode(res); // handleResponse(res); // } catch (e) { // Log.print("Error parsing response: $res"); // } // } // } // void handleResponse(Map res) { // Log.print("Handle Response: $res"); // للتأكد من دخول الدالة // // الحالة 1: الرقم غير مسجل (Failure) // if (res['status'] == 'failure') { // // إذا كان هناك أي ديالوج تحميل مفتوح، نغلقه أولاً، لكن بحذر // if (Get.isDialogOpen ?? false) Get.back(); // Get.defaultDialog( // title: "No user found".tr, // اختصرت العنوان ليظهر بشكل أفضل // titleStyle: AppStyle.title, // content: Column( // children: [ // Text( // "No passenger found for the given phone number".tr, // style: AppStyle.title, // غيرت الستايل ليكون أصغر قليلاً // textAlign: TextAlign.center, // ), // const SizedBox(height: 10), // Text( // "Send Intaleq app to him".tr, // style: AppStyle.title // .copyWith(color: AppColor.greenColor, fontSize: 14), // textAlign: TextAlign.center, // ) // ], // ), // confirm: MyElevatedButton( // title: 'Send Invite'.tr, // onPressed: () { // Get.back(); // إغلاق الديالوج // var rawPhone = box.read(BoxName.sosPhonePassenger); // // تأكد أن rawPhone ليس null // if (rawPhone == null) return; // var phone = formatSyrianPhoneNumber(rawPhone); // // تصحيح نص الرسالة // var message = '''Dear Friend, // 🚀 I have just started an exciting trip on Intaleq! // Download the app to track my ride: // 👉 Android: https://play.google.com/store/apps/details?id=com.Intaleq.intaleq&hl=en-US // 👉 iOS: https://apps.apple.com/st/app/intaleq-rider/id6748075179 // See you there! // Intaleq Team'''; // launchCommunication('whatsapp', phone, message); // }), // cancel: MyElevatedButton( // title: 'Cancel'.tr, // onPressed: () { // Get.back(); // })); // } // // الحالة 2: نجاح (Success) // else if (res['status'] == 'success') { // // إغلاق أي ديالوج سابق (مثل Loading) // if (Get.isDialogOpen ?? false) Get.back(); // Get.snackbar("Success".tr, "The invitation was sent successfully".tr, // backgroundColor: AppColor.greenColor, colorText: Colors.white); // List tokensData = res['data']; // for (var device in tokensData) { // String tokenParent = device['token']; // NotificationService.sendNotification( // category: "Trip Monitoring", // target: tokenParent, // title: "Trip Monitoring".tr, // body: "Click to track the trip".tr, // isTopic: false, // tone: 'tone1', // driverList: [rideId, driverId], // ); // // حفظ آخر توكن // box.write(BoxName.tokenParent, tokenParent); // } // box.write(BoxName.parentTripSelected, true); // } // } // // Function to check if the point is inside the polygon // bool isPointInPolygon(LatLng point, List polygon) { // int intersections = 0; // for (int i = 0; i < polygon.length; i++) { // LatLng vertex1 = polygon[i]; // LatLng vertex2 = // polygon[(i + 1) % polygon.length]; // Loop back to the start // if (_rayIntersectsSegment(point, vertex1, vertex2)) { // intersections++; // } // } // // If the number of intersections is odd, the point is inside // return intersections % 2 != 0; // } // // Helper function to check if a ray from the point intersects with a polygon segment // bool _rayIntersectsSegment(LatLng point, LatLng vertex1, LatLng vertex2) { // double px = point.longitude; // double py = point.latitude; // double v1x = vertex1.longitude; // double v1y = vertex1.latitude; // double v2x = vertex2.longitude; // double v2y = vertex2.latitude; // // Check if the point is outside the vertical bounds of the segment // if ((py < v1y && py < v2y) || (py > v1y && py > v2y)) { // return false; // } // // Calculate the intersection of the ray and the segment // double intersectX = v1x + (py - v1y) * (v2x - v1x) / (v2y - v1y); // // Check if the intersection is to the right of the point // return intersectX > px; // } // bool isInUniversity = false; // // Function to check if the passenger is in any university polygon // // Function to check if the passenger is in any university polygon and return the university name // String checkPassengerLocation(LatLng passengerLocation, // List> universityPolygons, List universityNames) { // for (int i = 0; i < universityPolygons.length; i++) { // if (isPointInPolygon(passengerLocation, universityPolygons[i])) { // isInUniversity = true; // return "Passenger is in ${universityNames[i]}"; // } // } // return "Passenger is not in any university"; // } // String passengerLocationStringUnvirsity = 'unKnown'; // void getPassengerLocationUniversity() { // // Check if the passenger is inside any of the university polygons and get the university name // passengerLocationStringUnvirsity = checkPassengerLocation( // passengerLocation, // UniversitiesPolygons.universityPolygons, // UniversitiesPolygons.universityNames, // ); // if (passengerLocationStringUnvirsity != 'unKnown') { // // Get.snackbar('you are in $passengerLocationStringUnvirsity', ""); // } // Log.print(passengerLocationStringUnvirsity); // } // // Initialize polygons from UniversitiesPolygons // void _initializePolygons() { // List> universityPolygons = // UniversitiesPolygons.universityPolygons; // for (int i = 0; i < universityPolygons.length; i++) { // Polygon polygon = Polygon( // polygonId: PolygonId('univ_$i'), // points: universityPolygons[i], // fillColor: Colors.blueAccent.withOpacity(0.2), // strokeColor: Colors.blueAccent, // strokeWidth: 2, // ); // polygons.add(polygon); // } // update(); // } // LatLng driverLocationToPassenger = const LatLng(32, 35); // Future getDriverCarsLocationToPassengerAfterApplied() async { // // driverCarsLocationToPassengerAfterApplied // // 1. الشرط الأمني: تتبع فقط إذا كانت الرحلة نشطة // bool isRideActive = (statusRide == 'Apply' || // statusRide == 'Arrived' || // statusRide == 'Begin' || // currentRideState.value == RideState.driverApplied || // currentRideState.value == RideState.driverArrived || // currentRideState.value == RideState.inProgress); // if (!isRideActive || // statusRide == 'Finished' || // statusRide == 'Cancel' || // currentRideState.value == RideState.finished || // currentRideState.value == RideState.noRide || // currentRideState.value == RideState.preCheckReview) { // return; // } // // 2. منع التداخل (Blocking) // if (_isFetchingDriverLocation) return; // _isFetchingDriverLocation = true; // try { // var res = await CRUD().get( // link: AppLink.getDriverCarsLocationToPassengerAfterApplied, // payload: {'driver_id': driverId}); // if (res != 'failure') { // datadriverCarsLocationToPassengerAfterApplied = jsonDecode(res); // if (datadriverCarsLocationToPassengerAfterApplied['message'] != null && // datadriverCarsLocationToPassengerAfterApplied['message'] // .isNotEmpty) { // var _data = // datadriverCarsLocationToPassengerAfterApplied['message'][0]; // LatLng newDriverPos = LatLng( // double.parse(_data['latitude'].toString()), // double.parse(_data['longitude'].toString())); // // أضف هذا السطر لتقليل استهلاك الذاكرة // if (driverCarsLocationToPassengerAfterApplied.length > 10) { // driverCarsLocationToPassengerAfterApplied.removeAt(0); // } // driverLocationToPassenger = newDriverPos; // driverCarsLocationToPassengerAfterApplied.add(newDriverPos); // // 🔥 الإضافة هنا أيضاً 🔥 // // 🔥 تحديث التوقيت حتى لو جاءت من API لكي يهدأ الحارس قليلاً // _lastSocketLocationTime = DateTime.now(); // _checkAndRecalculateIfDeviated(newDriverPos); // // [تعديل هام] تنظيف آمن: لا نحذف ماركر السائق الحالي // clearMarkersExceptStartEndAndDriver(); // // تحريك الماركر // reloadMarkerDriverCarsLocationToPassengerAfterApplied(); // } // } // update(); // } catch (e) { // Log.print('Error fetching driver location: $e'); // } finally { // _isFetchingDriverLocation = false; // } // } // Future runEvery30SecondsUntilConditionMet() async { // // Calculate the duration of the trip in minutes. // double tripDurationInMinutes = durationToPassenger / 5; // int loopCount = tripDurationInMinutes.ceil(); // // If the trip duration is less than or equal to 50 minutes, then break the loop. // for (var i = 0; i < loopCount; i++) { // // Wait for 50 seconds. // await Future.delayed(const Duration(seconds: 5)); // if (rideTimerBegin == true || statusRide == 'Apply') { // await getDriverCarsLocationToPassengerAfterApplied(); // reloadMarkerDriverCarsLocationToPassengerAfterApplied(); // } // } // } // Future runWhenRideIsBegin() async { // // Calculate the duration of the trip in minutes. // double tripDurationInMinutes = durationToRide / 6; // int loopCount = tripDurationInMinutes.ceil(); // // If the trip duration is less than or equal to 50 minutes, then break the loop. // clearMarkersExceptStartEnd(); // for (var i = 0; i < loopCount; i++) { // // Wait for 50 seconds. // await Future.delayed(const Duration(seconds: 4)); // // if (rideTimerBegin == true && statusRide == 'Apply') { // await getDriverCarsLocationToPassengerAfterApplied(); // // } // reloadMarkerDriverCarsLocationToPassengerAfterApplied(); // } // } // Timer? _timer; // // final int updateIntervalMs = 100; // Update every 100ms // // final double minMovementThreshold = // // 1.0; // Minimum movement in meters to trigger update // void clearMarkersExceptStartEndAndDriver() { // markers.removeWhere((marker) { // String id = marker.markerId.value; // // لا تحذف نقطة البداية // if (id == 'start') return false; // // لا تحذف نقطة النهاية // if (id == 'end') return false; // // لا تحذف السائق الحالي // if (id == currentDriverMarkerId) return false; // // احذف أي شيء آخر (مثل السيارات التي ظهرت وقت البحث) // return true; // }); // // ملاحظة: لا نستدعي update() هنا لأننا سنستدعيها في نهاية الدالة الرئيسية // } // void clearMarkersExceptStartEnd() { // markers.removeWhere((marker) { // String id = marker.markerId.value; // return id != 'start' && id != 'end'; // }); // update(); // } // // 1. تعريف ID ثابت للسائق طوال الرحلة // String get currentDriverMarkerId => 'driver_marker_$driverId'; // void reloadMarkerDriverCarsLocationToPassengerAfterApplied() { // if (datadriverCarsLocationToPassengerAfterApplied == null || // datadriverCarsLocationToPassengerAfterApplied['message'] == null || // datadriverCarsLocationToPassengerAfterApplied['message'].isEmpty) { // return; // } // var driverData = // datadriverCarsLocationToPassengerAfterApplied['message'][0]; // // جلب الإحداثيات الجديدة // LatLng newPosition = LatLng(double.parse(driverData['latitude'].toString()), // double.parse(driverData['longitude'].toString())); // double newHeading = // double.tryParse(driverData['heading'].toString()) ?? 0.0; // // تحديد الأيقونة // String icon; // if (driverData['model'].toString().contains('دراجة') || // driverData['make'].toString().contains('دراجة')) { // icon = motoIcon; // } else if (driverData['gender'] == 'Female') { // icon = ladyIcon; // } else { // icon = carIcon; // } // // 2. البحث عن الماركر الجديد وتحديثه أو إنشاء جديد // final String markerId = currentDriverMarkerId; // final mId = MarkerId(markerId); // final existingMarker = markers.cast().firstWhere( // (m) => m?.markerId == mId, // orElse: () => null, // ); // if (existingMarker != null) { // _smoothlyUpdateMarker(existingMarker, newPosition, newHeading, icon); // } else { // markers = { // ...markers, // Marker( // markerId: mId, // position: newPosition, // rotation: newHeading, // icon: InlqBitmap.fromStyleImage(icon), // anchor: const Offset(0.5, 0.5), // ), // }; // update(); // } // } // // التأكد من دالة التحريك السلس // void _smoothlyUpdateMarker( // Marker oldMarker, LatLng newPosition, double newHeading, String icon) { // double distance = Geolocator.distanceBetween( // oldMarker.position.latitude, // oldMarker.position.longitude, // newPosition.latitude, // newPosition.longitude); // if (distance < 2.0) return; // final MarkerId markerIdKey = oldMarker.markerId; // _animationTimers[markerIdKey.value]?.cancel(); // int ticks = 0; // const int totalSteps = 20; // const int stepDuration = 50; // double latStep = // (newPosition.latitude - oldMarker.position.latitude) / totalSteps; // double lngStep = // (newPosition.longitude - oldMarker.position.longitude) / totalSteps; // double headingStep = (newHeading - oldMarker.rotation) / totalSteps; // LatLng currentPos = oldMarker.position; // double currentHeading = oldMarker.rotation; // _animationTimers[markerIdKey.value] = // Timer.periodic(const Duration(milliseconds: stepDuration), (timer) { // ticks++; // currentPos = // LatLng(currentPos.latitude + latStep, currentPos.longitude + lngStep); // currentHeading += headingStep; // // Update the marker in the set // final updatedMarker = oldMarker.copyWith( // position: currentPos, // rotation: currentHeading, // icon: InlqBitmap.fromStyleImage(icon), // ); // markers = { // ...markers.where((m) => m.markerId != markerIdKey), // updatedMarker, // }; // // Native update through controller to avoid UI rebuild // if (mapController != null) { // mapController!.animateCamera(CameraUpdate.newLatLng( // currentPos)); // Optional: Follow car if needed // // Note: IntaleqMapController doesn't expose raw symbol update yet for Marker object, // // but declarative update via GetBuilder is fast. // } // update(); // if (ticks >= totalSteps) { // timer.cancel(); // _animationTimers.remove(markerIdKey.value); // } // }); // } // void _updateMarkerPosition( // LatLng newPosition, double newHeading, String icon) { // const String markerId = 'driverToPassengers'; // final mId = MarkerId(markerId); // final existingMarker = markers.cast().firstWhere( // (m) => m?.markerId == mId, // orElse: () => null, // ); // if (existingMarker != null) { // _smoothlyUpdateMarker(existingMarker, newPosition, newHeading, icon); // } else { // markers = { // ...markers, // Marker( // markerId: mId, // position: newPosition, // rotation: newHeading, // icon: InlqBitmap.fromStyleImage(icon), // anchor: const Offset(0.5, 0.5), // ), // }; // update(); // } // mapController?.animateCamera(CameraUpdate.newLatLng(newPosition)); // } // @override // void onClose() { // Log.print( // "--- MapPassengerController: Closing and cleaning up all resources. ---"); // // 1. إلغاء المؤقتات الفردية (باستخدام ?. الآمن) // timerToPassengerFromDriverAfterApplied?.cancel(); // _timer?.cancel(); // _masterTimer?.cancel(); // (أضف المؤقت الرئيسي) // _camThrottle?.cancel(); // (أضف مؤقت الكاميرا) // _heartbeatTimer?.cancel(); // EmergencySignalService.instance.stopListening(); // if (isSocketConnected) { // socket.emit('unsubscribe_all', // {'passenger_id': box.read(BoxName.passengerID).toString()}); // socket.disconnect(); // socket.dispose(); // } // // 2. إلغاء جميع المؤقتات في الخريطة (للتحريكات السلسة) // _animationTimers.forEach((key, timer) { // timer.cancel(); // }); // _animationTimers.clear(); // // 3. إغلاق متحكمات البث (StreamControllers) لمنع تسريب الذاكرة // if (!_timerStreamController.isClosed) { // _timerStreamController.close(); // } // if (!_beginRideStreamController.isClosed) { // _beginRideStreamController.close(); // } // if (!_rideStatusStreamController.isClosed) { // _rideStatusStreamController.close(); // } // if (!timerController.isClosed) { // timerController.close(); // } // // 4. التخلص من متحكم الخريطة (ممارسة جيدة) // mapController = null; // Log.print("--- Cleanup complete. ---"); // super.onClose(); // } // restCounter() { // clearPlacesDestination(); // clearPolyline(); // data = []; // rideConfirm = false; // shouldFetch = false; // timeToPassengerFromDriverAfterApplied = 0; // update(); // } // //driver behaviour // double calculateBearing(double lat1, double lon1, double lat2, double lon2) { // double deltaLon = lon2 - lon1; // double y = sin(deltaLon) * cos(lat2); // double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(deltaLon); // double bearing = atan2(y, x); // return (bearing * 180 / pi + 360) % 360; // تحويل إلى درجات // } // void analyzeBehavior(Position currentPosition, List routePoints) { // double actualBearing = currentPosition.heading; // الاتجاه الفعلي من GPS // double expectedBearing = calculateBearing( // routePoints[0].latitude, // routePoints[0].longitude, // routePoints[1].latitude, // routePoints[1].longitude, // ); // double bearingDifference = (expectedBearing - actualBearing).abs(); // if (bearingDifference > 30) { // Log.print("⚠️ السائق انحرف عن المسار!"); // } // } // void detectStops(Position currentPosition) { // if (currentPosition.speed < 0.5) { // Log.print("🚦 السائق توقف في موقع غير متوقع!"); // } // } // Future cancelRideAfterRejectFromAll() async { // clearPlacesDestination(); // clearPolyline(); // data = []; // await CRUD().post( // link: "${AppLink.server}/ride/rides/cancel_ride_by_passenger.php", // payload: { // "ride_id": rideId.toString(), // Convert to String // "reason": 'notApplyFromAnyDriver' // }); // rideConfirm = false; // statusRide == 'Cancel'; // isSearchingWindow = false; // shouldFetch = false; // isPassengerChosen = false; // isCashConfirmPageShown = false; // // totalStepDurations = 0; // isCashSelectedBeforeConfirmRide = false; // timeToPassengerFromDriverAfterApplied = 0; // changeCancelRidePageShow(); // remainingTime = 0; // update(); // } // // متغيرات أسباب الإلغاء // int selectedReasonIndex = -1; // String selectedReasonText = ""; // TextEditingController otherReasonController = TextEditingController(); // /// تحديث السبب المختار // void selectReason(int index, String reason) { // selectedReasonIndex = index; // selectedReasonText = reason; // update(); // } // /// **دالة إلغاء الرحلة (النهائية)** // Future cancelRide() async { // // 1. التحقق من اختيار سبب // if (selectedReasonIndex == -1) { // Get.snackbar( // 'Attention'.tr, // 'Please select a reason first'.tr, // snackPosition: SnackPosition.BOTTOM, // backgroundColor: Colors.orange, // colorText: Colors.white, // ); // return; // } // // 2. تجهيز نص السبب النهائي // String finalReason = selectedReasonText; // if (finalReason == "Other".tr) { // if (otherReasonController.text.trim().isEmpty) { // Get.snackbar("Attention".tr, "Please write the reason...".tr, // backgroundColor: Colors.red, colorText: Colors.white); // return; // } // finalReason = otherReasonController.text.trim(); // } // // 3. التنظيف المحلي الفوري (UX Optimization) // Get.back(); // إغلاق الـ BottomSheet // if (isCancelRidePageShown) // changeCancelRidePageShow(); // إخفاء زر الإلغاء إن وجد // // 🔥 استدعاء دالة التنظيف الشاملة هنا 🔥 // resetAllMapStates(); // // إيقاف جميع التايمرات // // إيقاف جميع التايمرات // stopAllTimers(); // currentRideState.value = RideState.cancelled; // await RideLiveNotification.cancel(); // إغلاق أندرويد // IosLiveActivityService.endRideActivity(); // ✅ إغلاق iOS // PipService.disablePip(); // ✅ إيقاف PiP عند الإلغاء // // 4. الاتصال بالسيرفر لإلغاء الرحلة وإبلاغ السائق // if (rideId != 'yet' && rideId != null) { // Log.print( // '📡 Sending Cancel Request to Server with Reason: $finalReason'); // try { // await CRUD().post( // link: "${AppLink.server}/ride/rides/cancel_ride_by_passenger.php", // payload: { // "ride_id": rideId.toString(), // "reason": finalReason // ✅ إرسال السبب للسيرفر // }, // ); // // لا داعي لإرسال FCM أو Socket يدوياً من هنا، PHP يقوم بذلك // } catch (e) { // Log.print("Error cancelling on server: $e"); // } // } // // 5. العودة للصفحة الرئيسية // Get.offAll(() => const MapPagePassenger()); // } // void changePickerShown() { // isPickerShown = !isPickerShown; // heightPickerContainer = isPickerShown == true ? 150 : 90; // update(); // } // // ── Multi-Waypoint Methods ────────────────────────────────────────────────── // void addMenuWaypoint() { // if (activeMenuWaypointCount >= 2) return; // activeMenuWaypointCount++; // // Increase expanded bottom menu height to accommodate new waypoint row // mainBottomMenuMapHeight = Get.height * .6 + (activeMenuWaypointCount * 56); // update(); // } // void removeMenuWaypoint(int index) { // if (index < 0 || index >= 2) return; // // Shift items if removing first waypoint while second exists // if (index == 0 && activeMenuWaypointCount == 2) { // menuWaypoints[0] = menuWaypoints[1]; // menuWaypointNames[0] = menuWaypointNames[1]; // } // menuWaypoints[activeMenuWaypointCount - 1] = null; // menuWaypointNames[activeMenuWaypointCount - 1] = ''; // activeMenuWaypointCount--; // mainBottomMenuMapHeight = Get.height * .6 + (activeMenuWaypointCount * 56); // update(); // } // void clearAllMenuWaypoints() { // menuWaypoints = [null, null]; // menuWaypointNames = ['', '']; // activeMenuWaypointCount = 0; // isPickingWaypoint = false; // pickingWaypointIndex = -1; // update(); // } // void startPickingWaypointOnMap(int index) { // pickingWaypointIndex = index; // isPickingWaypoint = true; // isPickerShown = true; // heightPickerContainer = 150; // // Close the expanded menu to show the map picker // isMainBottomMenuMap = true; // mainBottomMenuMapHeight = Get.height * .22; // update(); // } // void setMenuWaypointFromMap(int index, LatLng position) { // Log.print('📍 setMenuWaypointFromMap called: index=$index, pos=$position'); // if (index < 0 || index >= 2) return; // menuWaypoints[index] = position; // menuWaypointNames[index] = // '${position.latitude.toStringAsFixed(4)}, ${position.longitude.toStringAsFixed(4)}'; // isPickingWaypoint = false; // pickingWaypointIndex = -1; // isPickerShown = false; // // Re-open expanded menu // isMainBottomMenuMap = false; // mainBottomMenuMapHeight = Get.height * .6 + (activeMenuWaypointCount * 56); // update(); // } // void setMenuWaypointFromSearch(int index, LatLng pos, String name) { // if (index < 0 || index >= 2) return; // menuWaypoints[index] = pos; // menuWaypointNames[index] = name; // update(); // } // /// Build OSRM waypoint coordinate string for the route URL // String _buildOsrmWaypointCoords() { // String coords = ''; // for (int i = 0; i < activeMenuWaypointCount; i++) { // final wp = menuWaypoints[i]; // if (wp != null) { // coords += ';${wp.longitude},${wp.latitude}'; // } // } // return coords; // } // void changeHeightPointsPageForRider() { // isPointsPageForRider = !isPointsPageForRider; // heightPointsPageForRider = isPointsPageForRider == true ? Get.height : 0; // update(); // } // getCoordinateFromMapWayPoints(int index) { // placesCoordinate[index] = newStartPointLocation.toString(); // update(); // } // // --- ابدأ الإضافة هنا --- // // 1. قائمة لتخزين نقاط التوقف // List> waypoints = []; // // 2. دالة لإضافة نقطة توقف جديدة // void addWaypoint(Map placeDetails) { // // يمكنك إضافة منطق للتحقق من عدد نقاط التوقف المسموح بها هنا // waypoints.add(placeDetails); // update(); // لتحديث الواجهة // // TODO: أضف هنا استدعاء دالة إعادة رسم المسار مع نقاط التوقف الجديدة // // getDirectionMapWithWaypoints(); // } // // 3. دالة لحذف نقطة توقف // void removeWaypoint(int index) { // if (index >= 0 && index < waypoints.length) { // waypoints.removeAt(index); // update(); // لتحديث الواجهة // // TODO: أضف هنا استدعاء دالة إعادة رسم المسار بعد حذف النقطة // // getDirectionMapWithWaypoints(); // } // } // // --- انتهى --- // void changeMainBottomMenuMap() { // if (isWayPointStopsSheetUtilGetMap == true) { // changeWayPointSheet(); // } else { // isMainBottomMenuMap = !isMainBottomMenuMap; // mainBottomMenuMapHeight = // isMainBottomMenuMap == true ? Get.height * .22 : Get.height * .6; // isWayPointSheet = false; // if (heightMenuBool == true) { // getDrawerMenu(); // } // initilizeGetStorage(); // update(); // } // } // void downPoints() { // if (Get.find().wayPoints.length < 2) { // isWayPointStopsSheetUtilGetMap = false; // isWayPointSheet = false; // wayPointSheetHeight = isWayPointStopsSheet ? Get.height * .45 : 0; // // changeWayPointStopsSheet(); // update(); // } // // changeWayPointStopsSheet(); // // isWayPointSheet = false; // update(); // } // void changeWayPointSheet() { // isWayPointSheet = !isWayPointSheet; // wayPointSheetHeight = isWayPointSheet == false ? 0 : Get.height * .45; // // if (heightMenuBool == true) { // // getDrawerMenu(); // // } // update(); // } // void changeWayPointStopsSheet() { // // int waypointsLength = Get.find().wayPoints.length; // if (wayPointIndex > -1) { // isWayPointStopsSheet = true; // isWayPointStopsSheetUtilGetMap = true; // } // isWayPointStopsSheet = !isWayPointStopsSheet; // wayPointSheetHeight = isWayPointStopsSheet ? Get.height * .45 : 0; // // if (heightMenuBool == true) { // // getDrawerMenu(); // // } // update(); // } // changeHeightPlaces() { // if (placesDestination.isEmpty) { // height = 0; // update(); // } // height = 150; // update(); // } // changeHeightStartPlaces() { // if (placesStart.isEmpty) { // height = 0; // update(); // } // height = 150; // update(); // } // changeHeightPlacesAll(int index) { // if (placeListResponseAll[index].isEmpty) { // height = 0; // update(); // } // height = 150; // update(); // } // changeHeightPlaces1() { // if (wayPoint1.isEmpty) { // height = 0; // update(); // } // height = 150; // update(); // } // changeHeightPlaces2() { // if (wayPoint2.isEmpty) { // height = 0; // update(); // } // height = 150; // update(); // } // changeHeightPlaces3() { // if (wayPoint3.isEmpty) { // height = 0; // update(); // } // height = 150; // update(); // } // changeHeightPlaces4() { // if (wayPoint4.isEmpty) { // height = 0; // update(); // } // height = 150; // update(); // } // hidePlaces() { // height = 0; // update(); // } // /// تحويل نصف قطر بالكيلومتر إلى دلتا درجات عرض // // double _haversineKm(double lat1, double lon1, double lat2, double lon2) { // // const R = 6371.0; // km // // final dLat = (lat2 - lat1) * math.pi / 180.0; // // final dLon = (lon2 - lon1) * math.pi / 180.0; // // final a = math.sin(dLat / 2) * math.sin(dLat / 2) + // // math.cos(lat1 * math.pi / 180.0) * // // math.cos(lat2 * math.pi / 180.0) * // // math.sin(dLon / 2) * // // math.sin(dLon / 2); // // final c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)); // // return R * c; // // } // /// تحويل نصف قطر بالكيلومتر إلى دلتا درجات عرض // // double _kmToLatDelta(double km) => km / 111.0; // // /// تحويل نصف قطر بالكيلومتر إلى دلتا درجات طول (تعتمد على خط العرض) // // double _kmToLngDelta(double km, double atLat) => // // km / (111.320 * math.cos(atLat * math.pi / 180.0)).abs().clamp(1e-6, 1e9); // /// حساب درجة التطابق النصي (كل كلمة تبدأ بها الاسم = 2 نقاط، يحتويها = 1 نقطة) // // double _relevanceScore(String name, String query) { // // final n = name.toLowerCase(); // // final parts = // // query.toLowerCase().split(RegExp(r'\s+')).where((p) => p.length >= 2); // // double s = 0.0; // // for (final p in parts) { // // if (n.startsWith(p)) { // // s += 2.0; // // } else if (n.contains(p)) { // // s += 1.0; // // } // // } // // return s; // // } // // الدالة الرئيسية لجلب الأماكن من السيرفر وترتيبها // // انسخ هذه الدوال والصقها داخل كلاس الكنترولر الخاص بك // // ----------------------------------------------------------------- // // --== الدالة الرئيسية للبحث ==-- // // ----------------------------------------------------------------- // /// الدالة الرئيسية لجلب الأماكن من السيرفر وترتيبها // // انسخ هذه الدوال والصقها داخل كلاس الكنترولر الخاص بك // // ----------------------------------------------------------------- // // --== الدالة الرئيسية للبحث ==-- // // ----------------------------------------------------------------- // /// الدالة الرئيسية لجلب الأماكن من السيرفر وترتيبها // Future getPlaces() async { // final q = placeDestinationController.text.trim(); // if (q.isEmpty || q.length < 3) { // placesDestination = []; // update(); // return; // } // final lat = passengerLocation.latitude; // final lng = passengerLocation.longitude; // final country = CountryPolygons.getCountryName(passengerLocation); // try { // final url = // '${AppLink.searchGeocoding}?q=${Uri.encodeComponent(q)}&lat=$lat&lng=$lng&radius=15000&country=$country'; // final response = await CRUD().getMapSaas(link: url); // if (response != null && response['results'] is List) { // List results = List.from(response['results']); // final List filteredResults = []; // final Set seenPlaces = {}; // for (final p in results) { // final name = p['name_ar'] ?? p['name'] ?? ''; // final district = p['district'] ?? ''; // final plat = p['latitude']?.toString() ?? '0'; // final plng = p['longitude']?.toString() ?? '0'; // final dedupeKey = // "${name.trim().toLowerCase()}_${district.trim().toLowerCase()}"; // if (!seenPlaces.contains(dedupeKey)) { // seenPlaces.add(dedupeKey); // p['distanceKm'] = (p['distance'] as num).toDouble() / 1000.0; // p['latitude'] = plat; // p['longitude'] = plng; // p['name'] = name; // p['address'] = p['full_address'] ?? // (district.isNotEmpty // ? "$district، ${p['governorate'] ?? ''}" // : (p['governorate'] ?? '')); // filteredResults.add(p); // } // } // placesDestination = filteredResults; // update(); // } // } catch (e) { // Log.print('Exception in getPlaces: $e'); // } // } // // ----------------------------------------------------------------- // // --== دوال مساعدة ==-- // // ----------------------------------------------------------------- // /// تحسب المسافة بين نقطتين بالكيلومتر (معادلة هافرساين) // double _haversineKm(double lat1, double lon1, double lat2, double lon2) { // const R = 6371.0; // نصف قطر الأرض بالكيلومتر // final dLat = (lat2 - lat1) * (pi / 180.0); // final dLon = (lon2 - lon1) * (pi / 180.0); // final rLat1 = lat1 * (pi / 180.0); // final rLat2 = lat2 * (pi / 180.0); // final a = sin(dLat / 2) * sin(dLat / 2) + // cos(rLat1) * cos(rLat2) * sin(dLon / 2) * sin(dLon / 2); // final c = 2 * atan2(sqrt(a), sqrt(1 - a)); // return R * c; // } // /// تحسب درجة تطابق بسيطة بين اسم المكان وكلمة البحث // double _relevanceScore(String placeName, String query) { // if (placeName.isEmpty || query.isEmpty) return 0.0; // final pLower = placeName.toLowerCase(); // final qLower = query.toLowerCase(); // if (pLower.startsWith(qLower)) return 1.0; // تطابق كامل في البداية // if (pLower.contains(qLower)) return 0.5; // تحتوي على الكلمة // return 0.0; // } // /// تحويل كيلومتر إلى فرق درجات لخط العرض // double _kmToLatDelta(double km) { // const kmInDegree = 111.32; // return km / kmInDegree; // } // /// تحويل كيلومتر إلى فرق درجات لخط الطول (يعتمد على خط العرض الحالي) // double _kmToLngDelta(double km, double latitude) { // const kmInDegree = 111.32; // return km / (kmInDegree * cos(latitude * (pi / 180.0))); // } // // var languageCode; // // // تحديد اللغة حسب الإدخال // // if (RegExp(r'[a-zA-Z]').hasMatch(placeDestinationController.text)) { // // languageCode = 'en'; // // } else { // // languageCode = 'ar'; // // } // // final bool isTextEmpty = placeDestinationController.text.trim().isEmpty; // // var key = Platform.isAndroid ? AK.mapAPIKEY : AK.mapAPIKEYIOS; // // final Uri url = Uri.parse( // // isTextEmpty // // ? 'https://places.googleapis.com/v1/places:searchNearby?key=$key' // // : 'https://places.googleapis.com/v1/places:searchText?key=$key', // // ); // // Log.print('url: $url'); // // // بناء الجسم حسب نوع الطلب // // final body = isTextEmpty // // ? jsonEncode({ // // "languageCode": languageCode, // // "locationRestriction": { // // "circle": { // // "center": { // // "latitude": passengerLocation.latitude, // // "longitude": passengerLocation.longitude // // }, // // "radius": 40000 // 40 كم // // } // // }, // // "maxResultCount": 10 // // }) // // : jsonEncode({ // // "textQuery": placeDestinationController.text, // // "languageCode": languageCode, // // "maxResultCount": 10, // // "locationBias": { // // "circle": { // // "center": { // // "latitude": passengerLocation.latitude, // // "longitude": passengerLocation.longitude // // }, // // "radius": 40000 // // } // // } // // }); // // final headers = { // // 'Content-Type': 'application/json', // // 'X-Goog-Api-Key': AK.mapAPIKEY, // // 'X-Goog-FieldMask': // // 'places.displayName,places.formattedAddress,places.location' // // }; // // try { // // final response = await http.post(url, headers: headers, body: body); // // Log.print('response: ${response.statusCode} - ${response.body}'); // // if (response.statusCode == 200) { // // final data = jsonDecode(response.body); // // placesDestination = data['places'] ?? []; // // update(); // // } else { // // Log.print('Error: ${response.statusCode} - ${response.reasonPhrase}'); // // } // // } catch (e) { // // Log.print('Exception: $e'); // // } // // } // getAIKey(String key) async { // var res = // await CRUD().get(link: AppLink.getapiKey, payload: {"keyName": key}); // if (res != 'failure') { // var d = jsonDecode(res)['message']; // return d[key].toString(); // } else {} // } // Future getPlacesStart() async { // final q = placeStartController.text.trim(); // if (q.isEmpty || q.length < 3) { // placesStart = []; // update(); // return; // } // final lat = passengerLocation.latitude; // final lng = passengerLocation.longitude; // final country = CountryPolygons.getCountryName(passengerLocation); // try { // final url = // '${AppLink.searchGeocoding}?q=${Uri.encodeComponent(q)}&lat=$lat&lng=$lng&radius=15000&country=$country'; // final response = await CRUD().getMapSaas(link: url); // if (response != null && response['results'] is List) { // List list = List.from(response['results']); // for (final p in list) { // p['distanceKm'] = (p['distance'] as num).toDouble() / 1000.0; // p['latitude'] = p['latitude'].toString(); // p['longitude'] = p['longitude'].toString(); // p['name'] = p['name_ar'] ?? p['name'] ?? ''; // p['address'] = p['full_address'] ?? // (p['district'] != null // ? "${p['district']}، ${p['governorate'] ?? ''}" // : (p['governorate'] ?? '')); // } // placesStart = list; // update(); // } // } catch (e) { // Log.print('Exception in getPlacesStart: $e'); // } // } // Future getPlacesListsWayPoint(int index) async { // final q = wayPoint0Controller.text.trim(); // if (q.length < 3) return; // final lat = passengerLocation.latitude; // final lng = passengerLocation.longitude; // final country = CountryPolygons.getCountryName(passengerLocation); // try { // final url = // '${AppLink.searchGeocoding}?q=${Uri.encodeComponent(q)}&lat=$lat&lng=$lng&radius=15000&country=$country'; // final response = await CRUD().getMapSaas(link: url); // if (response != null && response['results'] is List) { // List list = List.from(response['results']); // for (final p in list) { // p['distanceKm'] = (p['distance'] as num).toDouble() / 1000.0; // p['latitude'] = p['latitude'].toString(); // p['longitude'] = p['longitude'].toString(); // p['name'] = p['name_ar'] ?? p['name'] ?? ''; // p['address'] = p['full_address'] ?? // (p['district'] != null // ? "${p['district']}، ${p['governorate'] ?? ''}" // : (p['governorate'] ?? '')); // } // wayPoint0 = list; // placeListResponseAll[index] = list; // update(); // } // } catch (e) { // Log.print('Error fetching places in WayPoint: $e'); // } // } // // داخل MapPassengerController // bool lowPerf = false; // Timer? _camThrottle; // DateTime _lastUiUpdate = DateTime.fromMillisecondsSinceEpoch(0); // Future detectPerfMode() async { // try { // if (GetPlatform.isAndroid) { // final info = await DeviceInfoPlugin().androidInfo; // final sdk = info.version.sdkInt ?? 0; // final ram = info.availableRamSize ?? 0; // lowPerf = (sdk < 28) || (ram > 0 && ram < 3 * 1024 * 1024 * 1024); // } else { // lowPerf = false; // } // } catch (_) { // lowPerf = false; // } // update(); // } // // تحديث الكاميرا بثروتل // void onCameraMoveThrottled(CameraPosition pos) { // _camThrottle?.cancel(); // _camThrottle = Timer(const Duration(milliseconds: 160), () { // Log.print('📸 onCameraMoveThrottled: ${pos.target}'); // // ضع فقط المنطق الضروري هنا لتقليل الحمل // int waypointsLength = Get.find().wayPoints.length; // int index = wayPointIndex; // if (waypointsLength > 0) { // placesCoordinate[index] = // '${pos.target.latitude},${pos.target.longitude}'; // } // newMyLocation = pos.target; // }); // } // // Removed legacy light polylines since MapLibre vectors handle high-point geometries natively. // Future savePlaceToServer( // String latitude, String longitude, String name, String rate) async { // var data = { // 'latitude': latitude, // 'longitude': longitude, // 'name': name, // 'rate': rate, // }; // try { // CRUD().post( // link: AppLink.savePlacesServer, // payload: data, // ); // } catch (e) { // Log.print('Error: $e'); // } // } // Future getLocation() async { // Log.print('🛰️ getLocation() called'); // // Check if the app has permission to access location // permissionGranted = await location.hasPermission(); // if (permissionGranted == PermissionStatus.denied) { // permissionGranted = await location.requestPermission(); // if (permissionGranted != PermissionStatus.granted) { // // Location permission is still not granted, handle the error // return; // } // } // // Configure location accuracy // // LocationAccuracy desiredAccuracy = LocationAccuracy.high; // // Get the current location with a timeout to prevent hanging UI // LocationData? _locationData; // try { // _locationData = await location.getLocation().timeout( // const Duration(seconds: 5), // onTimeout: () { // Log.print("⚠️ Location fetch timed out after 5s."); // return LocationData.fromMap({ // "latitude": passengerLocation.latitude, // "longitude": passengerLocation.longitude, // "speed": 0.0 // }); // }, // ); // } catch (e) { // Log.print("⚠️ Error fetching location: $e"); // } // if (_locationData == null) { // isLoading = false; // update(); // return; // } // passengerLocation = // (_locationData.latitude != null && _locationData.longitude != null // ? LatLng(_locationData.latitude!, _locationData.longitude!) // : null)!; // // getLocationArea(passengerLocation.latitude, passengerLocation.longitude); // // Log.print('AppLink.endPoint: ${AppLink.endPoint}'); // // Log.print('BoxName.serverChosen: ${box.read(BoxName.serverChosen)}'); // newStartPointLocation = passengerLocation; // newMyLocation = passengerLocation; // // Resolve current location address // try { // getReverseGeocoding(passengerLocation).then((address) { // currentLocationString = address; // update(); // }); // } catch (e) { // Log.print('Error resolving current location: $e'); // } // // Trigger offline map caching for a 10km radius // OfflineMapService.instance // .downloadRegion(passengerLocation, radiusKm: 10.0); // speed = _locationData.speed!; // // //print location details // isLoading = false; // update(); // } // void clearPolyline() { // polyLines.clear(); // update(); // } // LatLngBounds calculateBounds(double lat, double lng, double radiusInMeters) { // const double earthRadius = 6378137.0; // Earth's radius in meters // double latDelta = (radiusInMeters / earthRadius) * (180 / pi); // double lngDelta = // (radiusInMeters / (earthRadius * cos(pi * lat / 180))) * (180 / pi); // double minLat = lat - latDelta; // double maxLat = lat + latDelta; // double minLng = lng - lngDelta; // double maxLng = lng + lngDelta; // // Ensure the latitude is between -90 and 90 // minLat = max(-90.0, minLat); // maxLat = min(90.0, maxLat); // // Ensure the longitude is between -180 and 180 // minLng = (minLng + 180) % 360 - 180; // maxLng = (maxLng + 180) % 360 - 180; // // Ensure the bounds are in the correct order // if (minLng > maxLng) { // double temp = minLng; // minLng = maxLng; // maxLng = temp; // } // return LatLngBounds( // southwest: LatLng(minLat, minLng), // northeast: LatLng(maxLat, maxLng), // ); // } // void onMapCreated(IntaleqMapController controller) { // mapController = controller; // update(); // } // void onStyleLoaded() async { // Log.print('🗺️ Intaleq Map Style Loaded. Initializing...'); // isStyleLoaded = true; // _loadMapIcons(); // // Smart Camera Reset logic: // if (mapController != null) { // if (markers.isNotEmpty && lastComputedBounds != null) { // await _safeAnimateCameraBounds(lastComputedBounds); // } else { // mapController!.animateCamera( // CameraUpdate.newLatLng(passengerLocation), // ); // } // } // update(); // } // /// Safe wrapper for animateCamera Bounds to prevent native std::domain_error crash on iOS. // Future _safeAnimateCameraBounds(LatLngBounds? bounds, // {double left = 60, // double top = 60, // double right = 60, // double bottom = 60}) async { // if (bounds == null || mapController == null) return; // try { // // Ensure the coordinates are valid // if (bounds.northeast.latitude == bounds.southwest.latitude && // bounds.northeast.longitude == bounds.southwest.longitude) { // Log.print( // '⚠️ _safeAnimateCameraBounds: Bounds are a single point, zooming to point instead.'); // await mapController // ?.animateCamera(CameraUpdate.newLatLngZoom(bounds.northeast, 15)); // return; // } // // Small delay to ensure iOS view layout is fully ready // await Future.delayed(const Duration(milliseconds: 200)); // await mapController?.animateCamera( // CameraUpdate.newLatLngBounds( // bounds, // left: left, // top: top, // right: right, // bottom: bottom, // ), // ); // } catch (e) { // Log.print('❌ _safeAnimateCameraBounds CRASH PREVENTED: $e'); // // Final fallback to prevent device freeze // try { // await mapController // ?.animateCamera(CameraUpdate.newLatLngZoom(bounds.northeast, 14)); // } catch (_) {} // } // } // Future _loadMapIcons() async { // // Wait up to 3 seconds for the map style to finish loading // for (int i = 0; i < 15; i++) { // if (mapController != null && isStyleLoaded) break; // await Future.delayed(const Duration(milliseconds: 200)); // } // if (mapController == null || !isStyleLoaded) { // Log.print( // '⚠️ _loadMapIcons: mapController or style not ready. Icons may not load.'); // } // await _addMapImage(startIcon, 'assets/images/A.png'); // await _addMapImage(endIcon, 'assets/images/b.png'); // await _addMapImage(carIcon, 'assets/images/car.png'); // await _addMapImage(motoIcon, 'assets/images/moto.png'); // await _addMapImage(ladyIcon, 'assets/images/lady.png'); // await _addMapImage('picker_icon', 'assets/images/picker.png'); // // Waypoint markers - use moto1 & lady1 as colored waypoint icons // await _addMapImage('orange_marker', 'assets/images/moto1.png'); // await _addMapImage('violet_marker', 'assets/images/lady1.png'); // } // Future _addMapImage(String id, String path) async { // try { // final ByteData bytes = await rootBundle.load(path); // // Resize car icons for better visibility on map (e.g. 120px) // final size = _getImageSize(id); // if (size != null && (id == carIcon || id == motoIcon || id == ladyIcon)) { // final resized = await _resizeImage(bytes.buffer.asUint8List(), size); // await mapController?.addImage(id, resized); // Log.print( // '✅ Successfully added resized map image: $id (${size}x${size})'); // } else { // await mapController?.addImage(id, bytes.buffer.asUint8List()); // Log.print('✅ Successfully added map image: $id'); // } // } catch (e) { // Log.print('❌ Error loading map icon $id: $e'); // } // } // int? _getImageSize(String id) { // if (id == carIcon || id == motoIcon || id == ladyIcon) return 120; // return null; // } // Future _resizeImage(Uint8List bytes, int size) async { // return await compute((Uint8List data) { // final image = img.decodeImage(data); // if (image == null) return data; // final resized = img.copyResize(image, width: size, height: size); // return Uint8List.fromList(img.encodePng(resized)); // }, bytes); // } // // Wait up to 3 seconds for the map style to finish loading // void updateCurrentLocationFromCamera(LatLng target) { // Log.print('📍 updateCurrentLocationFromCamera: $target'); // newMyLocation = target; // if (startLocationFromMap == true) { // Log.print('📍 Updating startLocationFromMap to $target'); // newStartPointLocation = target; // } else if (passengerStartLocationFromMap == true) { // Log.print('📍 Updating passengerStartLocationFromMap to $target'); // newStartPointLocation = target; // } // int waypointsLength = Get.find().wayPoints.length; // if (waypointsLength > 0 && // wayPointIndex >= 0 && // wayPointIndex < placesCoordinate.length) { // Log.print('📍 Updating wayPointIndex $wayPointIndex to $target'); // placesCoordinate[wayPointIndex] = // '${target.latitude},${target.longitude}'; // } // update(); // } // String durationByPassenger = ''; // late DateTime newTime1 = DateTime.now(); // late DateTime timeFromDriverToPassenger = DateTime.now(); // String distanceByPassenger = ''; // late Duration durationFromDriverToPassenger; // double nearestDistance = double.infinity; // Future getNearestDriverByPassengerLocation() async { // if (!rideConfirm) { // if (dataCarsLocationByPassenger != 'failure' && // dataCarsLocationByPassenger != null && // dataCarsLocationByPassenger['message'] != null && // dataCarsLocationByPassenger['message'].length > 0) { // double nearestDistance = double.infinity; // Initialize nearest distance // CarLocation? nearestCar; // for (var i = 0; // i < dataCarsLocationByPassenger['message'].length; // i++) { // var carLocation = dataCarsLocationByPassenger['message'][i]; // // Log.print('carLocation: $carLocation'); // try { // // Calculate distance between passenger's location and current driver's location // final distance = Geolocator.distanceBetween( // passengerLocation.latitude, // passengerLocation.longitude, // double.parse(carLocation['latitude']), // double.parse(carLocation['longitude']), // ); // // Calculate duration assuming an average speed of 25 km/h (adjust as needed) // int durationToPassenger = (distance / 1000 / 25 * 3600).round(); // // Log.print('distance: $distance'); // // Log.print('durationToPassenger: $durationToPassenger'); // // Log.print('passengerLocation: $passengerLocation'); // // Log.print('carLocation: $carLocation'); // // Log.print('distance: $distance meters'); // // Log.print('durationToPassenger: $durationToPassenger seconds'); // // Update the UI with the distance and duration for each car // update(); // // If this distance is smaller than the nearest distance found so far, update nearestCar // if (distance < nearestDistance) { // nearestDistance = distance; // nearestCar = CarLocation( // distance: distance, // duration: durationToPassenger.toDouble(), // id: carLocation['driver_id'], // latitude: double.parse(carLocation['latitude']), // longitude: double.parse(carLocation['longitude']), // ); // // Log.print('nearestCar: $nearestCar'); // // Update the UI with the nearest driver // update(); // } // } catch (e) { // Log.print('Error calculating distance/duration: $e'); // } // } // // Return the nearest car found // return nearestCar; // } // } // // Return null if no drivers are found or if ride is confirmed // return null; // } // getNearestDriverByPassengerLocationAPIGOOGLE() async { // if (polyLines.isEmpty || data.isEmpty) { // return null; // Early return if data is empty // } // if (!rideConfirm) { // double nearestDistance = double.infinity; // if (dataCarsLocationByPassenger != 'failure') { // if (dataCarsLocationByPassenger['message'].length > 0) { // for (var i = 0; // i < dataCarsLocationByPassenger['message'].length; // i++) { // var carLocation = dataCarsLocationByPassenger['message'][i]; // // } // // isloading = true; // update(); // // Make API request to get exact distance and duration // String apiUrl = // '${AppLink.googleMapsLink}distancematrix/json?destinations=${carLocation['latitude']},${carLocation['longitude']}&origins=${passengerLocation.latitude},${passengerLocation.longitude}&units=metric&key=${AK.mapAPIKEY}'; // var response = await CRUD().getGoogleApi(link: apiUrl, payload: {}); // if (response != null && response['status'] == "OK") { // var data = response; // // Extract distance and duration from the response and handle accordingly // int distance1 = // data['rows'][0]['elements'][0]['distance']['value']; // distanceByPassenger = // data['rows'][0]['elements'][0]['distance']['text']; // durationToPassenger = // data['rows'][0]['elements'][0]['duration']['value']; // durationFromDriverToPassenger = // Duration(seconds: durationToPassenger.toInt()); // newTime1 = currentTime.add(durationFromDriverToPassenger); // timeFromDriverToPassenger = // newTime1.add(Duration(minutes: 2.toInt())); // durationByPassenger = // data['rows'][0]['elements'][0]['duration']['text']; // update(); // if (distance1 < nearestDistance) { // nearestDistance = distance1.toDouble(); // nearestCar = CarLocation( // distance: distance1.toDouble(), // duration: durationToPassenger.toDouble(), // id: carLocation['driver_id'], // latitude: double.parse(carLocation['latitude']), // longitude: double.parse(carLocation['longitude']), // ); // // isloading = false; // update(); // } // } // // Handle the distance and duration as needed // else { // // 'Failed to retrieve distance and duration: ${response['status']}'); // Log.print('${response['status']}: ${response['status']}}'); // // Handle the failure case // } // } // } // } // } // } // calculateDistanceBetweenPassengerAndDriverBeforeCancelRide() async { // await getDriverCarsLocationToPassengerAfterApplied(); // double distance = Geolocator.distanceBetween( // passengerLocation.latitude, // passengerLocation.longitude, // driverCarsLocationToPassengerAfterApplied.last.latitude, // driverCarsLocationToPassengerAfterApplied.last.longitude, // ); // if (distance > 500) { // isCancelRidePageShown = true; // update(); // } else { // Get.defaultDialog( // barrierDismissible: false, // title: 'The Driver Will be in your location soon .'.tr, // middleText: 'The distance less than 500 meter.'.tr, // confirm: Column( // children: [ // MyElevatedButton( // kolor: AppColor.greenColor, // title: 'Ok'.tr, // onPressed: () { // Get.back(); // }, // ), // MyElevatedButton( // kolor: AppColor.redColor, // title: 'No, I want to cancel this trip'.tr, // onPressed: () { // Get.back(); // MyDialog().getDialog( // 'Attention'.tr, // 'You will be charged for the cost of the driver coming to your location.' // .tr, // () async { // Get.back(); // Get.find() // .payToDriverForCancelAfterAppliedAndHeNearYou(rideId); // // isCancelRidePageShown = true; // // update(); // }, // ); // }, // ), // ], // ), // ); // // cancel: MyElevatedButton( // // title: 'No.Iwant Cancel Trip.'.tr, onPressed: () {})); // } // } // List headingAngles = []; // double calculateAngleBetweenLocations(LatLng start, LatLng end) { // double startLat = start.latitude * math.pi / 180; // double startLon = start.longitude * math.pi / 180; // double endLat = end.latitude * math.pi / 180; // double endLon = end.longitude * math.pi / 180; // double dLon = endLon - startLon; // double y = math.sin(dLon) * cos(endLat); // double x = cos(startLat) * math.sin(endLat) - // math.sin(startLat) * cos(endLat) * cos(dLon); // double angle = math.atan2(y, x); // double angleDegrees = angle * 180 / math.pi; // return angleDegrees; // } // late LatLngBounds boundsData; // late String startNameAddress = ''; // late String endNameAddress = ''; // List> stopPoints = []; // void removeStop(Map stop) { // stopPoints.remove(stop); // update(); // Trigger a rebuild of the UI // } // Future getReverseGeocoding(LatLng location) async { // final lat = location.latitude; // final lng = location.longitude; // final url = '${AppLink.reverseGeocoding}?lat=$lat&lng=$lng'; // try { // final response = await CRUD().getMapSaas(link: url); // if (response != null && response is List && response.isNotEmpty) { // final data = response[0]; // String name = data['name_ar'] ?? data['name'] ?? 'Unknown Location'.tr; // return name; // } // return 'Unknown Location'.tr; // } catch (e) { // Log.print('ReverseGeocoding Exception: $e'); // return 'Unknown Location'.tr; // } // } // bool isDrawingRoute = false; // void showDrawingBottomSheet() { // Log.print( // '🔔 showDrawingBottomSheet called. isDrawingRoute: $isDrawingRoute'); // final context = Get.context; // if (context == null) return; // WidgetsBinding.instance.addPostFrameCallback((_) { // // Close any existing open dialogs first // if (Get.isDialogOpen == true) { // Get.back(); // } // Get.dialog( // Dialog( // backgroundColor: Colors.transparent, // elevation: 0, // child: Container( // padding: const EdgeInsets.all(24), // width: 180, // decoration: BoxDecoration( // color: Colors.white.withOpacity(0.95), // borderRadius: BorderRadius.circular(24), // boxShadow: [ // BoxShadow( // color: Colors.black.withOpacity(0.15), // blurRadius: 20, // spreadRadius: 5, // ) // ], // ), // child: Column( // mainAxisSize: MainAxisSize.min, // mainAxisAlignment: MainAxisAlignment.center, // crossAxisAlignment: CrossAxisAlignment.center, // children: [ // // App Logo // Image.asset( // 'assets/images/logo.gif', // height: 64, // errorBuilder: (context, error, stackTrace) => const Icon( // Icons.map, // size: 64, // color: AppColor.primaryColor, // ), // ), // const SizedBox(height: 16), // const SizedBox( // width: 24, // height: 24, // child: MyCircularProgressIndicator(), // ), // const SizedBox(height: 16), // Text( // 'Drawing route on map...'.tr, // style: const TextStyle( // fontWeight: FontWeight.bold, // fontSize: 14, // color: AppColor.primaryColor, // ), // textAlign: TextAlign.center, // ), // ], // ), // ), // ), // barrierDismissible: false, // ); // // Auto-dismiss after exactly 2 seconds // Future.delayed(const Duration(seconds: 2), () { // if (Get.isDialogOpen == true) { // Get.back(); // } // }); // }); // } // String dynamicApiUrl = 'https://routec.intaleq.xyz/route'; // Future getDistanceFromDriverAfterAcceptedRide( // String origin, String destination) async { // String apiKey = Env.mapKeyOsm; // مفتاح API الخاص بك // if (origin.isEmpty) { // origin = '${passengerLocation.latitude},${passengerLocation.longitude}'; // } // // 2. بناء الرابط (URI) // // Waypoints غير مدعومة حالياً في OSRM، لذلك تم تجاهلها // var uri = Uri.parse( // '$dynamicApiUrl?origin=$origin&destination=$destination&steps=false&overview=false'); // Log.print('uri: $uri'); // // 3. إرسال الطلب مع الهيدر // http.Response response; // Map responseData; // try { // response = await http.get( // uri, // headers: { // 'X-API-KEY': apiKey, // }, // ).timeout(const Duration(seconds: 20)); // تايم آوت 20 ثانية // if (response.statusCode != 200) { // Log.print('Error from API: ${response.statusCode}'); // isLoading = false; // update(); // return; // خروج في حالة الخطأ // } // if (Get.isBottomSheetOpen ?? false) { // Get.back(); // لإغلاق شاشة "جاري الرسم" // } // isDrawingRoute = false; // Reset state // responseData = json.decode(response.body); // Log.print('responseData: $responseData'); // if (responseData['status'] != 'ok') { // Log.print('API returned an error: ${responseData['message']}'); // isLoading = false; // update(); // return; // خروج في حالة خطأ منطقي (مثل "no path") // } // } catch (e) { // Log.print('Failed to get directions: $e'); // isLoading = false; // update(); // return; // خروج عند فشل الاتصال // } // } // // (b = 1.5348) هو المعامل الذي تم حسابه من مقارنة 60 رحلة بين Google و OSRM // double kDurationScalar = // 1.5348; //this from colab 60 random locations from google and routec // // ----------------------------------------------------------------------------------------- // // GET DIRECTION MAP (FULL) // // ----------------------------------------------------------------------------------------- // // ----------------------------------------------------------------------------------------- // // GET DIRECTION MAP (With Auto-Retry Logic) // // ----------------------------------------------------------------------------------------- // // أضفنا attemptCount لتتبع عدد المحاولات // // ----------------------------------------------------------------------------------------- // // GET DIRECTION MAP (Retry or Fail Strict Logic) // // ----------------------------------------------------------------------------------------- // Future getDirectionMap(String origin, String destination, // [List waypoints = const [], int attemptCount = 0]) async { // // 1. إظهار التحميل فقط في المحاولة الأولى // if (attemptCount == 0) { // // NOTE: Do NOT set isLoading = true here! // // isLoading destroys the MapLibreMap widget entirely (replaced by spinner), // // which means markers/polylines cannot be added to the new map instance // // until its style finishes loading asynchronously — causing a race condition. // // The showDrawingBottomSheet() overlay provides sufficient user feedback. // isDrawingRoute = true; // update(); // if (isDrawingRoute) showDrawingBottomSheet(); // await getCarsLocationByPassengerAndReloadMarker(); // } // // تجهيز الإحداثيات // if (origin.isEmpty) { // origin = '${passengerLocation.latitude},${passengerLocation.longitude}'; // } // var coordDestination = destination.split(','); // double latDest = double.parse(coordDestination[0]); // double lngDest = double.parse(coordDestination[1]); // myDestination = LatLng(latDest, lngDest); // // ── 2. Unified SaaS Routing Strategy ────────────────────────── // final bool isSaaSRequest = true; // Uri uri; // var originCoords = origin.split(','); // final Map queryParams = { // 'fromLat': originCoords[0].trim(), // 'fromLng': originCoords[1].trim(), // 'toLat': latDest.toString(), // 'toLng': lngDest.toString(), // }; // // Add multi-stop waypoints to the query parameters // for (int i = 0; i < activeMenuWaypointCount; i++) { // final wp = menuWaypoints[i]; // if (wp != null) { // queryParams['stop${i + 1}Lat'] = wp.latitude.toString(); // queryParams['stop${i + 1}Lng'] = wp.longitude.toString(); // } // } // uri = Uri.parse(AppLink.mapSaasRoute).replace(queryParameters: queryParams); // Log.print( // 'Requesting Route URI (${isSaaSRequest ? "SaaS" : "OSRM"}, Attempt: ${attemptCount + 1}): $uri'); // http.Response response; // Map responseData; // try { // response = await http.get(uri, headers: { // 'x-api-key': Env.mapSaasKey, // }).timeout(const Duration(seconds: 20)); // responseData = json.decode(response.body); // // Validation: SaaS returns 200 with data, OSRM returns code: 'Ok' // bool isRequestValid = response.statusCode == 200 && // (isSaaSRequest || responseData['code'] == 'Ok'); // if (!isRequestValid) { // if (attemptCount < 2) { // await _retryProcess(origin, destination, waypoints, attemptCount); // return; // } // _handleFatalError( // "Server Error".tr, "Connection failed. Please try again.".tr); // return; // } // // ============================================================ // // 🛑 الفحص الأمني (Sanity Check) // // ============================================================ // double apiDistanceMeters; // String pointsString; // dynamic routeData; // // SaaS parsing // apiDistanceMeters = (responseData['distance'] as num).toDouble(); // pointsString = responseData['points'] ?? ""; // routeData = responseData; // For box storage // var originCoords = origin.split(','); // double startLat = double.parse(originCoords[0]); // double startLng = double.parse(originCoords[1]); // // المسافة الجوية // double aerialDistance = // Geolocator.distanceBetween(startLat, startLng, latDest, lngDest); // if (apiDistanceMeters < 50.0 && aerialDistance > 200.0) { // Log.print( // "⚠️ Suspicious Route detected! Server: $apiDistanceMeters m | Aerial: $aerialDistance m"); // if (attemptCount < 2) { // Log.print("🔄 Retrying request (Attempt ${attemptCount + 2})..."); // await Future.delayed(const Duration(seconds: 1)); // await getDirectionMap( // origin, destination, waypoints, attemptCount + 1); // return; // } else { // Log.print("❌ All retries failed. Calculating Route is impossible."); // _handleFatalError( // "Route Not Found".tr, // "We couldn't find a valid route to this destination. Please try selecting a different point." // .tr); // return; // } // } // // 3. معالجة البيانات // box.remove(BoxName.tripData); // box.write(BoxName.tripData, routeData); // durationToRide = // ((routeData['duration'] as num) * kDurationScalar).toInt(); // double distanceOfTrip = apiDistanceMeters / 1000.0; // distance = distanceOfTrip; // data = routeData['legs'] != null && routeData['legs'].isNotEmpty // ? (routeData['legs'][0]['steps'] ?? []) // : []; // List decodedPoints = []; // if (pointsString.isNotEmpty) { // decodedPoints = await compute(decodePolylineIsolate, pointsString); // } // if (decodedPoints.isEmpty) { // _handleFatalError("Map Error".tr, "Received empty route data.".tr); // return; // } // polylineCoordinates.clear(); // polylineCoordinates.addAll(decodedPoints); // final LatLng startLoc = polylineCoordinates.first; // final LatLng endLoc = polylineCoordinates.last; // // ── 4. العناوين والتحديثات ────────────────────────────────── // startNameAddress = responseData['startName'] ?? 'Start Point'.tr; // endNameAddress = responseData['endName'] ?? 'Destination'.tr; // Log.print('📍 ROUTE START: $startNameAddress'); // Log.print('📍 ROUTE END: $endNameAddress'); // // ── 5. Bounds Calculation (SaaS bbox vs OSRM manual) ────────── // if (isSaaSRequest && responseData['bbox'] != null) { // List bbox = responseData['bbox']; // if (bbox.length == 4) { // // SaaS format: [minLng, minLat, maxLng, maxLat] // lastComputedBounds = LatLngBounds( // southwest: LatLng(bbox[1], bbox[0]), // northeast: LatLng(bbox[3], bbox[2]), // ); // } // } else { // double? minLat, maxLat, minLng, maxLng; // for (LatLng point in polylineCoordinates) { // minLat = // minLat == null ? point.latitude : min(minLat, point.latitude); // maxLat = // maxLat == null ? point.latitude : max(maxLat, point.latitude); // minLng = // minLng == null ? point.longitude : min(minLng, point.longitude); // maxLng = // maxLng == null ? point.longitude : max(maxLng, point.longitude); // } // if (minLat != null) { // lastComputedBounds = LatLngBounds( // northeast: LatLng(maxLat!, maxLng!), // southwest: LatLng(minLat!, minLng!)); // } // } // // isDrawingRoute = false; // // 5b. Reset state when finished // if (isDrawingRoute) { // Log.print('🔔 Finalizing route drawing state'); // isDrawingRoute = false; // isLoading = false; // update(); // } // // 6. إضافة الماركرز // durationToAdd = Duration(seconds: durationToRide); // hours = durationToAdd.inHours; // minutes = (durationToAdd.inMinutes % 60).round(); // markers = { // Marker( // markerId: const MarkerId('start'), // position: startLoc, // icon: InlqBitmap.fromStyleImage('orange_marker'), // infoWindow: const InfoWindow(title: 'A'), // anchor: const Offset(0.5, 1.0), // ), // Marker( // markerId: const MarkerId('end'), // position: endLoc, // icon: InlqBitmap.fromStyleImage('violet_marker'), // infoWindow: const InfoWindow(title: 'B'), // anchor: const Offset(0.5, 1.0), // ), // }; // for (int i = 0; i < activeMenuWaypointCount; i++) { // final wp = menuWaypoints[i]; // if (wp != null) { // final bool isFirstWaypoint = i == 0; // markers.add(Marker( // markerId: MarkerId('waypoint_$i'), // position: wp, // icon: InlqBitmap.fromStyleImage( // isFirstWaypoint ? 'orange_marker' : 'violet_marker'), // infoWindow: // InfoWindow(title: isFirstWaypoint ? 'Stop 1' : 'Stop 2'), // anchor: const Offset(0.5, 1.0), // )); // } // } // // 7. رسم الخط // if (polyLines.isNotEmpty) clearPolyline(); // rideConfirm = false; // isMarkersShown = true; // update(); // تحديث أولي لإظهار الخريطة والماركرز // // إظهار الباتم شيت للسعر // await bottomSheet(); // // تشغيل الأنيميشن الخفيف لومضات المسار + fit camera after // await _playRouteAnimation(polylineCoordinates, lastComputedBounds); // } catch (e, stackTrace) { // // 🚨 Cleanup on error to prevent UI freeze // if (isDrawingRoute) { // isDrawingRoute = false; // isLoading = false; // update(); // } // Log.print('🚨 CRITICAL ERROR IN getDirectionMap: $e'); // Log.print('🚨 STACKTRACE: $stackTrace'); // if (attemptCount < 2) { // await _retryProcess(origin, destination, waypoints, attemptCount); // } else { // _handleFatalError("Connection Error".tr, // "Please check your internet and try again.".tr); // } // } // } // // --- رسم المسار النهائي مع تقسيم ملون حسب نقاط التوقف --- // Future _playRouteAnimation( // List coords, LatLngBounds? bounds) async { // // Segment colors matching UI dots: green → amber → purple → red // const List segmentColors = [ // Color(0xFF109642), // Green (start → stop 1) // Color(0xFFF59E0B), // Amber (stop 1 → stop 2) // Color(0xFF7C3AED), // Purple (last segment → dest) // Color(0xFFEF4444), // Red (fallback) // ]; // // ── Build final polyline segments ─────────────────────────────────── // // Build all segments in a temporary Set first, then assign once // Set newPolylines = {}; // if (activeMenuWaypointCount > 0) { // List splitIndices = []; // for (int w = 0; w < activeMenuWaypointCount; w++) { // final wp = menuWaypoints[w]; // if (wp == null) continue; // int bestIdx = 0; // double bestDist = double.infinity; // for (int j = 0; j < coords.length; j++) { // final dx = coords[j].latitude - wp.latitude; // final dy = coords[j].longitude - wp.longitude; // final d = dx * dx + dy * dy; // if (d < bestDist) { // bestDist = d; // bestIdx = j; // } // } // splitIndices.add(bestIdx); // } // splitIndices.sort(); // List boundaries = [0, ...splitIndices, coords.length - 1]; // for (int s = 0; s < boundaries.length - 1; s++) { // int from = boundaries[s]; // int to = boundaries[s + 1] + 1; // if (to > coords.length) to = coords.length; // if (from >= to - 1) continue; // final segCoords = coords.sublist(from, to); // if (segCoords.length < 2) continue; // final color = segmentColors[s % segmentColors.length]; // newPolylines.add(Polyline( // polylineId: PolylineId('segment_$s'), // points: segCoords, // color: color, // width: 6, // )); // } // } else { // newPolylines.add(Polyline( // polylineId: const PolylineId('route_primary'), // points: coords, // color: AppColor.primaryColor, // width: 6, // )); // } // polyLines = newPolylines; // update(); // Log.print( // '🗺️ Drawing ${markers.length} markers + ${polyLines.length} polylines on map'); // update(); // // ── Fit camera to full route bounds ──────────────────────────────── // if (bounds != null) { // await _safeAnimateCameraBounds(bounds); // } // } // // --- دالة المساعدة لإعادة المحاولة --- // Future _retryProcess(String origin, String dest, List waypoints, // int currentAttempt) async { // Log.print( // "🔄 Exception or Error caught. Retrying in 1s... (Attempt ${currentAttempt + 1})"); // await Future.delayed(const Duration(seconds: 1)); // getDirectionMap(origin, dest, waypoints, currentAttempt + 1); // } // // --- دالة جديدة لتنظيف الخريطة بالكامل ومنع تداخل الرحلات --- // void resetAllMapStates() { // Log.print('🧹 Resetting all map states to prevent sticky location bug'); // clearPlacesDestination(); // clearPlacesStart(); // clearPolyline(); // data = []; // passengerStartLocationFromMap = false; // startLocationFromMap = false; // isPickerShown = false; // workLocationFromMap = false; // homeLocationFromMap = false; // isAnotherOreder = false; // isWhatsAppOrder = false; // // ✅ أضف هذا: reset الوجهة لموقع الراكب حتى لا تبقى قيمة الرحلة القديمة // myDestination = passengerLocation; // hintTextDestinationPoint = 'Select your destination'.tr; // placeDestinationController.clear(); // placeStartController.clear(); // rideConfirm = false; // shouldFetch = false; // isDrawingRoute = false; // isLoading = false; // update(); // } // // ----------------------------------------------------------------------------------------- // // 🛑 دالة الخطأ القاتل (تغلق كل شيء وتعيد المستخدم للخريطة) // // ----------------------------------------------------------------------------------------- // void _handleFatalError(String title, String message) { // // 1. إغلاق شاشة التحميل (Drawing route...) // if (Get.isBottomSheetOpen == true || Get.isDialogOpen == true) { // Get.back(); // } // if (Get.isSnackbarOpen) Get.closeCurrentSnackbar(); // // 2. تصفير المتغيرات // isDrawingRoute = false; // isLoading = false; // update(); // // 3. إظهار الديالوج الإجباري // Get.defaultDialog( // title: title, // titleStyle: AppStyle.title.copyWith(color: AppColor.redColor), // middleText: message, // middleTextStyle: AppStyle.subtitle, // barrierDismissible: false, // لا يمكن إغلاقه بالضغط خارجاً // confirm: MyElevatedButton( // title: "Close".tr, // kolor: AppColor.redColor, // onPressed: () { // Get.back(); // إغلاق الديالوج // // 4. إعادة تحميل الصفحة بالكامل (تنظيف الحالة) // // تأكد من استيراد MapPagePassenger // Get.offAll(() => const MapPagePassenger()); // }, // ), // ); // } // // Legacy gradient and layered animations removed for MapLibre migration // String shortenAddress(String fullAddress) { // // Split the address into parts // List parts = fullAddress.split('،'); // // Remove any leading or trailing whitespace from each part // parts = parts.map((part) => part.trim()).toList(); // // Remove any empty parts // parts = parts.where((part) => part.isNotEmpty).toList(); // // Initialize the short address // String shortAddress = ''; // if (parts.isNotEmpty) { // // Add the first part (usually the most specific location) // shortAddress += parts[0]; // } // if (parts.length > 2) { // // Add the district or area name (usually the third part in Arabic format) // shortAddress += '، ${parts[2]}'; // } else if (parts.length > 1) { // // Add the second part for English or shorter addresses // shortAddress += '، ${parts[1]}'; // } // // Add the country (usually the last part) // if (parts.length > 1) { // shortAddress += '، ${parts.last}'; // } // // Remove any part that's just numbers (like postal codes) // shortAddress = shortAddress // .split('،') // .where((part) => !RegExp(r'^[0-9 ]+$').hasMatch(part.trim())) // .join('،'); // // Check if the address is in English // bool isEnglish = // RegExp(r'^[a-zA-Z0-9 ]+$').hasMatch(shortAddress.replaceAll('،', '')); // if (isEnglish) { // // Further processing for English addresses // List englishParts = shortAddress.split('،'); // if (englishParts.length > 2) { // shortAddress = // '${englishParts[0]}، ${englishParts[1]}، ${englishParts.last}'; // } else if (englishParts.length > 1) { // shortAddress = '${englishParts[0]}، ${englishParts.last}'; // } // } // return shortAddress; // } // double distanceOfDestination = 0; // bool haveSteps = false; // late LatLng latestPosition; // getMapPoints(String originSteps, String destinationSteps, int index) async { // isWayPointStopsSheetUtilGetMap = false; // // haveSteps = true; // // startCarLocationSearch(box.read(BoxName.carType)); // await getCarsLocationByPassengerAndReloadMarker(); // // await getCarsLocationByPassengerAndReloadMarker(); // // isLoading = true; // update(); // var url = // ('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destinationSteps&origin=$originSteps&key=${AK.mapAPIKEY}'); // var response = await CRUD().getGoogleApi(link: url, payload: {}); // data = response['routes'][0]['legs']; // // isLoading = false; // int durationToRide0 = data[0]['duration']['value']; // durationToRide = durationToRide + durationToRide0; // distance = distanceOfDestination + (data[0]['distance']['value']) / 1000; // update(); // // final points = // // decodePolyline(response["routes"][0]["overview_polyline"]["points"]); // final String pointsString = // response['routes'][0]["overview_polyline"]["points"]; // List decodedPoints = // await compute(decodePolylineIsolate, pointsString); // // decodePolyline(response["routes"][0]["overview_polyline"]["points"]); // for (int i = 0; i < decodedPoints.length; i++) { // polylineCoordinates.add(decodedPoints[i]); // } // // Define the northeast and southwest coordinates // if (polyLines.isEmpty) { // var polyline = Polyline( // polylineId: PolylineId('route_$index'), // points: polylineCoordinatesPointsAll[index], // width: 6, // color: const Color(0xFF2196F3), // ); // polyLines = {...polyLines, polyline}; // rideConfirm = false; // update(); // } // } // void updateCameraForDistanceAfterGetMap() { // LatLng coord1 = LatLng( // double.parse(coordinatesWithoutEmpty.first.split(',')[0]), // double.parse(coordinatesWithoutEmpty.first.split(',')[1])); // LatLng coord2 = LatLng( // double.parse(coordinatesWithoutEmpty.last.split(',')[0]), // double.parse(coordinatesWithoutEmpty.last.split(',')[1])); // LatLng northeast; // LatLng southwest; // if (coord1.latitude > coord2.latitude) { // northeast = coord1; // southwest = coord2; // } else { // northeast = coord2; // southwest = coord1; // } // // Create the LatLngBounds object // LatLngBounds bounds = // LatLngBounds(northeast: northeast, southwest: southwest); // // Fit the camera to the bounds // var cameraUpdate = CameraUpdate.newLatLngBounds(bounds, // left: 180, top: 180, right: 180, bottom: 180); // mapController!.animateCamera(cameraUpdate); // update(); // } // int selectedIndex = -1; // Initialize with no selection // void selectCarFromList(int index) { // selectedIndex = index; // Update selected index // carTypes.forEach( // (element) => element.isSelected = false); // Reset selection flags // carTypes[index].isSelected = true; // update(); // } // showBottomSheet1() async { // await bottomSheet(); // isBottomSheetShown = true; // heightBottomSheetShown = 250; // update(); // } // final promo = TextEditingController(); // bool promoTaken = false; // void applyPromoCodeToPassenger(BuildContext context) async { // if (promoTaken == true) { // MyDialog().getDialog( // 'Promo Already Used'.tr, // 'You have already used this promo code.'.tr, // () => Get.back(), // ); // return; // } // if (!promoFormKey.currentState!.validate()) return; // // العتبات بالليرة السورية // const double minPromoLowSYP = 172; // Speed / Balash // const double minPromoHighSYP = 200; // Comfort / Electric / Lady // try { // final value = await CRUD().get( // link: AppLink.getPassengersPromo, // payload: {'promo_code': promo.text}, // ); // if (value == 'failure') { // MyDialog().getDialog( // 'Promo Ended'.tr, // 'The promotion period has ended.'.tr, // () => Get.back(), // ); // return; // } // // هل يوجد فئة مؤهلة أصلاً قبل الخصم؟ // final bool eligibleNow = (totalPassengerSpeed >= minPromoLowSYP) || // (totalPassengerBalash >= minPromoLowSYP) || // (totalPassengerComfort >= minPromoHighSYP) || // (totalPassengerElectric >= minPromoHighSYP) || // (totalPassengerLady >= minPromoHighSYP); // if (!eligibleNow) { // Get.snackbar( // 'Lowest Price Achieved'.tr, // 'Cannot apply further discounts.'.tr, // backgroundColor: AppColor.yellowColor, // ); // return; // } // final decode = jsonDecode(value); // if (decode["status"] != "success") { // MyDialog().getDialog( // 'Promo Ended'.tr, // 'The promotion period has ended.'.tr, // () => Get.back(), // ); // return; // } // Get.snackbar('Promo Code Accepted'.tr, '', // backgroundColor: AppColor.greenColor); // final firstElement = decode["message"][0]; // final int discountPercentage = // int.tryParse(firstElement['amount'].toString()) ?? 0; // // قيمة المحفظة - قد تكون سالبة // final double walletVal = double.tryParse( // box.read(BoxName.passengerWalletTotal)?.toString() ?? '0') ?? // 0.0; // final bool isWalletNegative = walletVal < 0; // // -------------------------- // // دالة تُطبّق الخصم دون النزول تحت الحد الأدنى // // -------------------------- // double _applyDiscountPerTier({ // required double fare, // required double minThreshold, // required bool isWalletNegative, // }) { // if (fare < minThreshold) return fare; // غير مؤهل أصلاً // final double discount = fare * (discountPercentage / 100.0); // double result; // if (isWalletNegative) { // double neg = (-1) * walletVal; // walletVal < 0 => neg positive // result = fare + neg - discount; // } else { // result = fare - discount; // } // // لا نسمح بالنزول دون الحد الأدنى // if (result < minThreshold) { // result = minThreshold; // } // // ولا نسمح بمبلغ سالب // return result.clamp(0.0, double.infinity); // } // // Comfort // totalPassengerComfort = _applyDiscountPerTier( // fare: totalPassengerComfort, // minThreshold: minPromoHighSYP, // isWalletNegative: isWalletNegative, // ); // // Electric // totalPassengerElectric = _applyDiscountPerTier( // fare: totalPassengerElectric, // minThreshold: minPromoHighSYP, // isWalletNegative: isWalletNegative, // ); // // Lady // totalPassengerLady = _applyDiscountPerTier( // fare: totalPassengerLady, // minThreshold: minPromoHighSYP, // isWalletNegative: isWalletNegative, // ); // // Speed // totalPassengerSpeed = _applyDiscountPerTier( // fare: totalPassengerSpeed, // minThreshold: minPromoLowSYP, // isWalletNegative: isWalletNegative, // ); // // Balash // totalPassengerBalash = _applyDiscountPerTier( // fare: totalPassengerBalash, // minThreshold: minPromoLowSYP, // isWalletNegative: isWalletNegative, // ); // // تعديل دخل السائق وفق نسبة الخصم // totalDriver = totalDriver - (totalDriver * discountPercentage / 100.0); // promoTaken = true; // update(); // // مؤثرات // Confetti.launch( // context, // options: const ConfettiOptions(particleCount: 100, spread: 70, y: 0.6), // ); // Get.back(); // await Future.delayed(const Duration(milliseconds: 120)); // } catch (e) { // Get.snackbar('Error'.tr, e.toString(), // backgroundColor: AppColor.redColor); // } // } // double getDistanceFromText(String distanceText) { // // Remove any non-digit characters from the distance text // String distanceValue = distanceText.replaceAll(RegExp(r'[^0-9.]+'), ''); // // Parse the extracted numerical value as a double // double distance = double.parse(distanceValue); // return distance; // } // double costForDriver = 0; // double totalPassengerSpeed = 0; // double totalPassengerBalash = 0; // double totalPassengerElectric = 0; // double totalPassengerLady = 0; // double totalPassengerRayehGai = 0; // double totalPassengerRayehGaiComfort = 0; // double totalPassengerRayehGaiBalash = 0; // Future bottomSheet() async { // // if (data.isEmpty) return; // // === إعدادات عامة === // const double minFareSYP = 160; // حد أدنى // const double minBillableKm = 0.3; // حد أدنى للمسافة المفوترة // const double ladyFlatAddon = 20; // إضافة ثابتة لـ Lady // const double airportAddonSYP = 200; // إضافة المطار // // --- ⬇️ الإضافة الجديدة: إضافة حدود مطار دمشق ⬇️ --- // const double damascusAirportBoundAddon = 1400; // إضافة المطار (حدود) // // --- ⬆️ نهاية الإضافة ⬆️ --- // // كهرباء // const double electricPerKmUplift = 4; // زيادة/كم // const double electricFlatAddon = 10; // زيادة ثابتة // // Long Speed // const double longSpeedThresholdKm = 40.0; // const double longSpeedPerKm = 26.0; // Speed عند >40كم // // قواعد الرحلات البعيدة للدقائق (تعمل لكل الأوقات) // const double mediumDistThresholdKm = 25.0; // >25كم // const double longDistThresholdKm = 35.0; // >35كم // const double longTripPerMin = 6.0; // const int minuteCapMedium = 60; // سقف دقائق عند >25كم // const int minuteCapLong = 80; // سقف دقائق عند >35كم // const int freeMinutesLong = 10; // عفو 10 دقائق عند >35كم // // تخفيضات المسافات الكبيرة للفئات غير Speed // const double extraReduction100 = 0.07; // +7% فوق تخفيض >40كم للرحلات >100كم // const double maxReductionCap = 0.35; // سقف 35% كحد أقصى // // ====== زمن الرحلة ====== // durationToAdd = Duration(seconds: durationToRide); // hours = durationToAdd.inHours; // minutes = (durationToAdd.inMinutes % 60).round(); // final DateTime currentTime = DateTime.now(); // newTime = currentTime.add(durationToAdd); // averageDuration = (durationToRide / 60) / distance; // // +5 minutes per waypoint stop surcharge // final int waypointSurchargeMinutes = activeMenuWaypointCount * 5; // final int totalMinutes = // (durationToRide / 60).floor() + waypointSurchargeMinutes; // // ====== أدوات مساعدة ====== // bool _isAirport(String s) { // final t = s.toLowerCase(); // return t.contains('airport') || // s.contains('مطار') || // s.contains('المطار'); // } // bool _isClub(String s) { // final t = s.toLowerCase(); // return t.contains('club') || // t.contains('nightclub') || // t.contains('night club') || // s.contains('ديسكو') || // s.contains('ملهى ليلي'); // } // // --- ⬇️ الإضافة الجديدة: دالة التحقق من حدود المطار ⬇️ --- // // (P1: 33.415313, 36.499687) (P2: 33.400265, 36.531505) // bool _isInsideDamascusAirportBounds(double lat, double lng) { // final double northLat = 33.415313; // final double southLat = 33.400265; // final double eastLng = 36.531505; // final double westLng = 36.499687; // // التحقق من خط العرض (بين الشمال والجنوب) // bool isLatInside = (lat <= northLat) && (lat >= southLat); // // التحقق من خط الطول (بين الشرق والغرب) // bool isLngInside = (lng <= eastLng) && (lng >= westLng); // return isLatInside && isLngInside; // } // // --- ⬆️ نهاية الإضافة ⬆️ --- // // أسعار الدقيقة من السيرفر // final double naturePerMin = naturePrice; // طبيعي // final double latePerMin = latePrice; // ليل // final double heavyPerMin = heavyPrice; // ذروة // // سعر الدقيقة حسب الوقت (أساس قبل قواعد المسافة) // double _perMinuteByTime(DateTime now, bool clubCtx) { // final h = now.hour; // if (h >= 21 || h < 1) return latePerMin; // ليل // if (h >= 1 && h < 5) return clubCtx ? (latePerMin * 2) : latePerMin; // if (h >= 14 && h <= 17) return heavyPerMin; // ذروة // return naturePerMin; // طبيعي // } // // حد أدنى // double _applyMinFare(double fare) => // (fare < minFareSYP) ? minFareSYP : fare; // // عمولة الراكب (kazan من السيرفر) // double _withCommission(double base) => // (base * (1 + kazan / 100)).ceilToDouble(); // // ====== سياق ====== // final bool airportCtx = // _isAirport(startNameAddress) || _isAirport(endNameAddress); // final bool clubCtx = _isClub(startNameAddress) || _isClub(endNameAddress); // // --- ⬇️ الإضافة الجديدة: التحقق من سياق حدود المطار ⬇️ --- // // !! ⚠️ تأكد من أن هذه هي المتغيرات الصحيحة لإحداثيات نقطة النهاية !! // double destLat = 0.0; // double destLng = 0.0; // try { // destLat = myDestination.latitude; // destLng = myDestination.longitude; // } catch (_) { // if (coordinatesWithoutEmpty.isNotEmpty) { // destLat = // double.tryParse(coordinatesWithoutEmpty.last.split(',')[0]) ?? 0.0; // destLng = // double.tryParse(coordinatesWithoutEmpty.last.split(',')[1]) ?? 0.0; // } // } // final bool damascusAirportBoundCtx = _isInsideDamascusAirportBounds( // destLat, // destLng, // ); // final bool isInDamascusAirportBoundCtx = _isInsideDamascusAirportBounds( // newMyLocation.latitude.toDouble(), // <-- ⚠️ غيّر هذا للمتغير الصحيح // newMyLocation.longitude.toDouble(), // <-- ⚠️ غيّر هذا للمتغير الصحيح // ); // // --- ⬆️ نهاية الإضافة ⬆️ --- // // ====== مسافة مفوترة ====== // final double billableDistance = // (distance < minBillableKm) ? minBillableKm : distance; // // ====== Speed (قصير/طويل) ====== // final bool isLongSpeed = billableDistance > longSpeedThresholdKm; // final double perKmSpeedBaseFromServer = // speedPrice; // مثال: 2900 يأتي من السيرفر // final double perKmSpeed = // isLongSpeed ? longSpeedPerKm : perKmSpeedBaseFromServer; // // ====== تخفيضات الفئات الأخرى حسب بُعد الرحلة ====== // // ... (الكود كما هو) ... // double reductionPct40 = 0.0; // if (perKmSpeedBaseFromServer > 0) { // reductionPct40 = (1.0 - (longSpeedPerKm / perKmSpeedBaseFromServer)) // .clamp(0.0, maxReductionCap); // } // final double reductionPct100 = // (reductionPct40 + extraReduction100).clamp(0.0, maxReductionCap); // double distanceReduction = 0.0; // if (billableDistance > 100.0) { // distanceReduction = reductionPct100; // } else if (billableDistance > 40.0) { // distanceReduction = reductionPct40; // } // // ====== منطق الدقيقة يعمل لكل الأوقات ويتكيّف مع المسافة ====== // // ... (الكود كما هو) ... // double effectivePerMin = _perMinuteByTime(currentTime, clubCtx); // int billableMinutes = totalMinutes; // if (billableDistance > longDistThresholdKm) { // effectivePerMin = longTripPerMin; // final int capped = // (billableMinutes > minuteCapLong) ? minuteCapLong : billableMinutes; // billableMinutes = capped - freeMinutesLong; // if (billableMinutes < 0) billableMinutes = 0; // } else if (billableDistance > mediumDistThresholdKm) { // effectivePerMin = longTripPerMin; // billableMinutes = (billableMinutes > minuteCapMedium) // ? minuteCapMedium // : billableMinutes; // } // // ====== أسعار/كم قبل التخفيض ====== // // ... (الكود كما هو) ... // final double perKmComfortRaw = comfortPrice; // final double perKmDelivery = deliveryPrice; // final double perKmVanRaw = // (familyPrice > 0 ? familyPrice : (speedPrice + 13)); // final double perKmElectricRaw = perKmComfortRaw + electricPerKmUplift; // // ====== تطبيق التخفيضات على الفئات (نفس نسبة Speed للبعيد) ====== // // ... (الكود كما هو) ... // double perKmComfort = perKmComfortRaw * (1.0 - distanceReduction); // double perKmElectric = perKmElectricRaw * (1.0 - distanceReduction); // double perKmVan = perKmVanRaw * (1.0 - distanceReduction); // perKmComfort = perKmComfort.clamp(0, double.infinity); // perKmElectric = perKmElectric.clamp(0, double.infinity); // perKmVan = perKmVan.clamp(0, double.infinity); // final double perKmBalash = (perKmSpeed - 5).clamp(0, double.infinity); // // ====== دوال الاحتساب ====== // double _oneWayFare({ // required double perKm, // required bool isLady, // double flatAddon = 0, // }) { // double fare = billableDistance * perKm; // fare += // billableMinutes * effectivePerMin; // دقائق بعد السقف/العفو إن وُجد // fare += flatAddon; // if (isLady) fare += ladyFlatAddon; // if (airportCtx) fare += airportAddonSYP; // // --- ⬇️ الإضافة الجديدة: تطبيق إضافة حدود المطار ⬇️ --- // if (damascusAirportBoundCtx || isInDamascusAirportBoundCtx) { // fare += damascusAirportBoundAddon; // } // // --- ⬆️ نهاية الإضافة ⬆️ --- // return _applyMinFare(fare); // } // double _roundTripFare({required double perKm}) { // // خصم 40% لمسافة إياب واحدة + زمن مضاعف (بنفس قواعد الدقيقة المعدّلة) // double distPart = // (billableDistance * 2 * perKm) - ((billableDistance * perKm) * 0.4); // double timePart = (billableMinutes * 2) * effectivePerMin; // double fare = distPart + timePart; // if (airportCtx) fare += airportAddonSYP; // // --- ⬇️ الإضافة الجديدة: تطبيق إضافة حدود المطار ⬇️ --- // // تنطبق أيضاً على رحلات الذهاب والعودة لأنها "تصل" إلى الوجهة // if (damascusAirportBoundCtx || isInDamascusAirportBoundCtx) { // fare += damascusAirportBoundAddon; // } // // --- ⬆️ نهاية الإضافة ⬆️ --- // return _applyMinFare(fare); // } // // ====== حساب كل الفئات (Base قبل العمولة) ====== // final double costSpeed = _oneWayFare(perKm: perKmSpeed, isLady: false); // final double costBalash = _oneWayFare(perKm: perKmBalash, isLady: false); // final double costComfort = _oneWayFare(perKm: perKmComfort, isLady: false); // final double costElectric = _oneWayFare( // perKm: perKmElectric, isLady: false, flatAddon: electricFlatAddon); // final double costDelivery = // _oneWayFare(perKm: perKmDelivery, isLady: false); // final double costLady = _oneWayFare( // perKm: perKmComfort, // isLady: true); // Lady تعتمد Comfort بعد التخفيض + إضافة ثابتة // final double costVan = _oneWayFare(perKm: perKmVan, isLady: false); // final double costRayehGai = _roundTripFare(perKm: perKmSpeed); // final double costRayehGaiComfort = _roundTripFare(perKm: perKmComfort); // final double costRayehGaiBalash = _roundTripFare(perKm: perKmBalash); // // ====== أسعار الراكب بعد العمولة (kazan من السيرفر) ====== // totalPassengerSpeed = _withCommission(costSpeed); // totalPassengerBalash = _withCommission(costBalash); // totalPassengerComfort = _withCommission(costComfort); // totalPassengerElectric = _withCommission(costElectric); // totalPassengerLady = _withCommission(costLady); // totalPassengerScooter = _withCommission(costDelivery); // totalPassengerVan = _withCommission(costVan); // totalPassengerRayehGai = _withCommission(costRayehGai); // totalPassengerRayehGaiComfort = _withCommission(costRayehGaiComfort); // totalPassengerRayehGaiBalash = _withCommission(costRayehGaiBalash); // // افتراضي للعرض // totalPassenger = totalPassengerSpeed; // totalCostPassenger = totalPassenger; // // ====== دعم رصيد محفظة سلبي ====== // try { // final walletStr = box.read(BoxName.passengerWalletTotal).toString(); // final walletVal = double.tryParse(walletStr) ?? 0.0; // if (walletVal < 0) { // final neg = (-1) * walletVal; // totalPassenger += neg; // totalPassengerComfort += neg; // totalPassengerElectric += neg; // totalPassengerLady += neg; // totalPassengerBalash += neg; // totalPassengerScooter += neg; // totalPassengerRayehGai += neg; // totalPassengerRayehGaiComfort += neg; // totalPassengerRayehGaiBalash += neg; // totalPassengerVan += neg; // } // } catch (e) { // Log.print("Error: $e"); // } // update(); // changeBottomSheetShown(forceValue: true); // } // List polylineCoordinate = []; // String? cardNumber; // void readyWayPoints() { // hintTextwayPointStringAll = [ // hintTextwayPoint0, // hintTextwayPoint1, // hintTextwayPoint2, // hintTextwayPoint3, // hintTextwayPoint4, // ]; // polylineCoordinatesPointsAll = [ // polylineCoordinates0, // polylineCoordinates1, // polylineCoordinates2, // polylineCoordinates3, // polylineCoordinates4, // ]; // allTextEditingPlaces = [ // wayPoint0Controller, // wayPoint1Controller, // wayPoint2Controller, // wayPoint3Controller, // wayPoint4Controller, // ]; // currentLocationToFormPlacesAll = [ // currentLocationToFormPlaces0, // currentLocationToFormPlaces1, // currentLocationToFormPlaces2, // currentLocationToFormPlaces3, // currentLocationToFormPlaces4, // ]; // placeListResponseAll = [ // wayPoint0, // wayPoint1, // wayPoint2, // wayPoint3, // wayPoint4 // ]; // startLocationFromMapAll = [ // startLocationFromMap0, // startLocationFromMap1, // startLocationFromMap2, // startLocationFromMap3, // startLocationFromMap4, // ]; // currentLocationStringAll = [ // currentLocationString0, // currentLocationString1, // currentLocationString2, // currentLocationString3, // currentLocationString4, // ]; // placesCoordinate = [ // placesCoordinate0, // placesCoordinate1, // placesCoordinate2, // placesCoordinate3, // placesCoordinate4, // ]; // update(); // } // List driversForMishwari = []; // Future selectDriverAndCarForMishwariTrip() async { // // Calculate the bounds for 12km range // double latitudeOffset = 0.1; // 20km range in latitude // double longitudeOffset = 0.12; // 20km range in longitude // // Calculate bounding box based on passenger's location // double southwestLat = passengerLocation.latitude - latitudeOffset; // double northeastLat = passengerLocation.latitude + latitudeOffset; // double southwestLon = passengerLocation.longitude - longitudeOffset; // double northeastLon = passengerLocation.longitude + longitudeOffset; // // Create the payload with calculated bounds // var payload = { // 'southwestLat': southwestLat.toString(), // 'northeastLat': northeastLat.toString(), // 'southwestLon': southwestLon.toString(), // 'northeastLon': northeastLon.toString(), // }; // try { // // Fetch data from the API // var res = await CRUD().get( // link: AppLink.selectDriverAndCarForMishwariTrip, payload: payload); // if (res != 'failure') { // // Check if response is valid JSON // try { // var d = jsonDecode(res); // driversForMishwari = d['message']; // Log.print('driversForMishwari: $driversForMishwari'); // update(); // } catch (e) { // // Handle invalid JSON format // Log.print("Error decoding JSON: $e"); // return 'Server returned invalid data. Please try again later.'; // } // } else { // return 'No driver available now, try again later. Thanks for using our app.' // .tr; // } // } catch (e) { // // Handle network or other exceptions // Log.print("Error fetching data: $e"); // return 'There was an issue connecting to the server. Please try again later.' // .tr; // } // } // final Rx selectedDateTime = DateTime.now().obs; // void updateDateTime(DateTime newDateTime) { // selectedDateTime.value = newDateTime; // } // Future mishwariOption() async { // isLoading = true; // update(); // // add dialoug for select driver and car // await selectDriverAndCarForMishwariTrip(); // Future.delayed(Duration.zero); // isLoading = false; // update(); // Get.to(() => CupertinoDriverListWidget()); // // changeCashConfirmPageShown(); // } // var driverIdVip = ''; // Future saveTripData( // Map driver, DateTime tripDateTime) async { // try { // // Prepare trip data // Map tripData = { // 'id': driver['driver_id'].toString(), // Ensure the id is a string // 'phone': driver['phone'], // 'gender': driver['gender'], // 'name': driver['NAME'], // 'name_english': driver['name_english'], // 'address': driver['address'], // 'religion': driver['religion'] ?? 'UnKnown', // 'age': driver['age'].toString(), // Convert age to String // 'education': driver['education'] ?? 'UnKnown', //startlocationname // 'license_type': driver['license_type'] ?? 'UnKnown', // 'national_number': driver['national_number'] ?? 'UnKnown', // 'car_plate': driver['car_plate'], // 'make': driver['make'], // 'model': driver['model'], // 'year': driver['year'].toString(), // Convert year to String // 'color': driver['color'], // 'color_hex': driver['color_hex'], // 'displacement': driver['displacement'], // 'fuel': driver['fuel'], // 'token': driver['token'], // 'rating': driver['rating'].toString(), // Convert rating to String // 'countRide': // driver['ride_count'].toString(), // Convert countRide to String // 'passengerId': box.read(BoxName.passengerID), // 'timeSelected': tripDateTime.toIso8601String(), // 'status': 'pending', // 'startNameAddress': startNameAddress.toString(), // 'locationCoordinate': // '${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}', // }; // Log.print('tripData: $tripData'); // // Send data to server // var response = // await CRUD().post(link: AppLink.addMishwari, payload: tripData); // // Log.print('response: $response'); // if (response != 'failure') { // // Trip saved successfully // // Get.snackbar('Success'.tr, 'Trip booked successfully'.tr); // var id = response['message']['id'].toString(); // await CRUD() // .post(link: '${AppLink.server}/ride/rides/add.php', payload: { // "start_location": // '${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}', // "end_location": // '${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}', // "date": DateTime.now().toString(), // "time": DateTime.now().toString(), // "endtime": DateTime.now().add(const Duration(hours: 2)).toString(), // "price": '50', // "passenger_id": box.read(BoxName.passengerID).toString(), // "driver_id": driver['driver_id'].toString(), // "status": "waiting", // 'carType': 'vip', // "price_for_driver": '50', // "price_for_passenger": '50', // "distance": '20', // "paymentMethod": 'cash', // }).then((value) { // if (value is String) { // final parsedValue = jsonDecode(value); // rideId = parsedValue['message']; // } else if (value is Map) { // rideId = value['message']; // } else { // Log.print('Unexpected response type: ${value.runtimeType}'); // } // }); // driverIdVip = driver['driver_id'].toString(); // driverId = driver['driver_id'].toString(); // DateTime timeSelected = DateTime.parse(tripDateTime.toIso8601String()); // Get.find().scheduleNotificationsForTimeSelected( // "Your trip is scheduled".tr, // "Don't forget your ride!".tr, // "tone1", // timeSelected); // await NotificationService.sendNotification( // category: 'OrderVIP', // target: driver['token'].toString(), // title: 'OrderVIP'.tr, // body: '$rideId - VIP Trip', // isTopic: false, // Important: this is a token // tone: 'tone1', // driverList: [ // id, // rideId, // driver['id'], // passengerLocation.latitude.toString(), // startNameAddress.toString(), // passengerLocation.longitude.toString(), // (box.read(BoxName.name).toString().split(' ')[0]).toString(), // box.read(BoxName.passengerID).toString(), // box.read(BoxName.phone).toString(), // box.read(BoxName.email).toString(), // box.read(BoxName.passengerPhotoUrl).toString(), // box.read(BoxName.tokenFCM).toString(), // (driver['token'].toString()), // ], // ); // if (response['message'] == "Trip updated successfully") { // mySnackbarSuccess("Trip updated successfully".tr); // Log.print( // 'previous_driver_token: ${response['previous_driver_token']}'); // await NotificationService.sendNotification( // category: 'Order VIP Canceld', // target: response['previous_driver_token'].toString(), // title: 'Order VIP Canceld'.tr, // body: 'Passenger cancel order'.tr, // isTopic: false, // Important: this is a token // tone: 'cancel', // driverList: [], // ); // } // // data = []; // isBottomSheetShown = false; // update(); // Get.to(() => VipWaittingPage()); // } else { // throw Exception('Failed to save trip'); // } // } catch (e) { // // Show error message with more details for debugging // Get.snackbar('Error'.tr, 'Failed to book trip: $e'.tr, // backgroundColor: AppColor.redColor); // Log.print('Error: $e'); // } // } // Future cancelVip(String token, tripId) async { // var res = await CRUD() // .post(link: AppLink.cancelMishwari, payload: {'id': tripId}); // if (res != 'failur') { // Get.back(); // mySnackbarSuccess('You canceled VIP trip'.tr); // } // } // void sendToDriverAgain(String token) { // NotificationService.sendNotification( // category: 'Order VIP Canceld', // target: token.toString(), // title: 'Order VIP Canceld'.tr, // body: 'Passenger cancel order'.tr, // isTopic: false, // Important: this is a token // tone: 'cancel', // driverList: [], // ); // } // // دالة الفحص عند بدء التطبيق // Future detectAndCacheDeviceTier() async { // // 1. استخدام الكلاس الذي أنشأناه سابقاً للفحص // bool isHighEnd = await DevicePerformanceManager.isHighEndDevice(); // // 2. طباعة النتيجة للتأكد // Log.print("Device Analysis - Is Flagship/HighEnd? $isHighEnd"); // // 3. تخزين النتيجة بشكل منطقي صحيح // // إذا كان الجهاز قوياً (true)، فإن وضع الـ LowEnd يكون (false) // // والعكس صحيح // box.write(BoxName.lowEndMode, !isHighEnd); // } // initilizeGetStorage() async { // if (box.read(BoxName.addWork) == null) { // box.write(BoxName.addWork, 'addWork'); // } // if (box.read(BoxName.addHome) == null) { // box.write(BoxName.addHome, 'addHome'); // } // if (box.read(BoxName.lowEndMode) == null) { // detectAndCacheDeviceTier(); // } // } // late List recentPlaces = []; // getFavioratePlaces() async { // recentPlaces = await sql.getCustomQuery( // 'SELECT * FROM ${TableName.recentLocations} ORDER BY createdAt DESC'); // // Log.print('recentPlaces: ${recentPlaces}'); // } // double passengerRate = 5; // double comfortPrice = 45; // double speedPrice = 40; // double mashwariPrice = 40; // double familyPrice = 55; // double deliveryPrice = 1.2; // double minFareSYP = 16000; // حد أدنى للأجرة (سوريا) // double minBillableKm = 1.0; // حد أدنى للمسافة المفوترة // double commissionPct = 15; // عمولة التطبيق % (راكب) // getKazanPercent() async { // var res = await CRUD().get( // link: AppLink.getKazanPercent, // payload: {'country': box.read(BoxName.countryCode).toString()}, // ); // if (res != 'failure') { // var json = jsonDecode(res); // // التحقق الديناميكي من 'data' أو 'message' // var dataList = json['data'] ?? json['message']; // if (dataList != null && dataList is List && dataList.isNotEmpty) { // var firstRow = dataList[0]; // kazan = double.parse(firstRow['kazan'].toString()); // naturePrice = double.parse(firstRow['naturePrice'].toString()); // heavyPrice = double.parse(firstRow['heavyPrice'].toString()); // latePrice = double.parse(firstRow['latePrice'].toString()); // comfortPrice = double.parse(firstRow['comfortPrice'].toString()); // speedPrice = double.parse(firstRow['speedPrice'].toString()); // deliveryPrice = double.parse(firstRow['deliveryPrice'].toString()); // mashwariPrice = double.parse(firstRow['freePrice'].toString()); // familyPrice = double.parse(firstRow['familyPrice'].toString()); // fuelPrice = double.parse(firstRow['fuelPrice'].toString()); // } // } // } // getPassengerRate() async { // var res = await CRUD().get( // link: AppLink.getPassengerRate, // payload: {'passenger_id': box.read(BoxName.passengerID)}); // if (res != 'failure') { // var json = jsonDecode(res); // var message = json['data'] ?? json['message']; // if (message['rating'] == null) { // passengerRate = 5.0; // Default rating // } else { // // Safely parse the rating to double // var rating = message['rating']; // if (rating is String) { // passengerRate = // double.tryParse(rating) ?? 5.0; // Default if parsing fails // } else if (rating is num) { // passengerRate = // rating.toDouble(); // Already a number, convert to double // } else { // passengerRate = 5.0; // Default for unexpected data types // } // } // } else { // passengerRate = 5.0; // Default rating for failure // } // } // addFingerPrint() async { // String fingerPrint = await DeviceHelper.getDeviceFingerprint(); // await CRUD().postWallet(link: AppLink.addFingerPrint, payload: { // 'token': (box.read(BoxName.tokenFCM.toString())), // 'passengerID': box.read(BoxName.passengerID).toString(), // "fingerPrint": fingerPrint // }); // } // firstTimeRunToGetCoupon() async { // // Check if it's the first time and the app is installed and gift token is available // if (box.read(BoxName.isFirstTime).toString() == '0' && // box.read(BoxName.isInstall).toString() == '1' && // box.read(BoxName.isGiftToken).toString() == '0') { // var promo, discount, validity; // var resPromo = await CRUD().get(link: AppLink.getPromoFirst, payload: { // "passengerID": box.read(BoxName.passengerID).toString(), // }); // if (resPromo != 'failure') { // var d1 = jsonDecode(resPromo); // promo = d1['message']['promo_code']; // discount = d1['message']['amount']; // validity = d1['message']['validity_end_date']; // } // box.write(BoxName.isFirstTime, '1'); // // Show a full-screen modal styled as an ad // Get.dialog( // AlertDialog( // contentPadding: // EdgeInsets.zero, // Removes the padding around the content // content: SizedBox( // width: 300, // Match the width of PromoBanner // // height: 250, // Match the height of PromoBanner // child: PromoBanner( // promoCode: promo, // discountPercentage: discount, // validity: validity, // ), // ), // ), // ); // } // } // // --- دالة جديدة للاستماع ومعالجة الرابط --- // void _listenForDeepLink() { // ever(_deepLinkController.rawDeepLink, (String? link) async { // if (link != null && link.isNotEmpty) { // Log.print('📍 MapPassengerController processing link: $link'); // // 1. استخراج الإحداثيات باستخدام الدالة الموجودة لديك مسبقاً // Map? coordinates = // await extractCoordinatesFromLinkAsync(link); // if (coordinates != null) { // double destLat = coordinates['latitude']!; // double destLng = coordinates['longitude']!; // myDestination = LatLng(destLat, destLng); // // 2. التحقق من موقع الراكب الحالي // if (passengerLocation == null || // (passengerLocation.latitude == 0 && // passengerLocation.longitude == 0)) { // Log.print('⏳ Waiting for current location to calculate route...'); // await getLocation(); // جلب موقع الراكب إذا لم يكن متاحاً // } // if (passengerLocation != null) { // String originStr = // '${passengerLocation.latitude},${passengerLocation.longitude}'; // String destStr = '$destLat,$destLng'; // Log.print( // '🚀 Drawing route from Deep Link: $originStr to $destStr'); // // 3. مسح أي مسارات ونقاط توقف سابقة // clearPolyline(); // waypoints.clear(); // clearAllMenuWaypoints(); // // 4. استدعاء دالة رسم المسار وحساب التكلفة التي برمجتها // await getDirectionMap(originStr, destStr); // // 5. إظهار الواجهة السفلية للرحلة ليكون الطلب جاهزاً بنقرة واحدة // isBottomSheetShown = true; // heightBottomSheetShown = 250; // update(); // Get.snackbar( // 'Location Received'.tr, // 'Route and prices have been calculated successfully!'.tr, // backgroundColor: AppColor.greenColor, // colorText: Colors.white, // ); // } // } else { // Log.print('⚠️ Could not extract valid coordinates from link: $link'); // } // // تفريغ الرابط بعد معالجته حتى لا يتم استدعاؤه مرة أخرى بالخطأ // _deepLinkController.rawDeepLink.value = null; // } // }); // // معالجة الرابط إذا كان موجوداً مسبقاً (Cold Start) قبل تفعيل المستمع // if (_deepLinkController.rawDeepLink.value != null && // _deepLinkController.rawDeepLink.value!.isNotEmpty) { // String link = _deepLinkController.rawDeepLink.value!; // _deepLinkController.rawDeepLink.value = null; // // نؤجل التنفيذ قليلاً لضمان تحميل الخريطة // Future.delayed(const Duration(milliseconds: 500), () async { // Log.print( // '📍 MapPassengerController processing link (Cold Start): $link'); // Map? coordinates = // await extractCoordinatesFromLinkAsync(link); // if (coordinates != null) { // double destLat = coordinates['latitude']!; // double destLng = coordinates['longitude']!; // myDestination = LatLng(destLat, destLng); // if (passengerLocation == null || // (passengerLocation.latitude == 0 && // passengerLocation.longitude == 0)) { // await getLocation(); // } // if (passengerLocation != null) { // String originStr = // '${passengerLocation.latitude},${passengerLocation.longitude}'; // String destStr = '$destLat,$destLng'; // clearPolyline(); // waypoints.clear(); // clearAllMenuWaypoints(); // await getDirectionMap(originStr, destStr); // isBottomSheetShown = true; // heightBottomSheetShown = 250; // update(); // } // } // }); // } // } // @override // void onInit() async { // super.onInit(); // _checkAndRefreshMapStyle(); // Verify style version and clear cache if needed // // // --- إضافة جديدة: تهيئة وحدة التحكم في الروابط العميقة --- // Get.put(DeepLinkController(), permanent: true); // // // ---------------------------------------------------- // // مرحلة 0: الضروري جداً لعرض الخريطة سريعاً // // mapAPIKEY = await storage.read(key: BoxName.mapAPIKEY); // await initilizeGetStorage(); // إعداد سريع // await _initMinimalIcons(); // start/end فقط // // await addToken(); // لو لازم للمصادقة // _listenForDeepLink(); // // initSocket(); // await getLocation(); // لتحديد الكاميرا // box.write(BoxName.carType, 'yet'); // box.write(BoxName.tipPercentage, '0'); // // await detectAndCacheDeviceTier(); // // لا تُنشئ Controllers الثقيلة الآن: // Get.lazyPut(() => TextToSpeechController(), // fenix: true); // Get.lazyPut(() => FirebaseMessagesController(), // fenix: true); // Get.lazyPut(() => AudioRecorderController(), // fenix: true); // // ابدأ الخريطة الآن (الشاشة ظهرت للمستخدم) // Future.delayed(const Duration(seconds: 4), () { // if (isLoading) { // isLoading = false; // update(); // } // }); // // مرحلة 1: مهام ضرورية للتسعير لكن غير حرجة لظهور UI // unawaited(_stagePricingAndState()); // // مرحلة 2: تحسينات/كماليات بالخلفية // unawaited(_stageNiceToHave()); // // ابدأ إعادة تحميل الماركر لكن بثروتل داخلي // // startMarkerReloading(); // تأكد أنه مَخنوق التحديث (throttled) // _startMasterTimer(); // // Start listening to emergency shake gestures // EmergencySignalService.instance.startListening(() { // if (statusRide == 'Begin' || statusRide == 'start') { // Log.print("🚨 Emergency shake verified! Prompting SOS..."); // if (isBottomSheetShown) { // sosPassenger(); // } else { // Get.snackbar( // 'Emergency Mode Triggered'.tr, // 'Stay calm. We are here to help.'.tr, // backgroundColor: AppColor.redColor, // colorText: Colors.white, // duration: const Duration(seconds: 4), // ); // sosPassenger(); // } // } // }); // } // // === Helpers === // Future _initMinimalIcons() async { // // Icons are now loaded dynamically via MapLibre's _loadMapIcons onStyleLoaded // } // Future _stagePricingAndState() async { // try { // await getKazanPercent(); // أسعار السيرفر // } catch (e) { // Log.print("Error: $e"); // } // try { // _checkInitialRideStatus(); // تحقق من حالة الرحلة الحالية // } catch (e) { // Log.print("Error: $e"); // } // // لو عندك ضبط “وضع خفيف” حسب الجهاز: // _applyLowEndModeIfNeeded(); // } // Future _stageNiceToHave() async { // Log.print('🚀 MapPassengerController: Starting _stageNiceToHave'); // // 🔥 Fix: Future.wait uses ONE argument (the list). // await Future.wait([ // Future(() async { // try { // Log.print('🔍 Loading Favorites...'); // getFavioratePlaces(); // } catch (e) { // Log.print("Error: $e"); // } // }), // Future(() async { // try { // Log.print('🔍 Loading Waypoints...'); // readyWayPoints(); // } catch (e) { // Log.print("Error: $e"); // } // }), // Future(() async { // try { // Log.print('🔍 Loading Rate...'); // getPassengerRate(); // } catch (e) { // Log.print("Error: $e"); // } // }), // Future(() async { // try { // Log.print('🔍 Loading Coupons...'); // firstTimeRunToGetCoupon(); // } catch (e) { // Log.print("Error: $e"); // } // }), // ]); // Log.print('✅ MapPassengerController: _stageNiceToHave complete'); // try { // cardNumber = await SecureStorage().readData(BoxName.cardNumber); // } catch (e) { // Log.print("Error: $e"); // } // } // void _applyLowEndModeIfNeeded() { // // مثال بسيط: يمكنك حفظ فلاج بنظامك (من السيرفر/الإعدادات/الكاش) لتفعيل وضع خفيف // // لاحقاً فعّل: map.trafficEnabled=false, buildingsEnabled=false, تبسيط polylines... // // controller.lowEndMode = true; // } // uploadPassengerLocation() async { // await CRUD().post(link: AppLink.addpassengerLocation, payload: { // "passengerId": box.read(BoxName.passengerID), // "lat": passengerLocation.latitude.toString(), // "lng": passengerLocation.longitude.toString(), // "rideId": rideId.toString() // }); // } // void _showRideStartNotifications() { // // تنبيهات الأسعار حسب نوع السيارة // if (['Speed', 'Awfar Car'].contains(box.read(BoxName.carType))) { // NotificationController().showNotification('Fixed Price'.tr, // 'The captain is responsible for the route.'.tr, 'ding'); // } else if (['Comfort', 'Lady'].contains(box.read(BoxName.carType))) { // NotificationController().showNotification('Attention'.tr, // 'The price may increase if the route changes.'.tr, 'ding'); // } // } // /// Checks the current version of assets/style.json and purges the map cache if it has changed. // Future _checkAndRefreshMapStyle() async { // try { // final String styleJson = await rootBundle.loadString('assets/style.json'); // final Map decoded = json.decode(styleJson); // final String? currentVersion = // decoded['metadata'] != null ? decoded['metadata']['version'] : null; // if (currentVersion == null) return; // final String lastVersion = box.read(BoxName.styleVersion) ?? "0.0.0"; // if (currentVersion != lastVersion) { // Log.print( // "♻️ Map Style Version mismatch ($lastVersion -> $currentVersion). Purging offline cache..."); // await OfflineMapService.instance.clearCache(); // // Final verification check: give native engine time to flush // await Future.delayed(const Duration(milliseconds: 500)); // box.write(BoxName.styleVersion, currentVersion); // Log.print("✅ Style Version updated to $currentVersion"); // } // } catch (e) { // Log.print("⚠️ Style version check failed: $e"); // } // } // } // class CarLocation { // final String id; // final double latitude; // final double longitude; // final double distance; // final double duration; // CarLocation({ // required this.id, // required this.latitude, // required this.longitude, // this.distance = 10000, // this.duration = 10000, // }); // }