From 4650c94bdc6091a8070e53361c2232cce93eca1e Mon Sep 17 00:00:00 2001 From: dab246 Date: Fri, 7 Jun 2024 02:04:01 +0700 Subject: [PATCH] TF-2871 Fix cannot open email correctly when click on notification in app terminated --- ios/Runner/AppDelegate.swift | 39 ++++++++------- ios/TwakeCore/Utils/CoreUtils.swift | 3 ++ .../home/presentation/home_controller.dart | 30 +++++------ .../presentation/services/fcm_receiver.dart | 50 ++++++++++++------- .../presentation/services/fcm_service.dart | 6 +-- 5 files changed, 73 insertions(+), 55 deletions(-) diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 0c13269e00..acd25aa7a2 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -8,7 +8,7 @@ import flutter_local_notifications @objc class AppDelegate: FlutterAppDelegate { var notificationInteractionChannel: FlutterMethodChannel? - var initialNotificationInfo: Any? + var remoteNotificationPayload: Any? override func application( _ application: UIApplication, @@ -17,7 +17,7 @@ import flutter_local_notifications GeneratedPluginRegistrant.register(with: self) createNotificationInteractionChannel() - initialNotificationInfo = launchOptions?[.remoteNotification] + remoteNotificationPayload = launchOptions?[.remoteNotification] if #available(iOS 10.0, *) { UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate @@ -84,15 +84,11 @@ import flutter_local_notifications override func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { - TwakeLogger.shared.log(message: "AppDelegate::userNotificationCenter::willPresent::notification: \(notification)") if let notificationBadgeCount = notification.request.content.badge?.intValue, notificationBadgeCount > 0 { let newBadgeCount = UIApplication.shared.applicationIconBadgeNumber + notificationBadgeCount - TwakeLogger.shared.log(message: "AppDelegate::userNotificationCenter::willPresent:newBadgeCount: \(newBadgeCount)") updateAppBadger(newBadgeCount: newBadgeCount) } - if let emailId = notification.request.content.userInfo[JmapConstants.EMAIL_ID] as? String, - !emailId.isEmpty, - !isAppForegroundActive() { + if validateDisplayPushNotification(userInfo: notification.request.content.userInfo) { completionHandler([.alert, .badge, .sound]) } else { completionHandler([]) @@ -100,22 +96,28 @@ import flutter_local_notifications } override func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { - TwakeLogger.shared.log(message: "AppDelegate::userNotificationCenter::didReceive::response: \(response)") let currentBadgeCount = UIApplication.shared.applicationIconBadgeNumber let newBadgeCount = currentBadgeCount > 0 ? currentBadgeCount - 1 : 0 updateAppBadger(newBadgeCount: newBadgeCount) - if let emailId = response.notification.request.content.userInfo[JmapConstants.EMAIL_ID] as? String { - self.notificationInteractionChannel?.invokeMethod("openEmail", arguments: emailId) - } + self.remoteNotificationPayload = response.notification.request.content.userInfo + self.notificationInteractionChannel?.invokeMethod( + CoreUtils.USER_CLICK_NOTIFICATION_IN_FOREGROUND_METHOD_CALL_NAME, + arguments: response.notification.request.content.userInfo) completionHandler() } + + private func validateDisplayPushNotification(userInfo: [AnyHashable : Any]) -> Bool { + if let emailId = userInfo[JmapConstants.EMAIL_ID] as? String, !emailId.isEmpty, appBackgroundIsActive() { + return true + } + return false + } } extension AppDelegate { private func updateAppBadger(newBadgeCount: Int) { - TwakeLogger.shared.log(message: "AppDelegate::updateAppBadger::newBadgeCount: \(newBadgeCount)") if #available(iOS 16.0, *) { UNUserNotificationCenter.current().setBadgeCount(newBadgeCount) } else { @@ -125,7 +127,6 @@ extension AppDelegate { } private func removeAppBadger() { - TwakeLogger.shared.log(message: "AppDelegate::removeAppBadger") if #available(iOS 16.0, *) { UNUserNotificationCenter.current().setBadgeCount(0) } else { @@ -134,23 +135,23 @@ extension AppDelegate { } } - private func isAppForegroundActive() -> Bool { - return UIApplication.shared.applicationState == .active + private func appBackgroundIsActive() -> Bool { + return UIApplication.shared.applicationState == .background } private func createNotificationInteractionChannel() { let controller : FlutterViewController = window?.rootViewController as! FlutterViewController self.notificationInteractionChannel = FlutterMethodChannel( - name: "notification_interaction_channel", + name: CoreUtils.NOTIFICATION_INTERACTION_CHANNEL_NAME, binaryMessenger: controller.binaryMessenger ) self.notificationInteractionChannel?.setMethodCallHandler { (call, result) in switch call.method { - case "getInitialNotificationInfo": - result(self.initialNotificationInfo) - self.initialNotificationInfo = nil + case CoreUtils.GET_REMOTE_NOTIFICATION_PAYLOAD_METHOD_CALL_NAME: + result(self.remoteNotificationPayload) + self.remoteNotificationPayload = nil default: break } diff --git a/ios/TwakeCore/Utils/CoreUtils.swift b/ios/TwakeCore/Utils/CoreUtils.swift index f0d1bde463..2b74d558c5 100644 --- a/ios/TwakeCore/Utils/CoreUtils.swift +++ b/ios/TwakeCore/Utils/CoreUtils.swift @@ -5,6 +5,9 @@ class CoreUtils { static let ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS" static let EN_US_POSIX_LOCALE = "en_US_POSIX" + static let NOTIFICATION_INTERACTION_CHANNEL_NAME = "notification_interaction_channel" + static let GET_REMOTE_NOTIFICATION_PAYLOAD_METHOD_CALL_NAME = "getRemoteNotificationPayload" + static let USER_CLICK_NOTIFICATION_IN_FOREGROUND_METHOD_CALL_NAME = "userClickNotificationInForeground" func getCurrentDate() -> Date { if #available(iOS 15, *) { diff --git a/lib/features/home/presentation/home_controller.dart b/lib/features/home/presentation/home_controller.dart index d6fde517b6..823ef2058f 100644 --- a/lib/features/home/presentation/home_controller.dart +++ b/lib/features/home/presentation/home_controller.dart @@ -2,11 +2,9 @@ import 'package:core/utils/platform_info.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_downloader/flutter_downloader.dart'; import 'package:get/get.dart'; -import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/core/session/session.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:jmap_dart_client/jmap/mail/email/email_address.dart'; -import 'package:model/account/personal_account.dart'; import 'package:model/email/email_content.dart'; import 'package:model/email/email_content_type.dart'; import 'package:receive_sharing_intent/receive_sharing_intent.dart'; @@ -43,7 +41,6 @@ class HomeController extends ReloadableController { this._cleanupRecentLoginUsernameCacheInteractor, ); - PersonalAccount? currentAccount; EmailId? _emailIdPreview; @override @@ -61,6 +58,12 @@ class HomeController extends ReloadableController { super.onReady(); } + @override + void dispose() { + _emailIdPreview = null; + super.dispose(); + } + @override void handleReloaded(Session session) { if (_emailIdPreview != null) { @@ -87,9 +90,9 @@ class HomeController extends ReloadableController { static void downloadCallback(String id, DownloadTaskStatus status, int progress) {} - void _cleanupCache() async { + Future _cleanupCache() async { if (PlatformInfo.isIOS) { - await _handleIOSDataMessage(); + _handleUserClickNotificationOnIOS(); } await HiveCacheConfig.instance.onUpgradeDatabase(cachingManager); @@ -118,17 +121,14 @@ class HomeController extends ReloadableController { _emailReceiveManager.receivingFileSharingStream.listen(_emailReceiveManager.setPendingFileInfo); } - Future _handleIOSDataMessage() async { - if (Get.arguments is EmailId) { - _emailIdPreview = Get.arguments; + Future _handleUserClickNotificationOnIOS() async { + final emailId = _getEmailIdFromNavigationRouter(); + if (emailId != null) { + _emailIdPreview = emailId; } else { - final notificationInfo = await FcmReceiver.instance.getIOSInitialNotificationInfo(); - if (notificationInfo != null && notificationInfo.containsKey('email_id')) { - final emailId = notificationInfo['email_id'] as String?; - if (emailId?.isNotEmpty == true) { - _emailIdPreview = EmailId(Id(emailId!)); - } - } + _emailIdPreview = await FcmReceiver.instance.getEmailIdFromRemoteNotificationPayload(); } } + + EmailId? _getEmailIdFromNavigationRouter() => Get.arguments as EmailId?; } \ No newline at end of file diff --git a/lib/features/push_notification/presentation/services/fcm_receiver.dart b/lib/features/push_notification/presentation/services/fcm_receiver.dart index e3d635b5b4..e6da59376e 100644 --- a/lib/features/push_notification/presentation/services/fcm_receiver.dart +++ b/lib/features/push_notification/presentation/services/fcm_receiver.dart @@ -4,6 +4,8 @@ import 'package:core/utils/broadcast_channel/broadcast_channel.dart'; import 'package:core/utils/platform_info.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/services.dart'; +import 'package:jmap_dart_client/jmap/core/id.dart'; +import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/controller/fcm_message_controller.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/services/fcm_service.dart'; import 'package:tmail_ui_user/main/utils/app_config.dart'; @@ -19,7 +21,7 @@ Future handleFirebaseBackgroundMessage(RemoteMessage message) async { class FcmReceiver { FcmReceiver._internal() { if (PlatformInfo.isIOS) { - _setUpIOSNotificationInteraction(); + _setUpIOSNotificationInteractionMethodChannel(); } } @@ -28,6 +30,12 @@ class FcmReceiver { static FcmReceiver get instance => _instance; static const notificationInteractionChannel = MethodChannel('notification_interaction_channel'); + + static const String GET_REMOTE_NOTIFICATION_PAYLOAD_METHOD_CALL_NAME = 'getRemoteNotificationPayload'; + static const String USER_CLICK_NOTIFICATION_IN_FOREGROUND_METHOD_CALL_NAME = 'userClickNotificationInForeground'; + static const String BROADCAST_CHANNEL_NAME = 'background-message'; + static const String EMAIL_ID = 'email_id'; + static const int MAX_COUNT_RETRY_TO_GET_FCM_TOKEN = 3; int _countRetryToGetFcmToken = 0; @@ -45,14 +53,25 @@ class FcmReceiver { } } - void _setUpIOSNotificationInteraction() { - notificationInteractionChannel.setMethodCallHandler((call) async { - log('FcmReceiver::_setUpIOSNotificationInteraction:notificationInteractionChannel: $call'); - if (call.method == 'openEmail' && call.arguments is String) { - log('FcmReceiver::_setUpIOSNotificationInteraction:openEmail with id = ${call.arguments}'); - FcmService.instance.handleOpenEmailFromNotification(call.arguments); + void _setUpIOSNotificationInteractionMethodChannel() { + notificationInteractionChannel.setMethodCallHandler(_handleChannelMethodCall); + } + + Future _handleChannelMethodCall(MethodCall methodCall) async { + if (methodCall.method == USER_CLICK_NOTIFICATION_IN_FOREGROUND_METHOD_CALL_NAME) { + final emailId = parsingEmailIDFromNotificationPayload(methodCall.arguments); + if (emailId != null) { + FcmService.instance.handleUserClickNotificationInForegroundOnIOS(emailId); } - }); + } + } + + EmailId? parsingEmailIDFromNotificationPayload(dynamic notificationPayload) { + final id = notificationPayload[EMAIL_ID] as String?; + if (id != null && id.isNotEmpty) { + return EmailId(Id(id)); + } + return null; } Future _requestNotificationPermissionOnWeb() async { @@ -77,7 +96,7 @@ class FcmReceiver { } void _onMessageBroadcastChannel() { - final broadcast = BroadcastChannel('background-message'); + final broadcast = BroadcastChannel(BROADCAST_CHANNEL_NAME); broadcast.onMessage.listen((event) { if (event is html.MessageEvent) { FcmService.instance.handleMessageEventBroadcastChannel(event); @@ -120,16 +139,13 @@ class FcmReceiver { }); } - Future?> getIOSInitialNotificationInfo() async { + Future getEmailIdFromRemoteNotificationPayload() async { try { - final notificationInfo = await notificationInteractionChannel.invokeMethod('getInitialNotificationInfo'); - log('FcmReceiver::getIOSInitialNotificationInfo:notificationInfo: $notificationInfo'); - if (notificationInfo != null && notificationInfo is Map) { - return notificationInfo; - } - return null; + final notificationPayload = await notificationInteractionChannel.invokeMethod(GET_REMOTE_NOTIFICATION_PAYLOAD_METHOD_CALL_NAME); + final emailId = parsingEmailIDFromNotificationPayload(notificationPayload); + return emailId; } catch (e) { - logError('FcmReceiver::getIOSInitialNotificationInfo: Exception: $e'); + logError('FcmReceiver::getEmailIdFromRemoteNotificationPayload: Exception: $e'); return null; } } diff --git a/lib/features/push_notification/presentation/services/fcm_service.dart b/lib/features/push_notification/presentation/services/fcm_service.dart index 031b43d82b..4ab42e1614 100644 --- a/lib/features/push_notification/presentation/services/fcm_service.dart +++ b/lib/features/push_notification/presentation/services/fcm_service.dart @@ -4,7 +4,6 @@ import 'dart:convert'; import 'package:core/utils/app_logger.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/mail/email/email.dart'; import 'package:tmail_ui_user/features/push_notification/presentation/model/broadcast_message_event_data.dart'; import 'package:tmail_ui_user/main/routes/app_routes.dart'; @@ -63,11 +62,10 @@ class FcmService { fcmTokenStreamController = StreamController.broadcast(); } - void handleOpenEmailFromNotification(String emailId) { - log('FcmService::handleOpenEmailFromNotification:emailId: $emailId'); + void handleUserClickNotificationInForegroundOnIOS(EmailId emailId) { popAndPush( RouteUtils.generateNavigationRoute(AppRoutes.home), - arguments: EmailId(Id(emailId)) + arguments: emailId ); }