Fix payment server: remove global conRide to prevent crashing non-ride DB queries

This commit is contained in:
Hamza-Ayed
2026-08-01 17:17:33 +03:00
parent 09fa0c4e37
commit 1ddc6f8aa7
7 changed files with 217 additions and 31 deletions
@@ -14,7 +14,7 @@ try {
status,
start_location,
end_location,
carType,
car_type AS carType,
driver_id,distance,
price,
created_at
@@ -90,6 +90,6 @@ try {
} catch (Exception $e) {
error_log("[getRideStatusFromStartApp] Error: " . $e->getMessage());
echo json_encode(["status" => "failure", "message" => "An internal error occurred."]);
echo json_encode(["status" => "failure", "message" => $e->getMessage()]);
}
?>
+10
View File
@@ -0,0 +1,10 @@
<?php
require_once __DIR__ . '/connect.php';
try {
$con_ride = Database::get('ride');
$stmt = $con_ride->query("DESCRIBE ride");
$columns = $stmt->fetchAll(PDO::FETCH_ASSOC);
print_r($columns);
} catch (Exception $e) {
echo "Error: " . $e->getMessage();
}
@@ -129,4 +129,5 @@ class BoxName {
static const String isBusMode = 'isBusMode';
static const String busModeTripId = 'busModeTripId';
static const String busModeRouteId = 'busModeRouteId';
static const String isGmsAvailable = 'isGmsAvailable';
}
@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:geolocator/geolocator.dart' as geo;
import 'package:intaleq_maps/intaleq_maps.dart';
@@ -25,6 +26,37 @@ import 'background_service.dart';
import 'crud.dart';
import '../transit/transit_driver_controller.dart';
/// إعدادات تتبّع الموقع لحالة معيّنة من حالات السائق.
/// الهدف: عدم تشغيل الـ GPS بأقصى طاقته إلا أثناء الرحلة الفعلية.
class _LocationProfile {
final String name;
final LocationAccuracy accuracy;
final int interval; // ميلي ثانية
final double distanceFilter; // متر
const _LocationProfile(
this.name, this.accuracy, this.interval, this.distanceFilter);
_LocationProfile copyWith(
{String? name,
LocationAccuracy? accuracy,
int? interval,
double? distanceFilter}) =>
_LocationProfile(
name ?? this.name,
accuracy ?? this.accuracy,
interval ?? this.interval,
distanceFilter ?? this.distanceFilter,
);
/// بصمة تُستخدم لتفادي استدعاء changeSettings بلا داعٍ.
String get signature => '$accuracy|$interval|$distanceFilter';
@override
String toString() =>
'$name(acc: ${accuracy.name}, ${interval}ms, ${distanceFilter}m)';
}
class LocationController extends GetxController with WidgetsBindingObserver {
// ===================================================================
// ====== Tunables ======
@@ -38,6 +70,29 @@ class LocationController extends GetxController with WidgetsBindingObserver {
static const int powerSaveTriggerLevel = 20;
static const int powerSaveExitLevel = 25;
// ===================================================================
// ====== ملفات التتبّع التكيّفية (أ) ======
// ===================================================================
/// السائق غير متاح (off/blocked): أرخص إعداد ممكن مع إبقاء آخر موقع معروف.
static const _profileIdle =
_LocationProfile('idle', LocationAccuracy.balanced, 30000, 100);
/// متصل وينتظر طلباً: لا حاجة لدقة الملاحة — يكفي أن يعرفه السيرفر بالحي.
static const _profileWaiting =
_LocationProfile('waiting', LocationAccuracy.high, 15000, 50);
/// قَبِل الطلب وفي طريقه للراكب: الراكب يراقب السهم، نحتاج تحديثاً معقولاً.
static const _profileEnRoute =
_LocationProfile('enRoute', LocationAccuracy.high, 5000, 15);
/// رحلة جارية (أو وضع الباص): أعلى دقة — هنا تُحتسب المسافة والأجرة.
static const _profileOnTrip =
_LocationProfile('onTrip', LocationAccuracy.navigation, 4000, 10);
/// بعد هذه المدة من السكون نضاعف الفترة (سائق واقف في مرآب/إشارة طويلة).
static const Duration stationaryGrace = Duration(seconds: 90);
static const double stationarySpeedThreshold = 1.0; // م/ث ≈ 3.6 كم/س
// ===================================================================
// ====== Services & Variables ======
// ===================================================================
@@ -84,6 +139,17 @@ class LocationController extends GetxController with WidgetsBindingObserver {
bool _isReady = false;
bool _isPowerSavingMode = false;
/// هل خدمات جوجل متوفرة على الجهاز؟ (ج)
/// null = لم يُفحص بعد. على أجهزة هواوي بلا GMS يسقط الباكيج إلى
/// LocationManager الخام: دقة أقل واستهلاك بطارية أعلى ⇒ نخفّف الإعدادات.
bool? _gmsAvailable;
static const MethodChannel _deviceServicesChannel =
MethodChannel('com.siro.siro_driver/device_services');
/// آخر إعداد طُبّق فعلياً — لتفادي استدعاء changeSettings بلا تغيير.
String? _appliedProfileSignature;
DateTime? _stationarySince;
final List<Map<String, dynamic>> _trackBuffer = [];
final List<Map<String, dynamic>> _behaviorBuffer = [];
@@ -130,6 +196,7 @@ class LocationController extends GetxController with WidgetsBindingObserver {
_isReady = true;
initSocket();
await _detectGmsAvailability();
await _initLocationSettings();
_listenToBatteryChanges();
@@ -163,10 +230,15 @@ class LocationController extends GetxController with WidgetsBindingObserver {
// إيقاف خدمة الخلفية
BackgroundServiceHelper.stopService();
if (socket == null || (!socket!.connected && !_isInitializingSocket)) {
Log.print("🔄 Initializing Socket on resume...");
initSocket();
// 🔥 Fix: iOS silently kills WebSockets in background.
// The socket_io_client might still report 'connected' as true for ~20s.
// We MUST force a disconnect so initSocket() properly creates a fresh connection
// and immediately emits 'get_pending_orders' to catch missed FCM payloads.
if (socket != null) {
socket!.disconnect();
}
initSocket();
} else if (state == AppLifecycleState.paused ||
state == AppLifecycleState.detached) {
Log.print("📱 Lifecycle: App is in BACKGROUND");
@@ -555,13 +627,9 @@ class LocationController extends GetxController with WidgetsBindingObserver {
Future<void> _subscribeLocationStream() async {
_locSub?.cancel();
int interval = _isPowerSavingMode ? 10000 : 5000;
await location.enableBackgroundMode(enable: true);
location.changeSettings(
accuracy: LocationAccuracy.navigation,
interval: interval,
distanceFilter: _isPowerSavingMode ? 20 : 10,
);
// مصدر الحقيقة الوحيد لإعدادات الموقع: _applyProfile.
await _applyProfile(force: true);
_locSub = location.onLocationChanged.listen((LocationData loc) async {
if (loc.latitude == null || loc.longitude == null) return;
@@ -577,6 +645,11 @@ class LocationController extends GetxController with WidgetsBindingObserver {
speed = loc.speed ?? 0.0;
heading = loc.heading ?? 0.0;
// إعادة تقييم ملف التتبّع مع كل قراءة — لا يستدعي changeSettings
// إلا إذا تغيّرت الحالة فعلاً (حالة السائق/الرحلة/السكون/البطارية).
_trackStationary();
await _applyProfile();
box.write('last_lat', pos.latitude);
box.write('last_lng', pos.longitude);
box.write('last_heading', heading);
@@ -635,6 +708,8 @@ class LocationController extends GetxController with WidgetsBindingObserver {
_locSub?.cancel();
_locSub = null;
_appliedProfileSignature = null;
_stationarySince = null;
_recordTimer?.cancel();
_uploadBatchTimer?.cancel();
_socketHeartbeat?.cancel();
@@ -746,24 +821,123 @@ class LocationController extends GetxController with WidgetsBindingObserver {
if (level >= powerSaveExitLevel) _isPowerSavingMode = false;
if (previousMode != _isPowerSavingMode) {
_startBatchTimers();
_updateLocationSettings();
_applyProfile();
}
});
}
Future<void> _updateLocationSettings() async {
if (_locSub == null) return;
int interval = _isPowerSavingMode ? 10000 : 5000;
// ===================================================================
// ====== محرّك الملفات التكيّفية (أ + ج) ======
// ===================================================================
/// (ج) فحص توفّر Google Play Services مرة واحدة عند الإقلاع.
/// النتيجة تُحفظ في الصندوق ليقرأها الـ background isolate أيضاً،
/// وتُبَث مع بيانات السائق لمعرفة حجم شريحة الأجهزة بلا GMS.
Future<void> _detectGmsAvailability() async {
if (!Platform.isAndroid) {
_gmsAvailable = true; // iOS: CoreLocation دائماً متاح
return;
}
try {
final bool available =
await _deviceServicesChannel.invokeMethod<bool>('isGmsAvailable') ??
true;
_gmsAvailable = available;
box.write(BoxName.isGmsAvailable, available);
Log.print(available
? "✅ GMS available — FusedLocationProvider in use."
: "⚠️ No GMS (Huawei/AOSP) — falling back to raw LocationManager, "
"relaxing location profile to protect battery.");
} catch (e) {
// القناة غير مسجّلة (نسخة قديمة) — نفترض التوفّر ولا نغيّر السلوك.
_gmsAvailable = true;
Log.print("⚠️ GMS check failed, assuming available: $e");
}
}
/// تتبّع السكون: سائق واقف لا يحتاج قراءات متلاحقة.
void _trackStationary() {
if (speed >= stationarySpeedThreshold) {
_stationarySince = null;
} else {
_stationarySince ??= DateTime.now();
}
}
bool get _isStationary =>
_stationarySince != null &&
DateTime.now().difference(_stationarySince!) >= stationaryGrace;
/// يختار الملف المناسب من حالة السائق والرحلة، ثم يطبّق عليه
/// مُعدِّلات البطارية والسكون وغياب GMS.
_LocationProfile _resolveProfile() {
final String driverStatus =
box.read(BoxName.statusDriverLocation) ?? 'off';
final String rideStatus = (box.read(BoxName.rideStatus) ?? '').toString();
_LocationProfile p;
if (isBusMode) {
p = _profileOnTrip;
} else if (driverStatus == 'off' || driverStatus == 'blocked') {
p = _profileIdle;
} else if (rideStatus == 'Begin') {
p = _profileOnTrip;
} else if (rideStatus == 'Apply' || rideStatus == 'Arrived') {
p = _profileEnRoute;
} else {
p = _profileWaiting;
}
// وضع توفير الطاقة: ضاعف الفترة والمسافة، وانزل عن دقة الملاحة.
if (_isPowerSavingMode) {
p = p.copyWith(
name: '${p.name}+save',
accuracy: p.accuracy == LocationAccuracy.navigation
? LocationAccuracy.high
: p.accuracy,
interval: p.interval * 2,
distanceFilter: p.distanceFilter * 2,
);
}
// (ج) بلا GMS: كل قراءة تُشعل شريحة الـ GPS منفردة ⇒ خفّف التردد.
if (_gmsAvailable == false) {
p = p.copyWith(
name: '${p.name}+nogms',
interval: (p.interval * 1.5).round(),
distanceFilter: p.distanceFilter * 2,
);
}
// السكون المطوّل: ضاعف الفترة ثلاثاً — إلا في رحلة جارية (عدّاد الانتظار).
if (_isStationary && p.accuracy != LocationAccuracy.navigation) {
p = p.copyWith(
name: '${p.name}+idle',
interval: p.interval * 3,
);
}
return p;
}
/// يطبّق الملف الحالي. لا يستدعي changeSettings إلا عند تغيّر فعلي،
/// لأن كل استدعاء يعيد تشغيل مزوّد الموقع في الطبقة الأصلية.
Future<void> _applyProfile({bool force = false}) async {
if (_locSub == null && !force) return;
final p = _resolveProfile();
if (!force && p.signature == _appliedProfileSignature) return;
try {
await location.changeSettings(
accuracy: LocationAccuracy.navigation,
interval: interval,
distanceFilter: _isPowerSavingMode ? 20 : 10,
accuracy: p.accuracy,
interval: p.interval,
distanceFilter: p.distanceFilter,
);
Log.print(
"🔋 Location settings updated. Power Save: $_isPowerSavingMode");
_appliedProfileSignature = p.signature;
Log.print("📍 Location profile → $p");
} catch (e) {
Log.print("❌ Failed to update location settings: $e");
Log.print("❌ Failed to apply location profile $p: $e");
}
}
@@ -832,10 +1006,9 @@ class LocationController extends GetxController with WidgetsBindingObserver {
if (await _ensureServiceAndPermission()) {
try {
await location.enableBackgroundMode(enable: true);
location.changeSettings(
accuracy: LocationAccuracy.navigation,
interval: 1000,
distanceFilter: 10);
// (ب) لا نضبط الإعدادات هنا: كانت interval: 1000 تتعارض مع إعدادات
// _subscribeLocationStream وتُبقي الـ GPS مشتعلاً كل ثانية.
// مصدر الحقيقة الوحيد الآن هو _applyProfile.
} catch (e) {
Log.print("Warning: $e");
}
@@ -32,8 +32,7 @@ class HomeCaptainController extends GetxController {
Timer? activeTimer;
Map data = {};
bool isHomeMapActive = true;
InlqBitmap carIcon =
InlqBitmap.fromStyleImage('car_icon', size: 2.3);
InlqBitmap carIcon = InlqBitmap.fromStyleImage('car_icon', size: 2.3);
bool isMapReadyForCommands = false;
bool isLoading = true;
late double kazan = 0;
@@ -388,6 +387,7 @@ class HomeCaptainController extends GetxController {
_checkFatigueBeforeOnline(); // Throws exception if tired
if (double.parse(totalPoints) > minPointsThreshold) {
box.write(BoxName.statusDriverLocation, 'on');
locationController.startLocationUpdates();
HapticFeedback.heavyImpact();
activeStartTime = DateTime.now();
@@ -412,6 +412,7 @@ class HomeCaptainController extends GetxController {
update();
});
} else {
box.write(BoxName.statusDriverLocation, 'off');
locationController.stopLocationUpdates();
activeStartTime = null;
activeTimer?.cancel();
@@ -426,6 +427,7 @@ class HomeCaptainController extends GetxController {
update();
}
} else {
box.write(BoxName.statusDriverLocation, 'off');
locationController.stopLocationUpdates();
activeStartTime = null;
activeTimer?.cancel();
+2 -2
View File
@@ -2,7 +2,7 @@ name: intaleq_driver
description: "A new Flutter project."
publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 1.0.1+6
version: 2.0.0+66
environment:
sdk: ">=3.0.5 <4.0.0"
@@ -18,7 +18,7 @@ dependencies:
path: ./packages/get
get_storage:
path: ./packages/get_storage
intaleq_maps: ^2.2.1
intaleq_maps: ^2.3.0
secure_string_operations:
path: ./secure_string_operations
trip_overlay_plugin:
+2 -2
View File
@@ -2,7 +2,7 @@ name: intaleq_rider
description: "A new Flutter project."
publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 1.0.6+6
version: 2.0.0+66
environment:
sdk: ">=3.0.5 <4.0.0"
@@ -79,7 +79,7 @@ dependencies:
internet_connection_checker: ^3.0.1
connectivity_plus: ^6.1.5
app_links: ^7.0.0
intaleq_maps: ^2.2.1
intaleq_maps: ^2.3.0
socket_io_client: 1.0.2
# home_widget: ^0.7.0+1