import 'dart:convert'; import 'dart:io'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:didvan/models/notification_message.dart'; import 'package:didvan/services/app_home_widget/home_widget_repository.dart'; import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/services/storage/storage.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; import 'package:path_provider/path_provider.dart'; class NotificationService { static final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); static Future initializeNotification() async { const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); const InitializationSettings initializationSettings = InitializationSettings(android: initializationSettingsAndroid); await _flutterLocalNotificationsPlugin.initialize( initializationSettings, onDidReceiveNotificationResponse: _onNotificationResponse, ); if (!kIsWeb) { const AndroidNotificationChannel channel = AndroidNotificationChannel( 'content', 'Content Notification', description: 'Notification channel', importance: Importance.max, playSound: true, ); await _flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>() ?.createNotificationChannel(channel); } } static Future _onNotificationResponse( NotificationResponse response) async { if (kDebugMode) { print("=== LOCAL NOTIFICATION CLICKED ==="); print("Notification ID: ${response.id}"); print("Action ID: ${response.actionId}"); print("Input: ${response.input}"); print("Response Type: ${response.notificationResponseType}"); print("Payload: ${response.payload}"); print("==================================="); } try { final payload = response.payload; if (payload != null) { NotificationMessage data = NotificationMessage.fromJson(jsonDecode(payload)); HomeWidgetRepository.data = data; if (kDebugMode) { print("Parsed payload data: ${data.toJson()}"); } await HomeWidgetRepository.decideWhereToGoNotif(); await StorageService.delete( key: 'notification${AppInitializer.createNotificationId(data)}'); } } catch (e) { if (kDebugMode) { print("Error in _onNotificationResponse: $e"); } e.printError(); } } static Future showFirebaseNotification(RemoteMessage message) async { if (kDebugMode) { print("=== SHOWING FIREBASE NOTIFICATION ==="); print("Message Data: ${message.data}"); print( "Notification: ${message.notification?.title} - ${message.notification?.body}"); } try { final data = NotificationMessage.fromJson(message.data); if (kDebugMode) { print("Parsed NotificationMessage: ${data.toJson()}"); print("Notification Type: ${data.notificationType}"); } // Widget-refresh pushes carry notificationType=3 and skip the UI. if (data.notificationType == '3' || (data.notificationType?.contains('3') ?? false)) { if (kDebugMode) { print("Widget notification - calling fetchWidget()"); } HomeWidgetRepository.fetchWidget(); return; } NotificationService.showNotification(data); } catch (e) { if (kDebugMode) { print("Error in showFirebaseNotification: $e"); } e.printError(); } } /// Downloads [url] into a temp file and returns its absolute path. Used by /// `BigPictureStyleInformation` which needs a local file path. Returns /// null on any failure so we degrade to a no-image notification rather /// than crashing. static Future _downloadImageToTemp(String url) async { try { final res = await http.get(Uri.parse(url)); if (res.statusCode != 200) { if (kDebugMode) { print('image download failed: HTTP ${res.statusCode}'); } return null; } final dir = await getTemporaryDirectory(); final file = File( '${dir.path}/notif_${DateTime.now().millisecondsSinceEpoch}.img', ); await file.writeAsBytes(res.bodyBytes, flush: true); return file.path; } catch (e) { if (kDebugMode) print('image download exception: $e'); return null; } } static Future showNotification(NotificationMessage data) async { final notifType = data.notificationType ?? ''; if (notifType.contains('2')) { final String? storedValue = await StorageService.getValue( key: 'notification${AppInitializer.createNotificationId(data)}'); if (storedValue != null) { _flutterLocalNotificationsPlugin .cancel(AppInitializer.createNotificationId(data)); data.body = '${data.body}\n$storedValue'; } } // Resolve the image we want on the notification. // - `data.imageUrl` is the new content-service broadcast field (remote // URL — must be downloaded into the cache before Android can render // it via BigPictureStyle). // - `data.image` is the legacy widget-update field (already a local // path). String? bigPicturePath; if (data.imageUrl != null && data.imageUrl!.isNotEmpty) { bigPicturePath = await _downloadImageToTemp(data.imageUrl!); } else if (data.image != null && data.image!.isNotEmpty) { bigPicturePath = data.image; } // Pick the Android style: // - notificationType '1' (legacy "big news" push) → BigPictureStyle // - any push with a resolved image (content broadcasts) → // BigPictureStyle so the cover image shows up // - otherwise → MessagingStyle (compact) final useBigPicture = notifType.contains('1') || bigPicturePath != null; final AndroidNotificationDetails androidPlatformChannelSpecifics = AndroidNotificationDetails( 'content', 'Content Notification', channelDescription: 'Notification channel', importance: Importance.max, priority: Priority.high, playSound: true, styleInformation: useBigPicture && bigPicturePath != null ? BigPictureStyleInformation( FilePathAndroidBitmap(bigPicturePath), contentTitle: data.title, summaryText: data.body ?? '', ) : MessagingStyleInformation( Person(name: data.title), messages: [Message(data.body ?? '', DateTime.now(), null)], ), ); final NotificationDetails platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics); // Pass the full data map (incl. id/type/link/imageUrl) through the // payload so the onTap handler can route correctly. final payloadJson = jsonEncode(data.toJson()); await _flutterLocalNotificationsPlugin.show( useBigPicture ? DateTime.now().millisecondsSinceEpoch ~/ 1000 : AppInitializer.createNotificationId(data), data.title, data.body, platformChannelSpecifics, payload: payloadJson, ); if (notifType.contains('2')) { await StorageService.setValue( key: 'notification${AppInitializer.createNotificationId(data)}', value: data.body); } } }