Merge pull request #6 from FRE-Informatica/fd-562/notificacao_ios

FIX/FD-562: Notificação iOS
This commit is contained in:
Ivan Antunes 2024-07-25 08:08:25 -03:00 committed by GitHub
commit 453cec6040
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 172 additions and 54 deletions

View File

@ -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

View File

@ -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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
6436409727A31CD000820AF7 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
6436409927A31CD100820AF7 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/InfoPlist.strings; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
@ -301,8 +299,6 @@
6436409C27A31CD800820AF7 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
6436409227A31CD600820AF7 /* pt */,
6436409227A31CDA00820AF7 /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";

View File

@ -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)
}

View File

@ -25,8 +25,6 @@
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>CFBundleURLTypes</key>
<array>
<dict>
@ -42,10 +40,12 @@
</array>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
<key>FlutterDeepLinkingEnabled</key>
<true/>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
@ -63,6 +63,8 @@
</dict>
<key>NSCameraUsageDescription</key>
<string>In order to take a picture or video, this app requires permission to access the camera.</string>
<key>NSFaceIDUsageDescription</key>
<string>Why is my app authenticating using face id?</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>In order to upload data, this app requires permission to access the photo library.</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
@ -70,6 +72,7 @@
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
<string>remote-notification</string>
</array>
<key>UILaunchStoryboardName</key>
@ -78,8 +81,6 @@
<string>Main</string>
<key>UIRequiresFullScreen</key>
<true/>
<key>NSFaceIDUsageDescription</key>
<string>Why is my app authenticating using face id?</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>

View File

@ -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<String, String> headers = {};
static LoginCall loginCall = LoginCall();
static UpdToken updToken = UpdToken();

View File

@ -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<String, dynamic> 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<String, dynamic> messageParsed = message.toMap();
Map<String, dynamic> data = messageParsed['data'];
Map<String, dynamic> 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<String, dynamic> payload, {Map<String, dynamic> 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<String, dynamic> 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<String, dynamic> message, BuildContext context, Map<String, dynamic> extra) {
log('Showing message notification dialog');
log('Notification "message": $message');
log('Extra: $extra');
Map<String, dynamic> 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<String, dynamic> 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 {

View File

@ -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<FFAppState>();
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