import 'dart:async'; import 'dart:developer'; import 'dart:ui' as ui; import 'package:app_tracking_transparency/app_tracking_transparency.dart'; import 'package:wanisdriver/routes/app_routes.dart'; import 'package:wanisdriver/services/push_notification_service.dart'; import 'package:wanisdriver/utils/colors.dart'; import 'package:wanisdriver/utils/dependency_injection.dart'; import 'package:wanisdriver/utils/firebase_options.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:get/get.dart'; import 'package:overlay_support/overlay_support.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:mapbox_maps_flutter/mapbox_maps_flutter.dart'; import 'Provider/RideNotification.dart'; import 'Provider/RideProvider.dart'; import 'Provider/ThemeProvider.dart'; import 'config/constants/app_constant.dart'; import 'config/screen_size.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:wanisdriver/utils/routes.dart'; import 'package:timezone/timezone.dart' as tz; import 'services/RideNotificationHelper.dart'; @pragma('vm:entry-point') Future _firebaseMessagingBackgroundHandler(RemoteMessage message) async { // Check if Firebase is already initialized if (Firebase.apps.isEmpty) { await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); } log("Handling a background message: ${message.messageId}"); final SharedPreferences prefs = await SharedPreferences.getInstance(); int actionId = int.tryParse(message.data["actionId"] ?? "0") ?? 0; if (actionId != 0) { final notifCached = prefs.getStringList("notification"); await prefs.setStringList('notification', ['$actionId|${message.data["action"]}', ...notifCached ?? []]); } } final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); late SharedPreferences sharedPref; // Add this at the top with other global variables final FlutterLocalNotificationsPlugin localNotificationsPlugin = FlutterLocalNotificationsPlugin(); final GlobalKey navigatorKey = GlobalKey(); void setupLocator() { // In your setupLocator() function if (!getIt.isRegistered()) { getIt.registerSingleton( FlutterLocalNotificationsPlugin(), ); } } Future main() async { WidgetsFlutterBinding.ensureInitialized(); await SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, // Optional: allows upside-down portrait ]); if (Firebase.apps.isEmpty) { await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); } MapboxOptions.setAccessToken(mapBoxApikey); await DependencyInjector.setup(); // Initialize notification service final notificationNewService = RideNotificationService(); await notificationNewService.initialize(); SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive); WakelockPlus.enable(); FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler); // Add this in your main() function after Firebase initialization Future _initializeLocalNotifications() async { const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); const InitializationSettings initializationSettings = InitializationSettings( android: initializationSettingsAndroid, iOS: DarwinInitializationSettings(), ); await localNotificationsPlugin.initialize(initializationSettings); } await _initializeLocalNotifications(); PushNotificationService notificationService = PushNotificationService(); await notificationService.initNotificationPlugin(); sharedPref = await SharedPreferences.getInstance(); // Assuming DependencyInjector.instance.providers returns a list // Set up your services via GetIt setupLocator(); runApp(MultiProvider( providers:DependencyInjector.providers, child: const MyApp(), )); } void onDidReceiveNotificationResponse(NotificationResponse notificationResponse) async { final String? payload = notificationResponse.payload; if (payload != null) { log('Notification payload: $payload'); // Handle the notification tap action here, e.g., navigate to a specific screen } } class MyApp extends StatefulWidget { const MyApp({super.key}); @override State createState() => _MyAppState(); } class _MyAppState extends State { List _messageList = []; static const MethodChannel _rideChannel = MethodChannel('com.camelsoft.wanisdriver/ride'); Future _requestNotificationPermission() async { FirebaseMessaging messaging = FirebaseMessaging.instance; NotificationSettings settings = await messaging.requestPermission( alert: true, announcement: false, badge: true, carPlay: false, criticalAlert: false, provisional: false, sound: true, ); log('User granted permission: ${settings.authorizationStatus}'); } @override void initState() { super.initState(); _requestNotificationPermission(); getIt().setupInteractedMessage(); WidgetsFlutterBinding.ensureInitialized().addPostFrameCallback((_) async { final status = await AppTrackingTransparency.requestTrackingAuthorization(); }); Get.put(RideProvider()); PushNotificationService().setupInteractedMessage(); FirebaseMessaging.onMessage.listen(_onMessageReceived); FirebaseMessaging.onMessageOpenedApp.listen(_handleNotificationTap); _setupRideNavigationListener(); } void _onMessageReceived(RemoteMessage message) { try { if (message.notification != null) { final String title = message.notification!.title ?? "Title"; final String body = message.notification!.body ?? "Body"; final String notificationIdStr = message.data['id'] ?? '0'; final int notificationId = int.tryParse(notificationIdStr) ?? 0; _showNotification(title, body, notificationId); } } catch (e) { log('Error handling the message: $e'); } } void _handleNotificationTap(RemoteMessage message) { final int? rideId = int.tryParse(message.data['rideid'] ?? ''); if (rideId != null) { context.read().enqueue(rideId); } else { log('Ride ID is null. Cannot navigate.'); } } Future _showNotification(String title, String body, int notificationId) async { _messageList.add(body); var inboxStyle = InboxStyleInformation( _messageList, contentTitle: '$title (${_messageList.length} new messages)', summaryText: '${_messageList.length} new messages', ); var androidDetails = AndroidNotificationDetails( 'ride_channel_id', 'ride_channel_name', importance: Importance.high, priority: Priority.high, groupKey: 'com.camelsoft.wanisdriver.ride', setAsGroupSummary: true, styleInformation: inboxStyle, ); await flutterLocalNotificationsPlugin.show( 0, '$title (${_messageList.length} new messages)', '${_messageList.length} new messages received', NotificationDetails(android: androidDetails), ); var individualAndroidDetails = const AndroidNotificationDetails( 'ride_channel_id', 'ride_channel_name', importance: Importance.high, priority: Priority.high, groupKey: 'com.camelsoft.wanisdriver.ride', ); await flutterLocalNotificationsPlugin.show( notificationId, title, body, NotificationDetails(android: individualAndroidDetails), ); } void _setupRideNavigationListener() { _rideChannel.setMethodCallHandler(_handleRideNavigation); } Future _handleRideNavigation(MethodCall call) async { if (call.method == 'navigateToRide') { int rideId = call.arguments; // Navigate to the new ride screen context.read().enqueue(rideId); } } Widget screenUtilInit(Widget upperChild){ return ScreenUtilInit( useInheritedMediaQuery: true, minTextAdapt: true, designSize: _getDesignSize(context), child: GestureDetector( onTap: () { FocusManager.instance.primaryFocus?.unfocus(); }, child:upperChild , ), ); } // Add this new method for scheduling local notifications only Future _scheduleRideNotification({ required int rideId, required String title, required String body, required DateTime scheduledTime, }) async { try { final androidDetails = const AndroidNotificationDetails( 'ride_schedule_channel', // Different channel than Firebase notifications 'Scheduled Ride Alerts', importance: Importance.high, priority: Priority.high, ); await localNotificationsPlugin.zonedSchedule( rideId, // Using rideId as notification ID title, body, tz.TZDateTime.from(scheduledTime.subtract(const Duration(minutes: 30)), tz.local), NotificationDetails(android: androidDetails), androidAllowWhileIdle: true, uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime, ); } catch (e) { log('Error scheduling ride notification: $e'); } } // Call this when you need to cancel a specific ride notification Future _cancelRideNotification(int rideId) async { await localNotificationsPlugin.cancel(rideId); } @override Widget build(BuildContext context) { final themeProvider = Provider.of(context); final screenUtility = ScreenConfiguration(); screenUtility.initialize(context); return screenUtilInit( GetMaterialApp( navigatorKey: navigatorKey, locale: const Locale('fr'), supportedLocales: const [ Locale('fr', 'FR'), Locale('en', 'US'), Locale('ar', 'AR'), ], localizationsDelegates: const [ GlobalWidgetsLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], debugShowCheckedModeBanner: false, theme: AppThemes.lightTheme, darkTheme: AppThemes.darkTheme, themeMode: themeProvider.themeMode, title: 'Wanis Smart Driver', getPages: routes, initialRoute: AppRoutes.SplashScreen, builder: (context, child) { return OverlaySupport.global( child: Directionality( textDirection: TextDirection.ltr, child: MediaQuery.removePadding( context: context, removeTop: false, removeBottom: false, child: child!, ), ), ); }, ), ); } } // Optimized design size based on screen width ui.Size _getDesignSize(BuildContext context) { final width = MediaQuery.of(context).size.width; if (width < 400) return const ui.Size(360, 690); if (width < 450) return const ui.Size(412, 892); if (width < 650) return const ui.Size(480, 892); if (width < 700) return const ui.Size(500, 932); return const ui.Size(612, 1024); // Tablet or large screen }