Update alarm notification when app in foreground, background and terminate
This commit is contained in:
@@ -1,117 +1,162 @@
|
||||
import 'dart:developer' as dev;
|
||||
import 'dart:io';
|
||||
import 'dart:math' as math;
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:alarm/alarm.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart' hide NotificationSettings;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
||||
import 'package:sfm_app/product/utils/app_logger_utils.dart';
|
||||
|
||||
import '../../firebase_options.dart';
|
||||
import '../../main.dart';
|
||||
import 'alarm_services.dart';
|
||||
|
||||
class NotificationServices {
|
||||
FirebaseMessaging messaging = FirebaseMessaging.instance;
|
||||
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||
static final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||
final FirebaseMessaging _messaging = FirebaseMessaging.instance;
|
||||
AlarmServices alarmServices = AlarmServices();
|
||||
|
||||
Future<void> initLocalNotifications(PersistentTabController controller) async {
|
||||
const AndroidInitializationSettings androidInitializationSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
|
||||
const DarwinInitializationSettings iosInitializationSettings = DarwinInitializationSettings();
|
||||
const InitializationSettings initializationSettings = InitializationSettings(
|
||||
android: androidInitializationSettings,
|
||||
iOS: iosInitializationSettings,
|
||||
);
|
||||
Future<void> initialize() async {
|
||||
await initializeLocalNotifications();
|
||||
dev.log("NotificationService initialized");
|
||||
}
|
||||
|
||||
await _flutterLocalNotificationsPlugin.initialize(
|
||||
initializationSettings,
|
||||
onDidReceiveNotificationResponse: (NotificationResponse response) {
|
||||
dev.log("Người dùng click thông báo ở foreground với payload: ${response.payload}");
|
||||
handleMessage(response.payload,controller);
|
||||
},
|
||||
);
|
||||
dev.log("Local notifications initialized");
|
||||
Future<void> initializeLocalNotifications() async {
|
||||
try{
|
||||
const AndroidInitializationSettings androidInitializationSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
|
||||
const DarwinInitializationSettings iosInitializationSettings = DarwinInitializationSettings();
|
||||
const InitializationSettings initializationSettings = InitializationSettings(
|
||||
android: androidInitializationSettings,
|
||||
iOS: iosInitializationSettings,
|
||||
);
|
||||
|
||||
await _notificationsPlugin.initialize(
|
||||
initializationSettings,
|
||||
onDidReceiveNotificationResponse: (NotificationResponse response) {
|
||||
dev.log("Người dùng click thông báo ở foreground với payload: ${response.payload}");
|
||||
handleMessage(response.payload,controller);
|
||||
},
|
||||
);
|
||||
dev.log("Local notifications initialized");
|
||||
}catch(e){
|
||||
dev.log("Error initializing local notifications: $e");
|
||||
}
|
||||
}
|
||||
|
||||
void firebaseInit(BuildContext context) {
|
||||
FirebaseMessaging.onMessage.listen((message) {
|
||||
dev.log("Foreground message payload: ${message.toMap()}");
|
||||
showNotification(message);
|
||||
});
|
||||
}
|
||||
|
||||
Future<String> getDeviceToken() async {
|
||||
print("GET FB TOKEN");
|
||||
String? token = await messaging.getToken();
|
||||
print("GET FB: ${token}");
|
||||
return token!;
|
||||
_showNotification(message);
|
||||
});
|
||||
}
|
||||
|
||||
void isTokenRefresh() {
|
||||
messaging.onTokenRefresh.listen((newToken) {
|
||||
_messaging.onTokenRefresh.listen((newToken) {
|
||||
dev.log("Refresh Firebase Messaging Token: $newToken");
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> showNotification(RemoteMessage message) async {
|
||||
dev.log(message.toString());
|
||||
dev.log(message.data.toString());
|
||||
dev.log(message.data["notification"].toString());
|
||||
String? title = message.data['title'];
|
||||
String? body = message.data['body'];
|
||||
String type = message.data['type'] ?? "normal";
|
||||
|
||||
if (title == null || body == null) {
|
||||
dev.log("Skipping notification due to missing title or body");
|
||||
return;
|
||||
static Future<bool?> requestNotificationPermission() async {
|
||||
try {
|
||||
if (Platform.isAndroid) {
|
||||
return await _notificationsPlugin
|
||||
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
|
||||
?.requestNotificationsPermission();
|
||||
} else if (Platform.isIOS) {
|
||||
return await _notificationsPlugin
|
||||
.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()
|
||||
?.requestPermissions(alert: true, sound: true, badge: true);
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
dev.log("Error requesting notification permission: $e");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
AndroidNotificationChannel androidNotificationChannel = AndroidNotificationChannel(
|
||||
math.Random.secure().nextInt(1000000).toString(),
|
||||
'High Importance Notification',
|
||||
importance: Importance.max,
|
||||
);
|
||||
Future<void> _showNotification(RemoteMessage message) async {
|
||||
try {
|
||||
// Early validation of notification data
|
||||
final title = message.data['title'] as String?;
|
||||
final body = message.data['body'] as String?;
|
||||
final type = message.data['type'] as String? ?? 'normal';
|
||||
|
||||
final androidPlugin = _flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>();
|
||||
await androidPlugin?.deleteNotificationChannel(androidNotificationChannel.id);
|
||||
if (title == null || body == null) {
|
||||
dev.log('Skipping notification: missing title or body', name: 'Notification');
|
||||
return;
|
||||
}
|
||||
|
||||
AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails(
|
||||
androidNotificationChannel.id,
|
||||
androidNotificationChannel.name,
|
||||
channelDescription: "Channel description",
|
||||
sound: getSound(type),
|
||||
importance: androidNotificationChannel.importance,
|
||||
priority: Priority.high,
|
||||
ticker: 'ticker',
|
||||
actions: type == "smoke_warning"
|
||||
? [
|
||||
const AndroidNotificationAction(
|
||||
"id1",
|
||||
"Hogg xóa được",
|
||||
// true thì khi nhấn vào button sẽ mở giao diện ra
|
||||
showsUserInterface: true,
|
||||
cancelNotification: false,
|
||||
)
|
||||
]
|
||||
: null
|
||||
);
|
||||
// Handle smoke warning notifications
|
||||
if (type == 'smoke_warning') {
|
||||
await alarmServices.showAlarm(title, body);
|
||||
dev.log('Displayed smoke warning notification', name: 'Notification');
|
||||
return;
|
||||
}
|
||||
|
||||
const DarwinNotificationDetails darwinNotificationDetails = DarwinNotificationDetails(
|
||||
presentAlert: true,
|
||||
presentBadge: true,
|
||||
presentBanner: true,
|
||||
presentSound: true,
|
||||
);
|
||||
// Create notification channel
|
||||
final channelId = math.Random.secure().nextInt(1000000).toString();
|
||||
const channelName = 'High Importance Notification';
|
||||
const channelDescription = 'Channel description';
|
||||
|
||||
NotificationDetails notificationDetails = NotificationDetails(
|
||||
android: androidNotificationDetails,
|
||||
iOS: darwinNotificationDetails,
|
||||
);
|
||||
final androidChannel = AndroidNotificationChannel(
|
||||
channelId,
|
||||
channelName,
|
||||
importance: Importance.max,
|
||||
description: channelDescription,
|
||||
);
|
||||
|
||||
// Truyền payload vào thông báo
|
||||
String payload = message.data['type'] ?? "default";
|
||||
await _flutterLocalNotificationsPlugin.show(
|
||||
math.Random.secure().nextInt(1000000),
|
||||
title,
|
||||
body,
|
||||
notificationDetails,
|
||||
payload: payload,
|
||||
);
|
||||
dev.log("Displayed notification with title: $title, body: $body, type: $type");
|
||||
final androidPlugin = _notificationsPlugin
|
||||
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>();
|
||||
|
||||
// Delete existing channel to prevent conflicts
|
||||
await androidPlugin?.deleteNotificationChannel(channelId);
|
||||
|
||||
// Configure notification details
|
||||
final androidDetails = AndroidNotificationDetails(
|
||||
channelId,
|
||||
channelName,
|
||||
channelDescription: channelDescription,
|
||||
sound: getSound(type),
|
||||
importance: Importance.max,
|
||||
priority: Priority.high,
|
||||
ticker: 'ticker',
|
||||
);
|
||||
|
||||
const iosDetails = DarwinNotificationDetails(
|
||||
presentAlert: true,
|
||||
presentBadge: true,
|
||||
presentBanner: true,
|
||||
presentSound: true,
|
||||
);
|
||||
|
||||
final notificationDetails = NotificationDetails(
|
||||
android: androidDetails,
|
||||
iOS: iosDetails,
|
||||
);
|
||||
|
||||
// Show notification
|
||||
await _notificationsPlugin.show(
|
||||
int.parse(channelId),
|
||||
title,
|
||||
body,
|
||||
notificationDetails,
|
||||
payload: type,
|
||||
);
|
||||
|
||||
dev.log(
|
||||
'Displayed notification - title: $title, body: $body, type: $type',
|
||||
name: 'Notification',
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
dev.log(
|
||||
'Failed to show notification: $e',
|
||||
name: 'Notification',
|
||||
error: e,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AndroidNotificationSound getSound(String type) {
|
||||
@@ -155,4 +200,120 @@ class NotificationServices {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> showBackgroundOrTerminateNotification(RemoteMessage message) async {
|
||||
try {
|
||||
// Early validation of notification data
|
||||
final title = message.data['title'] as String?;
|
||||
final body = message.data['body'] as String?;
|
||||
final type = message.data['type'] as String? ?? 'normal';
|
||||
|
||||
if (title == null || body == null) {
|
||||
dev.log('Skipping notification: missing title or body', name: 'Notification');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create notification channel
|
||||
final channelId = math.Random.secure().nextInt(1000000).toString();
|
||||
const channelName = 'High Importance Notification';
|
||||
const channelDescription = 'Channel description';
|
||||
|
||||
|
||||
// Configure notification details
|
||||
final androidDetails = AndroidNotificationDetails(
|
||||
channelId,
|
||||
channelName,
|
||||
channelDescription: channelDescription,
|
||||
sound: getSound(type),
|
||||
importance: Importance.max,
|
||||
priority: Priority.high,
|
||||
ticker: 'ticker',
|
||||
);
|
||||
|
||||
const iosDetails = DarwinNotificationDetails(
|
||||
presentAlert: true,
|
||||
presentBadge: true,
|
||||
presentBanner: true,
|
||||
presentSound: true,
|
||||
);
|
||||
|
||||
final notificationDetails = NotificationDetails(
|
||||
android: androidDetails,
|
||||
iOS: iosDetails,
|
||||
);
|
||||
|
||||
// Show notification
|
||||
await _notificationsPlugin.show(
|
||||
int.parse(channelId),
|
||||
title,
|
||||
body,
|
||||
notificationDetails,
|
||||
payload: type,
|
||||
);
|
||||
|
||||
dev.log(
|
||||
'Displayed notification - title: $title, body: $body, type: $type',
|
||||
name: 'Notification',
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
dev.log(
|
||||
'Failed to show notification: $e',
|
||||
name: 'Notification',
|
||||
error: e,
|
||||
stackTrace: stackTrace,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
try {
|
||||
AppLoggerUtils.warning("Background handler started: ${message.data}");
|
||||
await Alarm.init();
|
||||
if (Firebase.apps.isEmpty) {
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform,
|
||||
name: "sfm-notification"
|
||||
);
|
||||
AppLoggerUtils.warning("Firebase đã được khởi tạo trong background handler");
|
||||
} else {
|
||||
AppLoggerUtils.warning("Firebase đã được khởi tạo trước đó");
|
||||
}
|
||||
AppLoggerUtils.warning(message.toString());
|
||||
AppLoggerUtils.warning(message.data.toString());
|
||||
AppLoggerUtils.warning(message.data["notification"].toString());
|
||||
String? title = message.data['title'];
|
||||
String? body = message.data['body'];
|
||||
String type = message.data['type'] ?? "normal";
|
||||
final notificationService = NotificationServices();
|
||||
await notificationService.initialize();
|
||||
final alarmSettings = AlarmSettings(
|
||||
id: 42,
|
||||
dateTime: DateTime.now(),
|
||||
assetAudioPath: 'assets/sounds/warning_alarm.mp3',
|
||||
loopAudio: true,
|
||||
vibrate: true,
|
||||
warningNotificationOnKill: Platform.isIOS,
|
||||
androidFullScreenIntent: true,
|
||||
volumeSettings: VolumeSettings.fade(
|
||||
volume: 0.8,
|
||||
fadeDuration: const Duration(seconds: 5),
|
||||
volumeEnforced: true,
|
||||
),
|
||||
notificationSettings: NotificationSettings(
|
||||
title: title ?? "SFM",
|
||||
body: body ?? "",
|
||||
stopButton: 'Dừng thông báo',
|
||||
icon: "ic_launcher",
|
||||
),
|
||||
);
|
||||
if (type == "smoke_warning") {
|
||||
await Alarm.set(alarmSettings: alarmSettings);
|
||||
} else {
|
||||
await notificationService.showBackgroundOrTerminateNotification(message);
|
||||
}
|
||||
} catch (e) {
|
||||
AppLoggerUtils.warning("Error in background handler: $e");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user