// Haveno App extends the features of Haveno, supporting mobile devices and more. // Copyright (C) 2024 Kewbit (https://kewbit.org) // Source Code: https://git.haveno.com/haveno/haveno-app.git // // Author: Kewbit // Website: https://kewbit.org // Contact Email: me@kewbit.org // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:haveno/profobuf_models.dart'; import 'package:haveno_app/main.dart'; import 'package:haveno_app/views/screens/dispute_chat_screen.dart'; import 'package:haveno_app/views/screens/home_screen.dart'; import 'package:haveno_app/views/screens/trade_chat_screen.dart'; import 'package:timezone/timezone.dart' as tz; import 'package:timezone/data/latest_all.dart' as tz; class LocalNotificationsService { final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); final int foregroundServiceNotificationId = 888; bool _initialized = false; static final LocalNotificationsService _instance = LocalNotificationsService._internal(); factory LocalNotificationsService() => _instance; LocalNotificationsService._internal(); Future init() async { if (_initialized) return; // Prevent re-initialization _initialized = true; print("Initializing Timezones..."); tz.initializeTimeZones(); print("Timezones Initialized."); const LinuxInitializationSettings initializationSettingsLinux = LinuxInitializationSettings(defaultActionName: 'Haveno'); const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); const DarwinInitializationSettings initializationSettingsDarwin = DarwinInitializationSettings( requestAlertPermission: true, requestBadgePermission: true, requestSoundPermission: true, ); const DarwinInitializationSettings macOsInitializationSettingsDarwin = DarwinInitializationSettings( requestAlertPermission: false, requestBadgePermission: false, requestSoundPermission: false, ); const InitializationSettings initializationSettings = InitializationSettings( android: initializationSettingsAndroid, iOS: initializationSettingsDarwin, macOS: macOsInitializationSettingsDarwin, linux: initializationSettingsLinux ); print("Initializing FlutterLocalNotificationsPlugin..."); await _flutterLocalNotificationsPlugin.initialize( initializationSettings, onDidReceiveNotificationResponse: _onNotificationResponse, ); print("FlutterLocalNotificationsPlugin Initialized."); } Future showForegroundServiceNotification({ required String title, required String body, String? payload, }) async { const AndroidNotificationDetails androidPlatformChannelSpecifics = AndroidNotificationDetails( 'haveno', 'Haveno Plus Service', channelDescription: 'Haveno service running in the background', importance: Importance.max, priority: Priority.high, ticker: 'ticker', ongoing: true, ); const DarwinNotificationDetails iOSPlatformChannelSpecifics = DarwinNotificationDetails(); const DarwinNotificationDetails macOSPlatformChannelSpecifics = DarwinNotificationDetails(); const NotificationDetails platformChannelSpecifics = NotificationDetails( android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics, macOS: macOSPlatformChannelSpecifics ); await _flutterLocalNotificationsPlugin.show( foregroundServiceNotificationId, title, body, platformChannelSpecifics, payload: payload, ); } Future updateForegroundServiceNotification({ required String title, required String body, String? payload, }) async { await showForegroundServiceNotification(title: title, body: body, payload: payload); } Future showNotification({ required int id, required String title, required String body, String? payload, }) async { const AndroidNotificationDetails androidPlatformChannelSpecifics = AndroidNotificationDetails( 'haveno_notifications', 'Haveno Plus Notifications', channelDescription: 'Notifications for Haveno Plus events', importance: Importance.max, priority: Priority.high, ticker: 'ticker', ); const DarwinNotificationDetails iOSPlatformChannelSpecifics = DarwinNotificationDetails(); const DarwinNotificationDetails macOSPlatformChannelSpecifics = DarwinNotificationDetails( presentAlert: true, presentSound: true ); const NotificationDetails platformChannelSpecifics = NotificationDetails( android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics, macOS: macOSPlatformChannelSpecifics ); await _flutterLocalNotificationsPlugin.show( id, title, body, platformChannelSpecifics, payload: payload, ); } Future scheduleNotification({ required int id, required String title, required String body, required DateTime scheduledDate, String? payload, }) async { final tz.TZDateTime tzScheduledDate = tz.TZDateTime.from(scheduledDate, tz.local); const AndroidNotificationDetails androidPlatformChannelSpecifics = AndroidNotificationDetails( 'haveno_notifications', 'Haveno Plus Notifications', channelDescription: 'Notifications for Haveno Plus events', importance: Importance.max, priority: Priority.high, ticker: 'ticker', ); const DarwinNotificationDetails iOSPlatformChannelSpecifics = DarwinNotificationDetails(); const NotificationDetails platformChannelSpecifics = NotificationDetails( android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics, ); await _flutterLocalNotificationsPlugin.zonedSchedule( id, title, body, tzScheduledDate, platformChannelSpecifics, payload: payload, androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, uiLocalNotificationDateInterpretation: UILocalNotificationDateInterpretation.absoluteTime, ); } Future cancelNotification(int id) async { await _flutterLocalNotificationsPlugin.cancel(id); } Future cancelAllNotifications() async { await _flutterLocalNotificationsPlugin.cancelAll(); } void _onNotificationResponse(NotificationResponse notificationResponse) async { print('Notification tapped with payload: ${notificationResponse.payload}'); var object = jsonDecode(notificationResponse.payload!); switch (object['action']) { case 'route_to_chat_screen': //print(object['chateProtobufAsJson']); var chatMessage = ChatMessage()..mergeFromProto3Json(jsonDecode(object['chatMessageProtobufAsJson'])); if (chatMessage.tradeId.isNotEmpty) { if (chatMessage.type != SupportType.TRADE) { navigatorKey.currentState?.push( MaterialPageRoute(builder: (context) => DisputeChatScreen(tradeId: chatMessage.tradeId)) ); } else { navigatorKey.currentState?.push( MaterialPageRoute(builder: (context) => TradeChatScreen(tradeId: chatMessage.tradeId)) ); } break; } case 'route_to_active_trades_screen': navigatorKey.currentState?.push( MaterialPageRoute(builder: (context) => HomeScreen(initialIndex: 3)) ); break; default: print('Unknown action: ${object['action']}'); break; } } }