275 lines
9.1 KiB
Dart
275 lines
9.1 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'package:f_r_e_hub/app_state.dart';
|
|
import 'package:f_r_e_hub/backend/api_requests/api_calls.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
import 'package:rxdart/rxdart.dart';
|
|
import 'package:f_r_e_hub/components/templates_components/visit_request_template_component/visit_request_template_component_widget.dart';
|
|
|
|
class PushNotificationService {
|
|
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
|
|
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
|
|
FlutterLocalNotificationsPlugin();
|
|
final Subject<RemoteMessage> _onMessage = BehaviorSubject<RemoteMessage>();
|
|
final BehaviorSubject<BuildContext> _context =
|
|
BehaviorSubject<BuildContext>();
|
|
|
|
PushNotificationService() {
|
|
_initializeLocalNotifications(_context);
|
|
}
|
|
|
|
Future<void> initialize(BuildContext context) async {
|
|
_context.add(context);
|
|
await _requestPermissions();
|
|
_listenToForegroundMessages(context);
|
|
_listenToBackgroundMessages();
|
|
_listenToNotificationClicks(context);
|
|
await _updateDeviceToken();
|
|
}
|
|
|
|
Future<void> _requestPermissions() async {
|
|
NotificationSettings settings = await _firebaseMessaging.requestPermission(
|
|
alert: true,
|
|
badge: true,
|
|
sound: true,
|
|
);
|
|
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
|
|
debugPrint('User granted permission');
|
|
} else {
|
|
debugPrint('User declined or has not accepted permission');
|
|
}
|
|
}
|
|
|
|
Map<String, dynamic> validJsonFromString(String? string) {
|
|
// Switch(string != null || string.isNotEmpty) {
|
|
// case true:
|
|
// debugPrint()
|
|
// break;
|
|
// }
|
|
String stringValidate = string!
|
|
.replaceAllMapped(RegExp(r'(\w+):'),
|
|
(match) => '"${match[1]}":') // Enclose keys in double quotes
|
|
.replaceAllMapped(RegExp(r':\s*(\w+)'), (match) => ': "${match[1]}"');
|
|
Map<String, dynamic> json = jsonDecode(stringValidate);
|
|
return json;
|
|
}
|
|
|
|
void _initializeLocalNotifications(
|
|
BehaviorSubject<BuildContext> context) async {
|
|
while (context.valueOrNull == null) {
|
|
await Future.delayed(Duration(milliseconds: 100));
|
|
}
|
|
|
|
var initializationSettingsAndroid =
|
|
AndroidInitializationSettings('mipmap/ic_fre_black');
|
|
var initializationSettingsIOS = DarwinInitializationSettings(
|
|
requestAlertPermission: true,
|
|
requestBadgePermission: true,
|
|
requestSoundPermission: true,
|
|
);
|
|
var initializationSettings = InitializationSettings(
|
|
android: initializationSettingsAndroid,
|
|
iOS: initializationSettingsIOS,
|
|
);
|
|
_flutterLocalNotificationsPlugin.initialize(
|
|
initializationSettings,
|
|
onDidReceiveNotificationResponse: (NotificationResponse response) async {
|
|
debugPrint('Response payload:${response.payload}');
|
|
if (response.payload != null) {
|
|
// Preprocess the payload to ensure it's in a valid JSON format
|
|
String validJsonPayload = response.payload!
|
|
.replaceAllMapped(RegExp(r'(\w+):'),
|
|
(match) => '"${match[1]}":') // Enclose keys in double quotes
|
|
.replaceAllMapped(
|
|
RegExp(r':\s*(\w+)'),
|
|
(match) =>
|
|
': "${match[1]}"'); // Enclose string values in double quotes
|
|
|
|
try {
|
|
Map<String, dynamic> message = jsonDecode(validJsonPayload);
|
|
debugPrint('Notification payload: $message');
|
|
_handleNotificationClick(message);
|
|
} catch (e) {
|
|
debugPrint('Error decoding notification payload: $e');
|
|
}
|
|
}
|
|
},
|
|
);
|
|
_createNotificationChannel();
|
|
}
|
|
|
|
void _createNotificationChannel() {
|
|
_flutterLocalNotificationsPlugin
|
|
.resolvePlatformSpecificImplementation<
|
|
AndroidFlutterLocalNotificationsPlugin>()
|
|
?.createNotificationChannel(
|
|
AndroidNotificationChannel(
|
|
'channelID',
|
|
'channelName',
|
|
description: 'Channel Description',
|
|
importance: Importance.max,
|
|
),
|
|
);
|
|
}
|
|
|
|
void _listenToForegroundMessages(BuildContext context) {
|
|
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
|
debugPrint('Got a message whilst in the foreground!');
|
|
debugPrint('Message data: ${message.toMap()}');
|
|
_onMessage.add(message);
|
|
_showNotification(message);
|
|
});
|
|
}
|
|
|
|
void _listenToBackgroundMessages() {
|
|
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
|
}
|
|
|
|
void _listenToNotificationClicks(BuildContext context) {
|
|
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
|
|
debugPrint('Notification clicked!');
|
|
|
|
_onMessage.add(message);
|
|
NotificationHandler().handleMessage(message.data, context);
|
|
});
|
|
}
|
|
|
|
Future<void> _updateDeviceToken() async {
|
|
final NotificationSettings settings =
|
|
await _requestNotificationPermission();
|
|
await _fetchAndLogApnsToken(settings);
|
|
|
|
final String? deviceToken = await FirebaseMessaging.instance.getToken();
|
|
if (deviceToken == null) {
|
|
debugPrint('Failed to get Firebase Messaging token');
|
|
return;
|
|
}
|
|
|
|
debugPrint('Push Messaging token: $deviceToken');
|
|
FFAppState().token = deviceToken;
|
|
|
|
final ApiCallResponse? updTokenResponse =
|
|
await _updateTokenOnServer(deviceToken);
|
|
if (_isTokenUpdateSuccessful(updTokenResponse)) {
|
|
debugPrint('Token updated successfully');
|
|
} else {
|
|
debugPrint('Error updating token');
|
|
}
|
|
}
|
|
|
|
Future<NotificationSettings> _requestNotificationPermission() async {
|
|
final NotificationSettings settings =
|
|
await _firebaseMessaging.requestPermission();
|
|
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
|
|
debugPrint('User granted permission');
|
|
} else {
|
|
debugPrint('User declined or has not accepted permission');
|
|
}
|
|
return settings;
|
|
}
|
|
|
|
Future<void> _fetchAndLogApnsToken(NotificationSettings settings) async {
|
|
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
|
|
final String? apnsToken = await _firebaseMessaging.getAPNSToken();
|
|
if (apnsToken != null) {
|
|
debugPrint('APNS Token: $apnsToken');
|
|
} else {
|
|
debugPrint('Failed to get APNS token');
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<ApiCallResponse?> _updateTokenOnServer(String deviceToken) async {
|
|
return await PhpGroup.updToken.call(
|
|
token: deviceToken,
|
|
devid: FFAppState().devUUID,
|
|
useruuid: FFAppState().userUUID,
|
|
);
|
|
}
|
|
|
|
bool _isTokenUpdateSuccessful(ApiCallResponse? response) {
|
|
return PhpGroup.updToken.error((response?.jsonBody ?? '')) == false;
|
|
}
|
|
|
|
void _showNotification(RemoteMessage message) async {
|
|
var androidDetails = AndroidNotificationDetails(
|
|
'channelID',
|
|
'channelName',
|
|
channelDescription: 'Channel Description',
|
|
importance: Importance.max,
|
|
priority: Priority.high,
|
|
);
|
|
var iOSDetails = DarwinNotificationDetails();
|
|
var generalNotificationDetails =
|
|
NotificationDetails(android: androidDetails, iOS: iOSDetails);
|
|
|
|
await _flutterLocalNotificationsPlugin.show(
|
|
message.hashCode,
|
|
message.notification?.title,
|
|
message.notification?.body,
|
|
generalNotificationDetails,
|
|
payload: message.data.toString(),
|
|
);
|
|
}
|
|
|
|
_handleNotificationClick(Map<String, dynamic> payload) {
|
|
switch (payload.isNotEmpty) {
|
|
case true:
|
|
// Print the 'data' property
|
|
debugPrint('Notification payload: $payload');
|
|
// Handle the message data as needed
|
|
NotificationHandler().handleMessage(payload, _context.value);
|
|
// Access the 'data' property of 'RemoteMessage'
|
|
case false:
|
|
debugPrint('Notification payload is empty');
|
|
// Handle the message notification as needed
|
|
break;
|
|
}
|
|
}
|
|
|
|
static Future<void> _firebaseMessagingBackgroundHandler(
|
|
RemoteMessage message) async {
|
|
debugPrint('Handling a background message: ${message.messageId}');
|
|
}
|
|
}
|
|
|
|
class NotificationHandler {
|
|
void handleMessage(Map<String, dynamic> message, BuildContext context) {
|
|
debugPrint('Notification Received!');
|
|
message.forEach((key, value) {
|
|
debugPrint('$key: $value');
|
|
});
|
|
|
|
switch (message['type']) {
|
|
case 'visit_request':
|
|
_showVisitRequestDialog(message, context);
|
|
break;
|
|
case 'visit_response':
|
|
debugPrint('visit_response');
|
|
break;
|
|
default:
|
|
debugPrint('Notification type not recognized');
|
|
}
|
|
}
|
|
|
|
void _showVisitRequestDialog(
|
|
Map<String, dynamic> message, BuildContext context) {
|
|
showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return Dialog(
|
|
backgroundColor: Colors.transparent,
|
|
child: VisitRequestTemplateComponentWidget(
|
|
name: message['nome'] ?? 'Unknown',
|
|
reason: message['motivo'] ?? 'Unknown',
|
|
message: message['mensagem'] ?? 'Unknown',
|
|
document: message['documento'] ?? 'Unknown',
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|