// food_notification_service.dart — إشعارات وحدة التوصيل، منفصلة كلياً عن إشعارات الرحلات // // الفصل مقصود على ثلاثة مستويات: // 1) قناتان أندرويد خاصتان (food_delivery_offer_channel / food_delivery_status_channel) // غير 'high_importance_channel' الخاص بالرحلات — فيقدر السائق يكتم أو يغيّر // نغمة التوصيل من إعدادات النظام دون أن يمسّ تنبيه الرحلات إطلاقاً. // 2) معرّفات إشعارات مستقلة (7001/7002) فلا يستبدل إشعار توصيل إشعارَ رحلة. // 3) أزرار وحمولة خاصة ('FOOD_ACCEPT'/'FOOD_REJECT' + {"food_event": ...}) // يلتقطها فرع مستقل في handleNotificationResponse. import 'dart:convert'; import 'dart:ui'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'food_delivery_models.dart'; class FoodNotificationService { FoodNotificationService._(); static final FoodNotificationService instance = FoodNotificationService._(); // نفس نسخة الإضافة المستخدمة في NotificationController (singleton داخل الحزمة)، // لكن بقنوات ومعرّفات خاصة بنا — لا نستدعي initialize هنا إطلاقاً حتى لا // نستبدل معالِج النقر العام الذي هيّأه تطبيق الرحلات عند الإقلاع. final FlutterLocalNotificationsPlugin _plugin = FlutterLocalNotificationsPlugin(); static const String offerChannelId = 'food_delivery_offer_channel'; static const String statusChannelId = 'food_delivery_status_channel'; static const int offerNotificationId = 7001; static const int statusNotificationId = 7002; bool _channelsReady = false; Future ensureChannels() async { if (_channelsReady) return; final android = _plugin.resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>(); if (android == null) { _channelsReady = true; // iOS — لا قنوات return; } const offerChannel = AndroidNotificationChannel( offerChannelId, 'عروض التوصيل', description: 'تنبيه وصول عرض توصيل طلب طعام جديد', importance: Importance.max, playSound: true, sound: RawResourceAndroidNotificationSound('order1'), enableVibration: true, ); const statusChannel = AndroidNotificationChannel( statusChannelId, 'تحديثات طلبات التوصيل', description: 'تغيّر حالة طلب توصيل قائم (جاهز، ملغى، إلخ)', importance: Importance.high, playSound: true, ); await android.createNotificationChannel(offerChannel); await android.createNotificationChannel(statusChannel); _channelsReady = true; } /// إشعار عرض توصيل — ملء الشاشة (fullScreenIntent) كإشعار المكالمة، لأن مهلة /// العرض 20 ثانية ولا تحتمل أن يمرّ السائق على شريط الإشعارات لاحقاً. Future showOfferNotification(FoodDeliveryOffer offer) async { await ensureChannels(); final body = '${offer.merchantNameAr}\n' '💰 أجرة التوصيل: ${foodFormatPrice(offer.deliveryFee)}' '${offer.itemsCount > 0 ? ' | 🧾 ${offer.itemsCount} صنف' : ''}' '${offer.cashToCollect > 0 ? '\n💵 تحصيل نقدي: ${foodFormatPrice(offer.cashToCollect)}' : ''}'; final androidDetails = AndroidNotificationDetails( offerChannelId, 'عروض التوصيل', importance: Importance.max, priority: Priority.max, fullScreenIntent: true, category: AndroidNotificationCategory.call, visibility: NotificationVisibility.public, timeoutAfter: 20000, // يختفي مع انتهاء مهلة العرض نفسها sound: const RawResourceAndroidNotificationSound('order1'), audioAttributesUsage: AudioAttributesUsage.alarm, color: const Color(0xFFFF6B35), styleInformation: BigTextStyleInformation( body, contentTitle: '🍔 عرض توصيل جديد', summaryText: foodFormatPrice(offer.deliveryFee), ), actions: const [ AndroidNotificationAction('FOOD_ACCEPT', '✅ قبول', showsUserInterface: true, titleColor: Color(0xFF4CAF50)), AndroidNotificationAction('FOOD_REJECT', '❌ رفض', cancelNotification: true, titleColor: Color(0xFFE53935)), ], ); const iosDetails = DarwinNotificationDetails( sound: 'order1.wav', presentAlert: true, presentBadge: true, presentSound: true, categoryIdentifier: 'FOOD_OFFER_CATEGORY', interruptionLevel: InterruptionLevel.timeSensitive, ); await _plugin.show( id: offerNotificationId, title: '🍔 عرض توصيل جديد', body: '${offer.merchantNameAr} — ${foodFormatPrice(offer.deliveryFee)}', notificationDetails: NotificationDetails(android: androidDetails, iOS: iosDetails), payload: jsonEncode({ 'food_event': 'offer', 'order_id': offer.orderId, }), ); } Future cancelOfferNotification() async { await _plugin.cancel(id: offerNotificationId); } /// تحديث حالة مهمة قائمة (مثلاً إلغاء من النظام) — قناة أهدأ، بلا ملء شاشة Future showStatusNotification({ required int orderId, required String title, required String body, }) async { await ensureChannels(); const androidDetails = AndroidNotificationDetails( statusChannelId, 'تحديثات طلبات التوصيل', importance: Importance.high, priority: Priority.high, color: Color(0xFFFF6B35), ); const iosDetails = DarwinNotificationDetails( presentAlert: true, presentSound: true, ); await _plugin.show( id: statusNotificationId, title: title, body: body, notificationDetails: const NotificationDetails(android: androidDetails, iOS: iosDetails), payload: jsonEncode({'food_event': 'status', 'order_id': orderId}), ); } }