update sound when receive from FCM in foreground, background and terminate
This commit is contained in:
BIN
android/app/src/main/res/raw/new_alarm.mp3
Normal file
BIN
android/app/src/main/res/raw/new_alarm.mp3
Normal file
Binary file not shown.
BIN
android/app/src/main/res/raw/normal.mp3
Normal file
BIN
android/app/src/main/res/raw/normal.mp3
Normal file
Binary file not shown.
BIN
android/app/src/main/res/raw/success_alert.mp3
Normal file
BIN
android/app/src/main/res/raw/success_alert.mp3
Normal file
Binary file not shown.
BIN
android/app/src/main/res/raw/welcome.mp3
Normal file
BIN
android/app/src/main/res/raw/welcome.mp3
Normal file
Binary file not shown.
BIN
assets/sounds/new_alarm.wav
Normal file
BIN
assets/sounds/new_alarm.wav
Normal file
Binary file not shown.
BIN
assets/sounds/success_alert.wav
Normal file
BIN
assets/sounds/success_alert.wav
Normal file
Binary file not shown.
@@ -192,6 +192,8 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
LocaleManager.instance
|
||||
.setString(PreferencesKeys.ROLE, loginModel.role!);
|
||||
context.goNamed(AppRoutes.HOME.name);
|
||||
// context.goNamed("notification");
|
||||
|
||||
} else {
|
||||
showErrorTopSnackBarCustom(
|
||||
context, appLocalization(context).login_incorrect_usernameOrPass);
|
||||
@@ -214,6 +216,7 @@ class _LoginScreenState extends State<LoginScreen> {
|
||||
int timeNow = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
if (token != "" && (exp - timeNow) > 7200) {
|
||||
context.goNamed(AppRoutes.HOME.name);
|
||||
// context.goNamed("notification");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import 'dart:developer';
|
||||
// ignore_for_file: avoid_print
|
||||
|
||||
import 'dart:math' as math;
|
||||
import 'dart:developer' as dev;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'notification_bloc.dart';
|
||||
import '../../product/base/bloc/base_bloc.dart';
|
||||
import '../../product/services/notification_services.dart';
|
||||
@@ -14,11 +18,12 @@ class NotificationScreen extends StatefulWidget {
|
||||
|
||||
class _NotificationScreenState extends State<NotificationScreen> {
|
||||
late NotificationBloc notificationBloc;
|
||||
NotificationServices notificationServices = NotificationServices();
|
||||
final notificationPlugin = FlutterLocalNotificationsPlugin();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initNotification();
|
||||
notificationBloc = BlocProvider.of<NotificationBloc>(context);
|
||||
}
|
||||
|
||||
@@ -30,11 +35,43 @@ class _NotificationScreenState extends State<NotificationScreen> {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
log("Token: ${notificationServices.getDeviceToken()}");
|
||||
},
|
||||
child: Text("Get Notification Token"))
|
||||
onPressed: () async {
|
||||
showNewAlarmSoundNotification("warning_alarm");
|
||||
dev.log("Da vao day");
|
||||
},
|
||||
child: const Text("Show new Alarm Notification"),
|
||||
),
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
Future<void> initNotification() async{
|
||||
const isSettingAndroid = AndroidInitializationSettings('@mipmap/ic_launcher');
|
||||
const initSetting = InitializationSettings(android: isSettingAndroid);
|
||||
await notificationPlugin.initialize(initSetting);
|
||||
|
||||
}
|
||||
|
||||
Future<void> showNewAlarmSoundNotification(String sound) async {
|
||||
AndroidNotificationChannel androidNotificationChannel =
|
||||
AndroidNotificationChannel(
|
||||
math.Random.secure().nextInt(1000000).toString(),
|
||||
'high Important Notification',
|
||||
importance: Importance.max);
|
||||
AndroidNotificationDetails androidNotificationDetails =
|
||||
AndroidNotificationDetails(
|
||||
"androidNotificationChannel.id.toString()",
|
||||
androidNotificationChannel.name.toString(),
|
||||
sound: RawResourceAndroidNotificationSound(sound),
|
||||
channelDescription: "Channel description",
|
||||
importance: androidNotificationChannel.importance,
|
||||
priority: Priority.high,
|
||||
ticker: 'ticker',
|
||||
);
|
||||
|
||||
final NotificationDetails notificationDetails =
|
||||
NotificationDetails(android: androidNotificationDetails);
|
||||
await FlutterLocalNotificationsPlugin().show(0, 'New Notification',
|
||||
'Sound Notification Example', notificationDetails);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'product/services/notification_services.dart';
|
||||
import 'product/services/theme_services.dart';
|
||||
import 'product/services/language_services.dart';
|
||||
import 'bloc/main_bloc.dart';
|
||||
@@ -10,6 +14,10 @@ import 'product/constant/navigation/navigation_router.dart';
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await Firebase.initializeApp();
|
||||
FirebaseMessaging
|
||||
.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||
NotificationServices().setupInteractMessage();
|
||||
|
||||
runApp(
|
||||
BlocProvider(
|
||||
child: const MyApp(),
|
||||
@@ -17,6 +25,16 @@ void main() async {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
log("Full background message payload: ${message.toMap()}");
|
||||
await Firebase.initializeApp();
|
||||
final notificationServices = NotificationServices();
|
||||
await notificationServices.initLocalNotifications();
|
||||
await notificationServices.showNotification(message);
|
||||
log("Background message handled: ${message.data['title']}");
|
||||
}
|
||||
class MyApp extends StatefulWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@@ -39,6 +57,7 @@ class _MyAppState extends State<MyApp> {
|
||||
late MainBloc mainBloc;
|
||||
LanguageServices languageServices = LanguageServices();
|
||||
ThemeServices themeServices = ThemeServices();
|
||||
final NotificationServices notificationServices = NotificationServices();
|
||||
|
||||
setLocale(Locale locale) {
|
||||
_locale = locale;
|
||||
@@ -49,11 +68,16 @@ class _MyAppState extends State<MyApp> {
|
||||
_themeData = theme;
|
||||
mainBloc.sinkTheme.add(_themeData);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
mainBloc = BlocProvider.of(context);
|
||||
notificationServices.initLocalNotifications();
|
||||
notificationServices.firebaseInit(context);
|
||||
// notificationServices.setupInteractMessage();
|
||||
notificationServices.getDeviceToken().then((token){
|
||||
print("Firebase Token: $token");
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:sfm_app/feature/sound_notification_test/notification_bloc.dart';
|
||||
import 'package:sfm_app/feature/sound_notification_test/notification_screen.dart';
|
||||
import '../../../feature/sound_notification_test/notification_bloc.dart';
|
||||
import '../../../feature/sound_notification_test/notification_screen.dart';
|
||||
import '../../../bloc/device_detail_bloc.dart';
|
||||
import '../../../feature/devices/device_detail/device_detail_screen.dart';
|
||||
import '../../../bloc/device_notification_settings_bloc.dart';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'dart:developer' as dev;
|
||||
import 'dart:math' as math;
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
@@ -8,15 +6,34 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
|
||||
class NotificationServices {
|
||||
FirebaseMessaging messaging = FirebaseMessaging.instance;
|
||||
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||
|
||||
void firebaseInit(BuildContext context) async {
|
||||
Future<void> initLocalNotifications() async {
|
||||
const AndroidInitializationSettings androidInitializationSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
|
||||
const DarwinInitializationSettings iosInitializationSettings = DarwinInitializationSettings();
|
||||
const InitializationSettings initializationSettings = InitializationSettings(
|
||||
android: androidInitializationSettings,
|
||||
iOS: iosInitializationSettings,
|
||||
);
|
||||
|
||||
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);
|
||||
},
|
||||
);
|
||||
dev.log("Local notifications initialized");
|
||||
}
|
||||
|
||||
void firebaseInit(BuildContext context) {
|
||||
FirebaseMessaging.onMessage.listen((message) {
|
||||
initNotifications(context, message);
|
||||
showNotification(message);
|
||||
dev.log(
|
||||
"Title: ${message.notification!.title}, Body: ${message.notification!.body} from ${message.sentTime}");
|
||||
dev.log("Foreground message payload: ${message.toMap()}");
|
||||
if (WidgetsBinding.instance != null) {
|
||||
showNotification(message);
|
||||
} else {
|
||||
dev.log("App is in background, skipping foreground notification");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -25,80 +42,116 @@ class NotificationServices {
|
||||
return token!;
|
||||
}
|
||||
|
||||
void isTokenRefresh() async {
|
||||
void isTokenRefresh() {
|
||||
messaging.onTokenRefresh.listen((newToken) {
|
||||
dev.log("Refresh Firebase Messaging Token: $newToken");
|
||||
});
|
||||
}
|
||||
|
||||
void initNotifications(BuildContext context, RemoteMessage message) async {
|
||||
var androidInitializationSettings =
|
||||
const AndroidInitializationSettings('app_icon');
|
||||
var iosInitializationSettings = const DarwinInitializationSettings();
|
||||
var initializationSettings = InitializationSettings(
|
||||
android: androidInitializationSettings, iOS: iosInitializationSettings);
|
||||
await _flutterLocalNotificationsPlugin.initialize(initializationSettings,
|
||||
onDidReceiveNotificationResponse: (payload) {
|
||||
handleMessage(context, message);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> showNotification(RemoteMessage message) async {
|
||||
AndroidNotificationChannel androidNotificationChannel =
|
||||
AndroidNotificationChannel(
|
||||
math.Random.secure().nextInt(1000000).toString(),
|
||||
'high Important Notification',
|
||||
importance: Importance.max);
|
||||
AndroidNotificationDetails androidNotificationDetails =
|
||||
AndroidNotificationDetails(
|
||||
androidNotificationChannel.id.toString(),
|
||||
androidNotificationChannel.name.toString(),
|
||||
sound: RawResourceAndroidNotificationSound(message.data['sound']),
|
||||
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;
|
||||
}
|
||||
|
||||
AndroidNotificationChannel androidNotificationChannel = AndroidNotificationChannel(
|
||||
math.Random.secure().nextInt(1000000).toString(),
|
||||
'High Importance Notification',
|
||||
importance: Importance.max,
|
||||
);
|
||||
|
||||
final androidPlugin = _flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>();
|
||||
await androidPlugin?.deleteNotificationChannel(androidNotificationChannel.id);
|
||||
|
||||
AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails(
|
||||
androidNotificationChannel.id,
|
||||
androidNotificationChannel.name,
|
||||
channelDescription: "Channel description",
|
||||
sound: getSound(type),
|
||||
importance: androidNotificationChannel.importance,
|
||||
priority: Priority.high,
|
||||
ticker: 'ticker',
|
||||
actions: type == "warn1"
|
||||
? [
|
||||
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
|
||||
);
|
||||
|
||||
const DarwinNotificationDetails darwinNotificationDetails =
|
||||
DarwinNotificationDetails(
|
||||
const DarwinNotificationDetails darwinNotificationDetails = DarwinNotificationDetails(
|
||||
presentAlert: true,
|
||||
presentBadge: true,
|
||||
presentBanner: true,
|
||||
presentSound: true,
|
||||
);
|
||||
|
||||
NotificationDetails notificationDetails = NotificationDetails(
|
||||
android: androidNotificationDetails, iOS: darwinNotificationDetails);
|
||||
Future.delayed(Duration.zero, () {
|
||||
_flutterLocalNotificationsPlugin.show(0, message.notification!.title!,
|
||||
message.notification!.body, notificationDetails);
|
||||
});
|
||||
android: androidNotificationDetails,
|
||||
iOS: darwinNotificationDetails,
|
||||
);
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
||||
void handleMessage(BuildContext context, RemoteMessage message) async {
|
||||
if (message.data['type'] == "msj") {
|
||||
// Navigator.push(context,
|
||||
// MaterialPageRoute(builder: (context) => const MessageScreen()));
|
||||
} else if (message.data['type'] == "warn") {
|
||||
// Navigator.push(
|
||||
// context, MaterialPageRoute(builder: (context) => const MapScreen()));
|
||||
AndroidNotificationSound getSound(String type) {
|
||||
if (type == "welcome") {
|
||||
return const RawResourceAndroidNotificationSound("welcome");
|
||||
} else if (type == "success") {
|
||||
return const RawResourceAndroidNotificationSound("success_alert");
|
||||
} else if (type == "warn1") {
|
||||
return const RawResourceAndroidNotificationSound("warning_alarm");
|
||||
} else if (type == "warn2") {
|
||||
return const RawResourceAndroidNotificationSound("new_alarm");
|
||||
} else {
|
||||
dev.log("Not found data");
|
||||
return const RawResourceAndroidNotificationSound("normal");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setupInteractMessage(BuildContext context) async {
|
||||
// When app terminate
|
||||
RemoteMessage? initialMessage =
|
||||
await FirebaseMessaging.instance.getInitialMessage();
|
||||
if (initialMessage != null) {
|
||||
// showNotification(initialMessage)
|
||||
handleMessage(context, initialMessage);
|
||||
}
|
||||
void handleMessage(String? payload) {
|
||||
dev.log("Handling notification tap with payload: $payload");
|
||||
// Thêm logic xử lý khi nhấn thông báo ở đây
|
||||
// Ví dụ: Điều hướng màn hình hoặc xử lý dữ liệu
|
||||
}
|
||||
|
||||
// When app is inBackGround
|
||||
Future<void> setupInteractMessage() async {
|
||||
// Khi app terminated
|
||||
RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
|
||||
|
||||
if (initialMessage != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
try {
|
||||
handleMessage(initialMessage.data['type']);
|
||||
} catch (e, stack) {
|
||||
dev.log("Error handling initial message: $e\n$stack");
|
||||
}
|
||||
});
|
||||
}
|
||||
// Khi app ở background
|
||||
FirebaseMessaging.onMessageOpenedApp.listen((message) {
|
||||
handleMessage(context, message);
|
||||
try {
|
||||
handleMessage(message.data['type']);
|
||||
} catch (e, stack) {
|
||||
dev.log("Error in onMessageOpenedApp: $e\n$stack");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,6 +92,7 @@ flutter:
|
||||
- assets/images/
|
||||
- assets/icons/
|
||||
- assets/map_themes/
|
||||
- assets/sounds/
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||
|
||||
Reference in New Issue
Block a user