Skip to content

Commit

Permalink
TF-2871 Fix cannot open email correctly when click on notification in…
Browse files Browse the repository at this point in the history
… app terminated
  • Loading branch information
dab246 committed Jun 7, 2024
1 parent f0d88b3 commit 4650c94
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 55 deletions.
39 changes: 20 additions & 19 deletions ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -84,38 +84,40 @@ 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([])
}
}

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 {
Expand All @@ -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 {
Expand All @@ -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
}
Expand Down
3 changes: 3 additions & 0 deletions ios/TwakeCore/Utils/CoreUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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, *) {
Expand Down
30 changes: 15 additions & 15 deletions lib/features/home/presentation/home_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -43,7 +41,6 @@ class HomeController extends ReloadableController {
this._cleanupRecentLoginUsernameCacheInteractor,
);

PersonalAccount? currentAccount;
EmailId? _emailIdPreview;

@override
Expand All @@ -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) {
Expand All @@ -87,9 +90,9 @@ class HomeController extends ReloadableController {

static void downloadCallback(String id, DownloadTaskStatus status, int progress) {}

void _cleanupCache() async {
Future<void> _cleanupCache() async {
if (PlatformInfo.isIOS) {
await _handleIOSDataMessage();
_handleUserClickNotificationOnIOS();
}

await HiveCacheConfig.instance.onUpgradeDatabase(cachingManager);
Expand Down Expand Up @@ -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?;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -19,7 +21,7 @@ Future<void> handleFirebaseBackgroundMessage(RemoteMessage message) async {
class FcmReceiver {
FcmReceiver._internal() {
if (PlatformInfo.isIOS) {
_setUpIOSNotificationInteraction();
_setUpIOSNotificationInteractionMethodChannel();
}
}

Expand All @@ -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;
Expand All @@ -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<dynamic> _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<void> _requestNotificationPermissionOnWeb() async {
Expand All @@ -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);
Expand Down Expand Up @@ -120,16 +139,13 @@ class FcmReceiver {
});
}

Future<Map<String, dynamic>?> getIOSInitialNotificationInfo() async {
Future<dynamic> getEmailIdFromRemoteNotificationPayload() async {
try {
final notificationInfo = await notificationInteractionChannel.invokeMethod('getInitialNotificationInfo');
log('FcmReceiver::getIOSInitialNotificationInfo:notificationInfo: $notificationInfo');
if (notificationInfo != null && notificationInfo is Map<String, dynamic>) {
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;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -63,11 +62,10 @@ class FcmService {
fcmTokenStreamController = StreamController<String?>.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
);
}

Expand Down

0 comments on commit 4650c94

Please sign in to comment.