import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_overlay_window/flutter_overlay_window.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_stripe/flutter_stripe.dart'; import 'package:permission_handler/permission_handler.dart'; // ✅ جديد import 'package:device_info_plus/device_info_plus.dart'; // ✅ جديد import 'constant/api_key.dart'; import 'constant/info.dart'; import 'constant/notification.dart'; import 'controller/firebase/firbase_messge.dart'; import 'controller/firebase/local_notification.dart'; import 'controller/functions/background_service.dart'; import 'controller/functions/crud.dart'; import 'controller/local/local_controller.dart'; import 'controller/local/translations.dart'; import 'firebase_options.dart'; import 'models/db_sql.dart'; import 'print.dart'; import 'splash_screen_page.dart'; import 'views/home/Captin/orderCaptin/order_request_page.dart'; import 'views/home/Captin/driver_map_page.dart'; import 'views/home/Captin/orderCaptin/order_over_lay.dart'; final box = GetStorage(); const storage = FlutterSecureStorage(); DbSql sql = DbSql.instance; final GlobalKey navigatorKey = GlobalKey(); const platform = MethodChannel('com.intaleq_driver/app_control'); // ✅ قنوات الإشعارات المطلوبة const String backgroundServiceChannelId = 'driver_service_channel'; const String locationServiceChannelId = 'location_service_channel'; const String geolocatorChannelId = 'geolocator_channel'; /// تهيئة Firebase بوعي لمنع تهيئة مكرّرة على أندرويد (isolates متعددة) Future initFirebaseIfNeeded() async { if (Firebase.apps.isEmpty) { await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform); } else { Firebase.app(); } } /// ✅ طلب إذن الإشعارات على Android 13+ Future requestNotificationPermission() async { if (Platform.isAndroid) { try { final androidInfo = await DeviceInfoPlugin().androidInfo; if (androidInfo.version.sdkInt >= 33) { final status = await Permission.notification.request(); if (status.isGranted) { print('✅ Notification permission granted'); return true; } else if (status.isDenied) { print('⚠️ Notification permission denied'); return false; } else if (status.isPermanentlyDenied) { print('⚠️ Notification permission permanently denied'); await openAppSettings(); return false; } } else { print('✅ Android < 13, no runtime notification permission needed'); return true; } } catch (e) { print('❌ Error requesting notification permission: $e'); return false; } } return true; // iOS } /// ✅ إنشاء جميع قنوات الإشعارات المطلوبة Future createAllNotificationChannels() async { if (!Platform.isAndroid) return; final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); // قناة Background Service const AndroidNotificationChannel backgroundChannel = AndroidNotificationChannel( backgroundServiceChannelId, 'خدمة السائق', description: 'استقبال الطلبات في الخلفية', importance: Importance.low, playSound: false, enableVibration: false, showBadge: false, ); // قناة Location Service const AndroidNotificationChannel locationChannel = AndroidNotificationChannel( locationServiceChannelId, 'خدمة الموقع', description: 'تتبع موقع السائق', importance: Importance.low, playSound: false, enableVibration: false, showBadge: false, ); // قناة Geolocator const AndroidNotificationChannel geolocatorChannel = AndroidNotificationChannel( geolocatorChannelId, 'تحديد الموقع', description: 'خدمة تحديد الموقع الجغرافي', importance: Importance.low, playSound: false, enableVibration: false, showBadge: false, ); try { // إنشاء جميع القنوات await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.createNotificationChannel(backgroundChannel); await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.createNotificationChannel(locationChannel); await flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.createNotificationChannel(geolocatorChannel); print('✅ All notification channels created successfully'); } catch (e) { print('❌ Error creating notification channels: $e'); } } /// ============ Handlers: Background ============ @pragma('vm:entry-point') Future backgroundMessageHandler(RemoteMessage message) async { WidgetsFlutterBinding.ensureInitialized(); await initFirebaseIfNeeded(); await GetStorage.init(); if (!Get.isRegistered()) { Get.put(NotificationController()); } if (!Get.isRegistered()) { Get.put(FirebaseMessagesController()); } if (!await FlutterOverlayWindow.isPermissionGranted()) { Log.print("Overlay permission not granted; showing only notification."); } if (Platform.isAndroid) { String category = message.data['category'] ?? ''; if (message.notification != null) { Log.print('message.notification!.title: ${message.notification!.title}'); if (category == 'Order' || category == 'OrderSpeed') { final myListString = message.data['DriverList'] ?? '[]'; Log.print('myListString: $myListString'); List myList; try { myList = jsonDecode(myListString) as List; } catch (e) { Log.print('Error decoding JSON: $e'); myList = []; } final isOverlayActive = await FlutterOverlayWindow.isActive(); if (isOverlayActive) { await FlutterOverlayWindow.shareData(myList); } else { await FlutterOverlayWindow.showOverlay( enableDrag: true, flag: OverlayFlag.focusPointer, positionGravity: PositionGravity.auto, height: WindowSize.matchParent, width: WindowSize.matchParent, startPosition: const OverlayPosition(0, -30), ); await FlutterOverlayWindow.shareData(myList); } NotificationController().showNotification( message.notification!.title.toString(), message.notification!.body.toString(), 'order', myListString, ); } else { FirebaseMessagesController().fireBaseTitles(message); } } } } @pragma('vm:entry-point') void notificationTapBackground(NotificationResponse notificationResponse) { Log.print('Notification tapped in background!'); NotificationController().handleNotificationResponse(notificationResponse); } /// ============ Entrypoint: Overlay ============ @pragma('vm:entry-point') void overlayMain() async { WidgetsFlutterBinding.ensureInitialized(); await GetStorage.init(); if (!Get.isRegistered()) { Get.put(NotificationController()); } runApp(const MaterialApp( debugShowCheckedModeBanner: false, home: OrderOverlay(), )); } /// إغلاق الـ Overlay عند الحاجة Future closeOverLay() async { final isOverlayActive = await FlutterOverlayWindow.isActive(); if (isOverlayActive) { await FlutterOverlayWindow.closeOverlay(); } } /// ============ Entrypoint: App ============ void main() { runZonedGuarded(() async { WidgetsFlutterBinding.ensureInitialized(); await initFirebaseIfNeeded(); await WakelockPlus.enable(); await GetStorage.init(); await initializeDateFormatting(); Stripe.publishableKey = AK.publishableKeyStripe; await SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); // ✅ الترتيب الصحيح: الإذونات → القنوات → الخدمات // 1. طلب إذن الإشعارات أولاً (Android 13+) bool notificationPermissionGranted = await requestNotificationPermission(); if (!notificationPermissionGranted) { print('⚠️ تحذير: لم يتم منح إذن الإشعارات - قد لا تعمل بعض الميزات'); } // 2. إنشاء جميع قنوات الإشعارات await createAllNotificationChannels(); // 3. تهيئة الخدمة (بدون تشغيلها) await BackgroundServiceHelper.initialize(); // 4. سجل الهاندلر تبع رسائل الخلفية FirebaseMessaging.onBackgroundMessage(backgroundMessageHandler); runApp(const MyApp()); }, (error, stack) { final errorString = error.toString(); print("Caught Dart error: $error"); print(stack); final isIgnoredError = errorString.contains('PERMISSION_DENIED') || errorString.contains('FormatException') || errorString.contains('Null check operator used on a null value'); if (!isIgnoredError) { CRUD.addError(error.toString(), stack.toString(), 'main'); } else { print("Ignoring error and not sending to server: $errorString"); } }); } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State with WidgetsBindingObserver { @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); _initApp(); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } Future _initApp() async { try { if (!Get.isRegistered()) { Get.put(NotificationController()); } if (!Get.isRegistered()) { Get.put(FirebaseMessagesController()).getToken(); } await FirebaseMessaging.instance.requestPermission(); await NotificationController().initNotifications(); // Generate a random index to pick a message final random = Random(); final randomMessage = syrianDriverMessages[random.nextInt(syrianDriverMessages.length)]; // Schedule the notification with the random message NotificationController().scheduleNotificationsForSevenDays( randomMessage.split(':')[0], randomMessage.split(':')[1], "tone1", ); } catch (e) { Log.print("Error during _initApp: $e"); } } @override Widget build(BuildContext context) { final LocaleController localController = Get.put(LocaleController()); return GetMaterialApp( navigatorKey: navigatorKey, title: AppInformation.appName, translations: MyTranslation(), debugShowCheckedModeBanner: false, locale: localController.language, theme: localController.appTheme, initialRoute: '/', getPages: [ GetPage(name: '/', page: () => SplashScreen()), GetPage(name: '/OrderRequestPage', page: () => OrderRequestPage()), GetPage( name: '/passenger-location-map', page: () => PassengerLocationMapPage(), ), ], ); } }