26-1-20/1
This commit is contained in:
@@ -22,6 +22,7 @@ import '../../../print.dart';
|
||||
import '../../../views/home/my_wallet/walet_captain.dart';
|
||||
import '../../../views/widgets/elevated_btn.dart';
|
||||
import '../../firebase/firbase_messge.dart';
|
||||
import '../../functions/background_service.dart';
|
||||
import '../../functions/crud.dart';
|
||||
import '../../functions/location_background_controller.dart';
|
||||
import '../../functions/location_controller.dart';
|
||||
@@ -33,6 +34,7 @@ class HomeCaptainController extends GetxController {
|
||||
Duration activeDuration = Duration.zero;
|
||||
Timer? activeTimer;
|
||||
Map data = {};
|
||||
bool isHomeMapActive = true;
|
||||
BitmapDescriptor carIcon = BitmapDescriptor.defaultMarker;
|
||||
bool isLoading = true;
|
||||
late double kazan = 0;
|
||||
@@ -80,63 +82,75 @@ class HomeCaptainController extends GetxController {
|
||||
void toggleHeatmap() async {
|
||||
isHeatmapVisible = !isHeatmapVisible;
|
||||
if (isHeatmapVisible) {
|
||||
await _fetchAndDrawHeatmap();
|
||||
await fetchAndDrawHeatmap();
|
||||
} else {
|
||||
heatmapPolygons.clear();
|
||||
}
|
||||
update(); // تحديث الواجهة
|
||||
}
|
||||
|
||||
// دالة جلب البيانات ورسمها
|
||||
Future<void> _fetchAndDrawHeatmap() async {
|
||||
isLoading = true;
|
||||
update();
|
||||
// داخل MapDriverController
|
||||
|
||||
// استبدل هذا الرابط برابط ملف JSON الذي يولده كود PHP
|
||||
// مثال: https://your-domain.com/api/driver/heatmap_data.json
|
||||
// متغير لتخزين المربعات
|
||||
// Set<Polygon> heatmapPolygons = {};
|
||||
|
||||
// دالة جلب البيانات ورسم الخريطة
|
||||
Future<void> fetchAndDrawHeatmap() async {
|
||||
// استخدم الرابط المباشر لملف JSON لسرعة قصوى
|
||||
final String jsonUrl =
|
||||
"https://rides.intaleq.xyz/intaleq/ride/heatmap/heatmap_data.json";
|
||||
"https://api.intaleq.xyz/intaleq/ride/rides/heatmap_live.json";
|
||||
|
||||
try {
|
||||
final response = await http.get(Uri.parse(jsonUrl));
|
||||
// نستخدم timestamp لمنع الكاش من الموبايل نفسه
|
||||
final response = await http.get(
|
||||
Uri.parse("$jsonUrl?t=${DateTime.now().millisecondsSinceEpoch}"));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final List<dynamic> data = json.decode(response.body);
|
||||
_generateGoogleMapPolygons(data);
|
||||
} else {
|
||||
print("Failed to load heatmap data");
|
||||
_generatePolygons(data);
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error fetching heatmap: $e");
|
||||
} finally {
|
||||
isLoading = false;
|
||||
update();
|
||||
print("Heatmap Error: $e");
|
||||
}
|
||||
} // تحويل البيانات إلى مربعات على خريطة جوجل
|
||||
}
|
||||
|
||||
void _generateGoogleMapPolygons(List<dynamic> data) {
|
||||
void _generatePolygons(List<dynamic> data) {
|
||||
Set<Polygon> tempPolygons = {};
|
||||
// نصف قطر المربع (تقريباً 0.0005 يعادل 50-60 متر، مما يعطي مربع 100 متر)
|
||||
// يجب أن يتناسب مع الـ precision المستخدم في PHP
|
||||
|
||||
// الأوفست لرسم المربع (نصف حجم الشبكة)
|
||||
// الشبكة دقتها 0.01 درجة، لذا نصفها 0.005
|
||||
double offset = 0.005;
|
||||
|
||||
for (var point in data) {
|
||||
double lat = double.parse(point['lat'].toString());
|
||||
double lng = double.parse(point['lng'].toString());
|
||||
int count = int.parse(point['count'].toString());
|
||||
|
||||
// تحديد اللون بناءً على الكثافة
|
||||
String intensity = point['intensity'] ?? 'low';
|
||||
int count = int.parse(point['count'].toString()); // ✅ جلب العدد
|
||||
|
||||
Color color;
|
||||
if (count >= 5) {
|
||||
color = Colors.red.withOpacity(0.5); // عالي جداً
|
||||
} else if (count >= 3) {
|
||||
color = Colors.orange.withOpacity(0.5); // متوسط
|
||||
Color strokeColor;
|
||||
|
||||
// 🧠 منطق الألوان: ندمج الذكاء (Intensity) مع العدد (Count)
|
||||
if (intensity == 'high' || count >= 5) {
|
||||
// منطقة مشتعلة (أحمر)
|
||||
// إما فيها طلبات ضائعة (Timeout) أو فيها عدد كبير من الطلبات
|
||||
color = Colors.red.withOpacity(0.35);
|
||||
strokeColor = Colors.red.withOpacity(0.8);
|
||||
} else if (intensity == 'medium' || count >= 3) {
|
||||
// منطقة متوسطة (برتقالي)
|
||||
color = Colors.orange.withOpacity(0.35);
|
||||
strokeColor = Colors.orange.withOpacity(0.8);
|
||||
} else {
|
||||
color = Colors.green.withOpacity(0.4); // منخفض
|
||||
// منطقة خفيفة (أصفر)
|
||||
color = Colors.yellow.withOpacity(0.3);
|
||||
strokeColor = Colors.yellow.withOpacity(0.6);
|
||||
}
|
||||
|
||||
// إنشاء المربع
|
||||
// رسم المربع
|
||||
tempPolygons.add(Polygon(
|
||||
polygonId: PolygonId("$lat-$lng"),
|
||||
consumeTapEvents: true, // للسماح بالضغط عليه مستقبلاً
|
||||
points: [
|
||||
LatLng(lat - offset, lng - offset),
|
||||
LatLng(lat + offset, lng - offset),
|
||||
@@ -144,13 +158,19 @@ class HomeCaptainController extends GetxController {
|
||||
LatLng(lat - offset, lng + offset),
|
||||
],
|
||||
fillColor: color,
|
||||
strokeColor: color.withOpacity(0.8),
|
||||
strokeWidth: 1,
|
||||
visible: true,
|
||||
strokeColor: strokeColor,
|
||||
strokeWidth: 2,
|
||||
));
|
||||
}
|
||||
|
||||
heatmapPolygons = tempPolygons;
|
||||
update(); // تحديث الخريطة
|
||||
}
|
||||
|
||||
// دالة لتشغيل الخريطة الحرارية كل فترة (مثلاً عند فتح الصفحة)
|
||||
void startHeatmapCycle() {
|
||||
fetchAndDrawHeatmap();
|
||||
// يمكن تفعيل Timer هنا لو أردت تحديثها تلقائياً كل 5 دقائق
|
||||
}
|
||||
|
||||
void goToWalletFromConnect() {
|
||||
@@ -179,11 +199,14 @@ class HomeCaptainController extends GetxController {
|
||||
|
||||
String stringActiveDuration = '';
|
||||
void onButtonSelected() {
|
||||
// totalPoints = Get.find<CaptainWalletController>().totalPoints;
|
||||
|
||||
// تم الإصلاح: التأكد من أن المتحكم موجود قبل استخدامه لتجنب الكراش
|
||||
if (!Get.isRegistered<CaptainWalletController>()) {
|
||||
Get.put(CaptainWalletController());
|
||||
}
|
||||
totalPoints = Get.find<CaptainWalletController>().totalPoints;
|
||||
isActive = !isActive;
|
||||
if (isActive) {
|
||||
if (double.parse(totalPoints) > -30000) {
|
||||
if (double.parse(totalPoints) > -200) {
|
||||
locationController.startLocationUpdates();
|
||||
HapticFeedback.heavyImpact();
|
||||
// locationBackController.startBackLocation();
|
||||
@@ -214,6 +237,107 @@ class HomeCaptainController extends GetxController {
|
||||
// }
|
||||
}
|
||||
|
||||
// متغيرات العداد للحظر
|
||||
RxString remainingBlockTimeStr = "".obs;
|
||||
Timer? _blockTimer;
|
||||
|
||||
/// دالة الفحص والدايلوج
|
||||
void checkAndShowBlockDialog() {
|
||||
String? blockStr = box.read(BoxName.blockUntilDate);
|
||||
if (blockStr == null || blockStr.isEmpty) return;
|
||||
|
||||
DateTime blockExpiry = DateTime.parse(blockStr);
|
||||
DateTime now = DateTime.now();
|
||||
|
||||
if (now.isBefore(blockExpiry)) {
|
||||
// 1. إجبار السائق على وضع الأوفلاين
|
||||
box.write(BoxName.statusDriverLocation, 'blocked');
|
||||
update();
|
||||
|
||||
// 2. بدء العداد
|
||||
_startBlockCountdown(blockExpiry);
|
||||
|
||||
// 3. إظهار الديالوج المانع
|
||||
Get.defaultDialog(
|
||||
title: "حسابك مقيد مؤقتاً ⛔",
|
||||
titleStyle:
|
||||
const TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
|
||||
barrierDismissible: false, // 🚫 ممنوع الإغلاق بالضغط خارجاً
|
||||
onWillPop: () async => false, // 🚫 ممنوع زر الرجوع في الأندرويد
|
||||
content: Obx(() => Column(
|
||||
children: [
|
||||
const Icon(Icons.timer_off_outlined,
|
||||
size: 50, color: Colors.orange),
|
||||
const SizedBox(height: 15),
|
||||
const Text(
|
||||
"لقد تجاوزت حد الإلغاء المسموح به (3 مرات).\nلا يمكنك العمل حتى انتهاء العقوبة.",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
remainingBlockTimeStr.value, // 🔥 الوقت يتحدث هنا
|
||||
style: const TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
)),
|
||||
confirm: Obx(() {
|
||||
// الزر يكون مفعلاً فقط عندما ينتهي الوقت
|
||||
bool isFinished = remainingBlockTimeStr.value == "00:00:00" ||
|
||||
remainingBlockTimeStr.value == "Done";
|
||||
return ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: isFinished ? Colors.green : Colors.grey,
|
||||
),
|
||||
onPressed: isFinished
|
||||
? () {
|
||||
Get.back(); // إغلاق الديالوج
|
||||
box.remove(BoxName.blockUntilDate); // إزالة الحظر
|
||||
Get.snackbar("أهلاً بك", "يمكنك الآن استقبال الطلبات",
|
||||
backgroundColor: Colors.green);
|
||||
}
|
||||
: null, // زر معطل
|
||||
child: Text(isFinished ? "Go Online" : "انتظر انتهاء الوقت"),
|
||||
);
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
// الوقت انتهى أصلاً -> تنظيف
|
||||
box.remove(BoxName.blockUntilDate);
|
||||
}
|
||||
}
|
||||
|
||||
/// دالة العداد التنازلي
|
||||
void _startBlockCountdown(DateTime expiry) {
|
||||
_blockTimer?.cancel();
|
||||
_blockTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
DateTime now = DateTime.now();
|
||||
if (now.isAfter(expiry)) {
|
||||
// انتهى الوقت
|
||||
remainingBlockTimeStr.value = "Done";
|
||||
timer.cancel();
|
||||
} else {
|
||||
// حساب الفرق وتنسيقه
|
||||
Duration diff = expiry.difference(now);
|
||||
String twoDigits(int n) => n.toString().padLeft(2, "0");
|
||||
String hours = twoDigits(diff.inHours);
|
||||
String minutes = twoDigits(diff.inMinutes.remainder(60));
|
||||
String seconds = twoDigits(diff.inSeconds.remainder(60));
|
||||
|
||||
remainingBlockTimeStr.value = "$hours:$minutes:$seconds";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
_blockTimer?.cancel();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
void getRefusedOrderByCaptain() async {
|
||||
DateTime today = DateTime.now();
|
||||
int todayDay = today.day;
|
||||
@@ -232,7 +356,8 @@ class HomeCaptainController extends GetxController {
|
||||
await sql.getCustomQuery(customQuery);
|
||||
countRefuse = results[0]['count'].toString();
|
||||
update();
|
||||
if (int.parse(countRefuse) > 3 || double.parse(totalPoints) <= -3000) {
|
||||
if (double.parse(totalPoints) <= -200) {
|
||||
// if (int.parse(countRefuse) > 3 || double.parse(totalPoints) <= -200) {
|
||||
locationController.stopLocationUpdates();
|
||||
activeStartTime = null;
|
||||
activeTimer?.cancel();
|
||||
@@ -322,9 +447,9 @@ class HomeCaptainController extends GetxController {
|
||||
isLoading = true;
|
||||
update();
|
||||
// This ensures we try to get a fix, but map doesn't crash if it fails
|
||||
await Get.find<LocationController>().getLocation();
|
||||
await locationController.getLocation();
|
||||
|
||||
var loc = Get.find<LocationController>().myLocation;
|
||||
var loc = locationController.myLocation;
|
||||
if (loc.latitude != 0) {
|
||||
myLocation = loc;
|
||||
}
|
||||
@@ -357,8 +482,35 @@ class HomeCaptainController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. دالة نستدعيها عند قبول الطلب
|
||||
void pauseHomeMapUpdates() {
|
||||
isHomeMapActive = false;
|
||||
update();
|
||||
}
|
||||
|
||||
// 4. دالة نستدعيها عند العودة للصفحة الرئيسية
|
||||
void resumeHomeMapUpdates() {
|
||||
isHomeMapActive = true;
|
||||
// إنعاش الخريطة عند العودة
|
||||
if (mapHomeCaptainController != null) {
|
||||
onMapCreated(mapHomeCaptainController!);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
@override
|
||||
void onInit() async {
|
||||
// ✅ طلب الإذونات أولاً
|
||||
bool permissionsGranted = await PermissionsHelper.requestAllPermissions();
|
||||
|
||||
if (permissionsGranted) {
|
||||
// ✅ بدء الخدمة بعد الحصول على الإذونات
|
||||
await BackgroundServiceHelper.startService();
|
||||
print('✅ Background service started successfully');
|
||||
} else {
|
||||
print('❌ لم يتم منح الإذونات - الخدمة لن تعمل');
|
||||
// اعرض رسالة للمستخدم
|
||||
}
|
||||
// await locationBackController.requestLocationPermission();
|
||||
Get.put(FirebaseMessagesController());
|
||||
addToken();
|
||||
@@ -374,24 +526,34 @@ class HomeCaptainController extends GetxController {
|
||||
getCaptainWalletFromBuyPoints();
|
||||
// onMapCreated(mapHomeCaptainController!);
|
||||
// totalPoints = Get.find<CaptainWalletController>().totalPoints.toString();
|
||||
getRefusedOrderByCaptain();
|
||||
// getRefusedOrderByCaptain();
|
||||
// 🔥 الفحص عند تشغيل التطبيق
|
||||
checkAndShowBlockDialog();
|
||||
box.write(BoxName.statusDriverLocation, 'off');
|
||||
// 2. عدل الليسنر ليصبح مشروطاً
|
||||
locationController.addListener(() {
|
||||
// Only animate if active, map is ready, AND location is valid (not 0,0)
|
||||
if (isActive && mapHomeCaptainController != null) {
|
||||
var loc = locationController.myLocation;
|
||||
// الشرط الذهبي: إذا كانت الصفحة غير نشطة أو الخريطة غير موجودة، لا تفعل شيئاً
|
||||
if (!isHomeMapActive || mapHomeCaptainController == null || isClosed)
|
||||
return;
|
||||
|
||||
if (isActive) {
|
||||
// isActive الخاصة بالزر "متصل/غير متصل"
|
||||
var loc = locationController.myLocation;
|
||||
if (loc.latitude != 0 && loc.longitude != 0) {
|
||||
mapHomeCaptainController!.animateCamera(
|
||||
CameraUpdate.newCameraPosition(
|
||||
CameraPosition(
|
||||
target: loc,
|
||||
zoom: 17.5,
|
||||
tilt: 50.0,
|
||||
bearing: locationController.heading,
|
||||
try {
|
||||
mapHomeCaptainController!.animateCamera(
|
||||
CameraUpdate.newCameraPosition(
|
||||
CameraPosition(
|
||||
target: loc,
|
||||
zoom: 17.5,
|
||||
tilt: 50.0,
|
||||
bearing: locationController.heading,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
);
|
||||
} catch (e) {
|
||||
// التقاط الخطأ بصمت إذا حدث أثناء الانتقال
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user