import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; import 'package:path_provider/path_provider.dart'; import '../Helper/Logger.dart'; class LocalNotificationService { static final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; static final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin(); static const AndroidNotificationChannel channel = AndroidNotificationChannel( 'Nearle', // Channel ID 'Nearle Notification', // Channel name description: 'Channel for Nearle notifications', // Channel description importance: Importance.max, playSound: true, sound: RawResourceAndroidNotificationSound('notification_ring'), enableVibration: true, showBadge: true, ); static Future initialize(BuildContext context, String status) async { // Create Android notification channel await _notificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.createNotificationChannel(channel); // Initialize local notifications const InitializationSettings initializationSettings = InitializationSettings( android: AndroidInitializationSettings("@mipmap/ic_launcher"), iOS: DarwinInitializationSettings( requestSoundPermission: true, requestBadgePermission: true, requestAlertPermission: true, defaultPresentSound: true, defaultPresentBadge: true, defaultPresentBanner: true, defaultPresentAlert: true, defaultPresentList: true, ), ); await _notificationsPlugin.initialize( initializationSettings, onDidReceiveNotificationResponse: (NotificationResponse response) async { if (response.payload != null) { await selectNotification(response.payload); } }, ); // Handle messages when the app is opened from a terminated state RemoteMessage? initialMessage = await _firebaseMessaging.getInitialMessage(); if (initialMessage != null) { await _handleInitialMessage(initialMessage); } // Handle foreground messages FirebaseMessaging.onMessage.listen((RemoteMessage message) async { logger.i('Received foreground message: ${message.data}'); await _handleMessage(message); }); // Handle messages when the app is opened from a notification FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async { logger.i('Message opened app: ${message.data}'); await _handleMessageOpenedApp(message); }); } static Future _handleInitialMessage(RemoteMessage message) async { if (message.notification != null) { // Cancel the notification final notificationId = int.parse(message.data['notification_id'] ?? '0'); await _notificationsPlugin.cancel(notificationId); await display(message); } } static Future _handleMessage(RemoteMessage message) async { if (message.notification != null) { logger.i('Displaying notification: ${message.notification!.body}'); await display(message); } } static Future _handleMessageOpenedApp(RemoteMessage message) async { try { // Cancel the notification using the ID from message.data final notificationId = int.parse(message.data['notification_id'] ?? '0'); await _notificationsPlugin.cancel(notificationId); if (message.notification != null) { // Navigator.of(Get.context!).push( // MaterialPageRoute( // builder: (BuildContext context) => HomeView(selectedIndex: 1), // ), // ); } } on Exception catch (e) { logger.e('Error in handleMessageOpenedApp: $e'); } } static Future selectNotification(String? payload) async { try { if (payload != null) { final decodedPayload = jsonDecode(payload); final notificationId = int.parse(decodedPayload['id'] ?? '0'); // Cancel the specific notification await _notificationsPlugin.cancel(notificationId); // Navigate to the desired page // Navigator.of(Get.context!).push( // MaterialPageRoute( // builder: (BuildContext context) => HomeView(selectedIndex: 1), // ), // ); } } on Exception catch (e) { logger.e('Error in selectNotification: $e'); } } static Future _downloadAndSaveImage(String imageUrl, String fileName) async { try { final directory = await getTemporaryDirectory(); final filePath = '${directory.path}/$fileName'; final response = await http.get(Uri.parse(imageUrl)); if (response.statusCode == 200) { final file = File(filePath); await file.writeAsBytes(response.bodyBytes); logger.i('Image downloaded to: $filePath'); return filePath; } else { logger.e('Failed to download image: ${response.statusCode}'); return null; } } catch (e) { logger.e('Error downloading image: $e'); return null; } } static String? _extractImageUrl(RemoteMessage message) { // General data field String? imageUrl = message.data['image'] as String?; // Android-specific if (imageUrl == null && message.notification?.android?.imageUrl != null) { imageUrl = message.notification!.android!.imageUrl; } // iOS-specific if (imageUrl == null && message.notification?.apple?.imageUrl != null) { imageUrl = message.notification!.apple!.imageUrl; } logger.i('Extracted image URL: $imageUrl'); return imageUrl; } static Future display(RemoteMessage message) async { if (message.notification != null) { // Navigator.of(Get.context!).push( // MaterialPageRoute( // builder: (BuildContext context) => HomeView(selectedIndex: 1), // ), // ); } try { final id = DateTime.now().millisecondsSinceEpoch ~/ 1000; // Create a payload that includes the notification ID final payload = { 'id': id.toString(), 'data': message.data, }; NotificationDetails notificationDetails; // Extract image URL from data or android-specific field final imageUrl = _extractImageUrl(message); if (imageUrl != null && imageUrl.isNotEmpty) { // Download the image final imagePath = await _downloadAndSaveImage(imageUrl, 'notification_image.jpg'); if (imagePath != null) { if (Platform.isAndroid) { String? bodyText = message.notification?.body ?? message.data['body'] ?? ''; List? lines = bodyText?.split('\n'); // or split on ',' or build manually final inboxStyle = InboxStyleInformation( lines ?? [], contentTitle: message.notification?.title ?? message.data['title'], summaryText: '', // optional, can be empty or a short summary ); notificationDetails = NotificationDetails( android: AndroidNotificationDetails( 'Nearle', 'Nearle Notification', importance: Importance.max, priority: Priority.high, icon: 'notification', playSound: true, sound: RawResourceAndroidNotificationSound('notification_ring'), enableVibration: true, fullScreenIntent: true, channelShowBadge: true, ongoing: false, // Allow dismissal autoCancel: true, // Cancel when tapped styleInformation: inboxStyle, ), iOS: DarwinNotificationDetails( presentAlert: true, presentBadge: true, presentSound: true, presentList: true, presentBanner: true, attachments: [ DarwinNotificationAttachment(imagePath), ], ), ); } else { // iOS or other platforms notificationDetails = NotificationDetails( android: AndroidNotificationDetails( 'Nearle', 'Nearle Notification', importance: Importance.max, priority: Priority.high, icon: 'notification', playSound: true, sound: RawResourceAndroidNotificationSound('notification_ring'), enableVibration: true, fullScreenIntent: true, channelShowBadge: true, ongoing: false, // Allow dismissal autoCancel: true, // Cancel when tapped ), iOS: DarwinNotificationDetails( presentAlert: true, presentBadge: true, presentSound: true, presentList: true, presentBanner: true, attachments: [ DarwinNotificationAttachment(imagePath), ], ), ); } } else { // Fallback if image download fails notificationDetails = const NotificationDetails( android: AndroidNotificationDetails( 'Nearle', 'Nearle Notification', importance: Importance.max, priority: Priority.high, icon: 'notification', playSound: true, sound: RawResourceAndroidNotificationSound('notification_ring'), enableVibration: true, fullScreenIntent: true, channelShowBadge: true, ongoing: false, // Allow dismissal autoCancel: true, // Cancel when tapped ), iOS: DarwinNotificationDetails( presentAlert: true, presentBadge: true, presentSound: true, presentList: true, presentBanner: true, ), ); } } else { // No image in payload, use default notification details notificationDetails = const NotificationDetails( android: AndroidNotificationDetails( 'Nearle', 'Nearle Notification', importance: Importance.max, priority: Priority.high, icon: 'notification', playSound: true, sound: RawResourceAndroidNotificationSound('notification_ring'), enableVibration: true, fullScreenIntent: true, channelShowBadge: true, ongoing: false, // Allow dismissal autoCancel: true, // Cancel when tapped ), iOS: DarwinNotificationDetails( presentAlert: true, presentBadge: true, presentSound: true, presentList: true, presentBanner: true, ), ); } await _notificationsPlugin.show( id, message.notification?.title ?? message.data['title'] ?? 'Nearle', message.notification?.body ?? message.data['body'] ?? 'Notification', notificationDetails, payload: jsonEncode(payload), ); } on Exception catch (e) { logger.e('Error displaying notification: $e'); } } }