From a57cf381d61a73f3ce43131633e0361206053cfc Mon Sep 17 00:00:00 2001 From: Ivan Antunes Date: Wed, 24 Jul 2024 14:56:20 -0300 Subject: [PATCH] =?UTF-8?q?fix:=20Notifica=C3=A7=C3=A3o=20no=20iOS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Podfile.lock | 18 ++ ios/Runner.xcodeproj/project.pbxproj | 4 - ios/Runner/AppDelegate.swift | 10 ++ ios/Runner/Info.plist | 13 +- lib/backend/api_requests/api_calls.dart | 1 + .../pushNotificationService.dart | 162 +++++++++++++----- ...ation_modal_template_component_widget.dart | 18 +- 7 files changed, 172 insertions(+), 54 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 473970ce..20609413 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -160,6 +160,8 @@ PODS: - GoogleUtilities/Privacy - image_picker_ios (0.0.1): - Flutter + - local_auth_darwin (0.0.1): + - Flutter - nanopb (2.30910.0): - nanopb/decode (= 2.30910.0) - nanopb/encode (= 2.30910.0) @@ -169,10 +171,14 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS + - permission_handler_apple (9.3.0): + - Flutter - PromisesObjC (2.4.0) - SDWebImage (5.19.2): - SDWebImage/Core (= 5.19.2) - SDWebImage/Core (5.19.2) + - share_plus (0.0.1): + - Flutter - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -199,7 +205,10 @@ DEPENDENCIES: - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) + - local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) + - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite (from `.symlinks/plugins/sqflite/darwin`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) @@ -246,8 +255,14 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_secure_storage/ios" image_picker_ios: :path: ".symlinks/plugins/image_picker_ios/ios" + local_auth_darwin: + :path: ".symlinks/plugins/local_auth_darwin/darwin" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" + permission_handler_apple: + :path: ".symlinks/plugins/permission_handler_apple/ios" + share_plus: + :path: ".symlinks/plugins/share_plus/ios" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" sqflite: @@ -281,11 +296,14 @@ SPEC CHECKSUMS: GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 + local_auth_darwin: 4d56c90c2683319835a61274b57620df9c4520ab nanopb: 438bc412db1928dac798aa6fd75726007be04262 OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 SDWebImage: dfe95b2466a9823cf9f0c6d01217c06550d7b29a + share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 53560f07..9ace6cd2 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -41,8 +41,6 @@ 4C588A6A63D12FBFE8C3D586 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; 4C7A2C30DCF835BA60FAD235 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 50BE974D08F66282C0031620 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 6436409727A31CD000820AF7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - 6436409927A31CD100820AF7 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/InfoPlist.strings; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -301,8 +299,6 @@ 6436409C27A31CD800820AF7 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( - 6436409227A31CD600820AF7 /* pt */, - 6436409227A31CDA00820AF7 /* en */, ); name = InfoPlist.strings; sourceTree = ""; diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index f52eb22e..d1887b8a 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,6 +1,7 @@ import UIKit import Flutter +import flutter_local_notifications @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { @@ -8,6 +9,15 @@ import Flutter _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { + + FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in + GeneratedPluginRegistrant.register(with: registry) + } + + if #available(iOS 10.0, *) { + UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate + } + GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 928daf90..3aaec848 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -25,8 +25,6 @@ APPL CFBundleShortVersionString $(FLUTTER_BUILD_NAME) - ITSAppUsesNonExemptEncryption - CFBundleURLTypes @@ -42,10 +40,12 @@ CFBundleVersion $(FLUTTER_BUILD_NUMBER) - FirebaseAppDelegateProxyEnabled - + FirebaseAppDelegateProxyEnabled + FlutterDeepLinkingEnabled + ITSAppUsesNonExemptEncryption + NSAppTransportSecurity NSExceptionDomains @@ -63,6 +63,8 @@ NSCameraUsageDescription In order to take a picture or video, this app requires permission to access the camera. + NSFaceIDUsageDescription + Why is my app authenticating using face id? NSPhotoLibraryUsageDescription In order to upload data, this app requires permission to access the photo library. UIApplicationSupportsIndirectInputEvents @@ -70,6 +72,7 @@ UIBackgroundModes fetch + processing remote-notification UILaunchStoryboardName @@ -78,8 +81,6 @@ Main UIRequiresFullScreen - NSFaceIDUsageDescription - Why is my app authenticating using face id? UISupportedInterfaceOrientations UIInterfaceOrientationPortrait diff --git a/lib/backend/api_requests/api_calls.dart b/lib/backend/api_requests/api_calls.dart index 3b8fe216..15850cd4 100644 --- a/lib/backend/api_requests/api_calls.dart +++ b/lib/backend/api_requests/api_calls.dart @@ -15,6 +15,7 @@ const _kPrivateApiFunctionName = 'ffPrivateApiCall'; class PhpGroup { static String getBaseUrl() => 'https://freaccess.com.br/freaccess'; + // static String getBaseUrl() => 'http://192.168.2.250:8080'; static Map headers = {}; static LoginCall loginCall = LoginCall(); static UpdToken updToken = UpdToken(); diff --git a/lib/backend/push_notification/pushNotificationService.dart b/lib/backend/push_notification/pushNotificationService.dart index 79e6768f..8ec899a1 100644 --- a/lib/backend/push_notification/pushNotificationService.dart +++ b/lib/backend/push_notification/pushNotificationService.dart @@ -8,9 +8,12 @@ import 'package:f_r_e_hub/actions/actions.dart'; import 'package:f_r_e_hub/app_state.dart'; import 'package:f_r_e_hub/backend/api_requests/api_calls.dart'; import 'package:f_r_e_hub/components/templates_components/access_notification_modal_template_component/access_notification_modal_template_component_widget.dart'; +import 'package:f_r_e_hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart'; import 'package:f_r_e_hub/components/templates_components/message_notificaion_modal_template_component/message_notification_widget.dart'; import 'package:f_r_e_hub/components/templates_components/visit_request_template_component/visit_request_template_component_widget.dart'; +import 'package:f_r_e_hub/flutter_flow/flutter_flow_util.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:rxdart/rxdart.dart'; @@ -105,17 +108,17 @@ class PushNotificationService { await Future.delayed(Duration(milliseconds: 100)); } - var initializationSettingsAndroid = - AndroidInitializationSettings('mipmap/ic_fre_black'); - var initializationSettingsIOS = DarwinInitializationSettings( + const initializationSettingsAndroid = AndroidInitializationSettings('mipmap/ic_fre_black'); + const initializationSettingsIOS = DarwinInitializationSettings( requestAlertPermission: true, requestBadgePermission: true, requestSoundPermission: true, ); - var initializationSettings = InitializationSettings( + const initializationSettings = InitializationSettings( android: initializationSettingsAndroid, iOS: initializationSettingsIOS, ); + _flutterLocalNotificationsPlugin.initialize( initializationSettings, onDidReceiveNotificationResponse: (NotificationResponse response) async { @@ -184,7 +187,17 @@ class PushNotificationService { _onMessage.add(message); log('Extra: ${message.notification?.body}'); - NotificationHandler().handleMessage(message.data, context); }); + Map extra = {}; + log('Message: ${message.data}'); + + if (message.data['click_action'] == 'mensagem') { + extra['body'] = message.notification?.body; + } + + log('New Extra: $extra'); + + NotificationHandler().handleMessage(message.data, context, extra: extra); + }); } void configureTokenRefresh() { @@ -260,8 +273,11 @@ class PushNotificationService { } void _showNotification(RemoteMessage message) async { - String channelId = - _getChannelIdBasedOnClickAction(message.data['click_action']); + Map messageParsed = message.toMap(); + Map data = messageParsed['data']; + Map notification = messageParsed['notification']; + + String channelId = _getChannelIdBasedOnClickAction(data['click_action']); var androidDetails = AndroidNotificationDetails( channelId, @@ -270,19 +286,33 @@ class PushNotificationService { importance: Importance.max, priority: Priority.high, ); - var iOSDetails = DarwinNotificationDetails(); - var generalNotificationDetails = - NotificationDetails(android: androidDetails, iOS: iOSDetails); + var iOSDetails = const DarwinNotificationDetails( + categoryIdentifier: 'plainCategory', + sound: 'slow_spring_board.aiff', + presentList: true, + interruptionLevel: InterruptionLevel.critical, + ); + + var generalNotificationDetails = NotificationDetails(android: androidDetails, iOS: iOSDetails); log('Showing notification: ${message.messageId.hashCode}'); + log('Message Title: ${notification['title'].toString()}'); + log('Message Body: ${notification['body'].toString()}'); + log('Message Payload: ${data.toString()}'); + await _flutterLocalNotificationsPlugin.show( - // DateTime.now().millisecondsSinceEpoch % (1 << 31), - math.Random().nextInt(1 << 30), - message.notification?.title, - message.notification?.body, + DateTime.now().microsecond, + notification['title'].toString(), + notification['body'].toString(), generalNotificationDetails, - payload: message.data.toString(), - ); + payload: data.toString(), + ).catchError((err, stack) { + log('Error: $err'); + log('Stack: $stack'); + }); + + + } _handleNotificationClick(Map payload, {Map extra = const {}}) { @@ -317,15 +347,15 @@ class NotificationHandler { switch (message['click_action']) { case 'visit_request': _showVisitRequestDialog(message, context); - break; - case '': + case 'cancel_request': + _showVisitRequestResolvedDialog(message, context); break; case 'access': _showAcessNotificationModal(message, context); break; case 'mensagem': - _showMessageNotificationDialog(message, context, extra); + _showMessageNotificationDialog(message, context, extra); break; case 'enroll_cond': log('enroll_cond'); @@ -352,6 +382,7 @@ class NotificationHandler { void _showAcessNotificationModal( Map message, BuildContext context) { log('Showing access notification dialog'); + log('Message: ${message}'); log('USR_TIPO: ${message['USR_TIPO']}'); log('USR_ID: ${message['USR_ID']}'); log('USR_DOCUMENTO: ${message['USR_DOCUMENTO']}'); @@ -359,21 +390,27 @@ class NotificationHandler { context: context, builder: (BuildContext context) { String id = _getIdBasedOnUserType(message); - return Dialog( - backgroundColor: Colors.transparent, - child: AccessNotificationModalTemplateComponentWidget( - datetime: message['ACE_DATAHORA'].toString(), - drive: message['ACI_DESCRICAO'].toString(), - id: message['USR_TIPO'].toString() == 'O' - ? message['USR_ID'].toString() == '' - ? '0' - : message['USR_ID'].toString() - : message['USR_DOCUMENTO'].toString() == '' - ? '0' - : message['USR_DOCUMENTO'].toString(), - name: message['PES_NOME'].toString(), - type: message['USR_TIPO'], - )); + + return GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Dialog( + backgroundColor: Colors.transparent, + child: AccessNotificationModalTemplateComponentWidget( + datetime: message['ACE_DATAHORA'].toString(), + drive: message['SET_DESCRICAO'].toString(), + id: message['USR_TIPO'].toString() == 'O' + ? message['USR_ID'].toString() == '' + ? '0' + : message['USR_ID'].toString() + : message['USR_DOCUMENTO'].toString() == '' + ? '0' + : message['USR_DOCUMENTO'].toString(), + name: message['PES_NOME'].toString(), + type: message['USR_TIPO'], + )), + ); }, ); } @@ -382,17 +419,31 @@ class NotificationHandler { Map message, BuildContext context, Map extra) { log('Showing message notification dialog'); log('Notification "message": $message'); + log('Extra: $extra'); + Map local = {}; + + try { + local = jsonDecode(message['local']); + } catch (err) { + local = message['local']; + } + showDialog( useSafeArea: true, context: context, builder: (BuildContext context) { - return Dialog( - backgroundColor: Colors.transparent, - child: MessageNotificationModalTemplateComponentWidget( - id: message['local']['CLI_ID'].toString(), - from: message['remetente'].toString(), - to: message['destinatario'].toString() == 'O' ? 'Morador' : 'Visitante', - message: extra['body'].toString().isEmpty ? 'Unknown' : extra['body'].toString(), + return GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Dialog( + backgroundColor: Colors.transparent, + child: MessageNotificationModalTemplateComponentWidget( + id: local['CLI_ID'].toString(), + from: message['remetente'].toString(), + to: message['destinatario'].toString() == 'O' ? 'Morador' : 'Visitante', + message: extra['body'].toString().isEmpty ? 'Unknown' : extra['body'].toString(), + ), ), ); }, @@ -423,6 +474,35 @@ class NotificationHandler { }, ); } + + void _showVisitRequestResolvedDialog( + Map message, BuildContext context) { + log('Showing visit request notification dialog'); + + showDialog( + context: context, + builder: (BuildContext context) { + + return GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Dialog( + backgroundColor: Colors.transparent, + child: VisitRequestTemplateComponentWidget( + vteName: message['nomevisita'].toString(), + vteReason: message['motivo'].toString(), + vteMsg: message['resposta'].toString(), + vteDocument: message['documento'].toString(), + vteUUID: message['codvisita'].toString(), + vawRef: message['referencia'].toString(), + vawStatus: message['status'].toString(), + ), + ), + ); + }, + ); + } } class PushNotificationManager { diff --git a/lib/components/templates_components/access_notification_modal_template_component/access_notification_modal_template_component_widget.dart b/lib/components/templates_components/access_notification_modal_template_component/access_notification_modal_template_component_widget.dart index e293dfc4..789b1b44 100644 --- a/lib/components/templates_components/access_notification_modal_template_component/access_notification_modal_template_component_widget.dart +++ b/lib/components/templates_components/access_notification_modal_template_component/access_notification_modal_template_component_widget.dart @@ -41,6 +41,7 @@ class _AccessNotificationModalTemplateComponentWidgetState @override void initState() { super.initState(); + _model = createModel( context, () => AccessNotificationModalTemplateComponentModel()); @@ -68,6 +69,16 @@ class _AccessNotificationModalTemplateComponentWidgetState Widget build(BuildContext context) { context.watch(); + String labelTypeResident = FFLocalizations.of(context).getVariableText( + enText: 'Resident', + ptText: 'Morador' + ); + + String labelTypeVisitor = FFLocalizations.of(context).getVariableText( + enText: 'Visitor', + ptText: 'Visitante' + ); + return Align( alignment: const AlignmentDirectional(0.0, 0.0), child: Padding( @@ -180,7 +191,7 @@ class _AccessNotificationModalTemplateComponentWidgetState children: [ Expanded( child: TextFormField( - controller: _model.textController2, + controller: TextEditingController(text: widget.type == 'O' ? labelTypeResident : labelTypeVisitor), focusNode: _model.textFieldFocusNode2, autofocus: false, textInputAction: TextInputAction.next, @@ -314,8 +325,9 @@ class _AccessNotificationModalTemplateComponentWidgetState obscureText: false, decoration: InputDecoration( isDense: true, - labelText: FFLocalizations.of(context).getText( - '9jdmuak2' /* Acionamento */, + labelText: FFLocalizations.of(context).getVariableText( + enText: 'Access Sector', + ptText: 'Setor de Acesso', ), labelStyle: FlutterFlowTheme.of(context) .labelMedium