Food: background location + native delivery overlay

This commit is contained in:
Hamza-Ayed
2026-08-04 03:02:04 +03:00
parent d695834d19
commit ca55aaebe8
11 changed files with 772 additions and 31 deletions
@@ -7,6 +7,7 @@
// 3) active.php — مصدر الحقيقة لحالة المهام، يُصحّح أي فارق بعد أي انقطاع.
import 'dart:async';
import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:get/get.dart';
import 'package:just_audio/just_audio.dart';
@@ -16,11 +17,12 @@ import '../../main.dart';
import '../../print.dart';
import '../../views/food_delivery/food_offer_page.dart';
import '../../views/widgets/error_snakbar.dart';
import '../functions/location_controller.dart';
import '../voice_call_controller.dart';
import 'food_delivery_models.dart';
import 'food_delivery_service.dart';
import 'food_location_broadcaster.dart';
import 'food_notification_service.dart';
import 'food_overlay_bridge.dart';
import 'food_socket_service.dart';
class FoodDeliveryController extends GetxController {
@@ -70,6 +72,7 @@ class FoodDeliveryController extends GetxController {
final s = res.data!;
isDeliveryModeEnabled = s.deliveryModeEnabled;
box.write(BoxName.foodDeliveryModeEnabled, s.deliveryModeEnabled);
todayOrdersCount = s.todayOrdersCount;
todayEarnings = s.todayEarnings;
@@ -92,6 +95,11 @@ class FoodDeliveryController extends GetxController {
if (res.success) {
isDeliveryModeEnabled = enable;
// العزلة الخلفية تقرأ هذا المفتاح لتقرر فتح سوكيت الطعام أو إغلاقه،
// فيصل العرض حتى والتطبيق مغلق
box.write(BoxName.foodDeliveryModeEnabled, enable);
FlutterBackgroundService().invoke('foodModeChanged', {'enabled': enable});
if (enable) {
await FoodSocketService.instance.connect();
} else {
@@ -193,8 +201,17 @@ class FoodDeliveryController extends GetxController {
final isForeground = box.read(BoxName.isAppInForeground) ?? false;
if (isForeground == true) {
_openOfferScreen();
} else {
// التطبيق في الخلفية — إشعار ملء شاشة بقناة التوصيل الخاصة
return;
}
// التطبيق في الخلفية: النافذة العائمة أولاً (أندرويد)، وإن تعذّرت —
// صلاحية ناقصة أو iOS — يبقى الإشعار ملء الشاشة هو المسار البديل.
_showBackgroundOffer(offer);
}
Future<void> _showBackgroundOffer(FoodDeliveryOffer offer) async {
final shown = await FoodOverlayBridge.show(offer);
if (!shown) {
FoodNotificationService.instance.showOfferNotification(offer);
}
}
@@ -236,26 +253,6 @@ class FoodDeliveryController extends GetxController {
await _fetchPendingOffers();
}
await fetchActiveTasks(silent: true);
await _broadcastLocation();
}
// موقع السائق يُبثّ فقط ما دامت هناك مهمة نشطة — لا شيء يُرسل قبل الإسناد
// ولا بعد التسليم، والخادم يرفضه في الحالتين أصلاً كطبقة ثانية.
Future<void> _broadcastLocation() async {
if (activeTasks.isEmpty) return;
if (!Get.isRegistered<LocationController>()) return;
final loc = Get.find<LocationController>();
if (loc.myLocation.latitude == 0 && loc.myLocation.longitude == 0) return;
for (final task in activeTasks) {
await FoodDeliveryService.sendLocation(
orderId: task.id,
lat: loc.myLocation.latitude,
lng: loc.myLocation.longitude,
heading: loc.heading,
);
}
}
Future<void> _fetchPendingOffers() async {
@@ -274,6 +271,10 @@ class FoodDeliveryController extends GetxController {
final res = await FoodDeliveryService.getActiveTasks();
isLoadingTasks = false;
if (res.success) activeTasks = res.data ?? [];
// البثّ نفسه يجري في LocationController (المصدر الوحيد للموقع) — نكتفي
// هنا بتسجيل المهام النشطة كي يستمر البثّ بعد إغلاق هذه الشاشة.
FoodLocationBroadcaster.setActiveOrders(activeTasks.map((t) => t.id).toList());
_syncRideAvailability();
update();
}
@@ -323,6 +324,7 @@ class FoodDeliveryController extends GetxController {
_respondingOfferIds.remove(orderId);
pendingOffers.removeWhere((o) => o.orderId == orderId);
FoodNotificationService.instance.cancelOfferNotification();
FoodOverlayBridge.hide();
if (!res.success) {
mySnackbarWarning(res.message);