diff --git a/Linphone.xcodeproj/project.pbxproj b/Linphone.xcodeproj/project.pbxproj
index 67231e24a..8dfb1c299 100644
--- a/Linphone.xcodeproj/project.pbxproj
+++ b/Linphone.xcodeproj/project.pbxproj
@@ -17,6 +17,10 @@
66C491FD2B24D36500CEA16D /* AudioRouteUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FC2B24D36500CEA16D /* AudioRouteUtils.swift */; };
66C491FF2B24D4AC00CEA16D /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FE2B24D4AC00CEA16D /* FileUtils.swift */; };
66C492012B24DB6900CEA16D /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C492002B24DB6900CEA16D /* Log.swift */; };
+ 66FBFC482B83B8CC00BC6AB1 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C492002B24DB6900CEA16D /* Log.swift */; };
+ 66FBFC492B83BD2400BC6AB1 /* ConfigExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491F82B24D25A00CEA16D /* ConfigExtension.swift */; };
+ 66FBFC4A2B83BD3300BC6AB1 /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FE2B24D4AC00CEA16D /* FileUtils.swift */; };
+ 66FBFC4B2B83BD7B00BC6AB1 /* CoreExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66C491FA2B24D32600CEA16D /* CoreExtension.swift */; };
D706BA822ADD72D100278F45 /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D706BA812ADD72D100278F45 /* DeviceRotationViewModifier.swift */; };
D70C93DE2AC2D0F60063CA3B /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = D70C93DD2AC2D0F60063CA3B /* Localizable.xcstrings */; };
D717071E2AC5922E0037746F /* ColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D717071D2AC5922E0037746F /* ColorExtension.swift */; };
@@ -752,7 +756,11 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 66FBFC482B83B8CC00BC6AB1 /* Log.swift in Sources */,
6691CA7E2B839C2D00B2A7B8 /* NotificationService.swift in Sources */,
+ 66FBFC492B83BD2400BC6AB1 /* ConfigExtension.swift in Sources */,
+ 66FBFC4A2B83BD3300BC6AB1 /* FileUtils.swift in Sources */,
+ 66FBFC4B2B83BD7B00BC6AB1 /* CoreExtension.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/msgNotificationService/NotificationService.swift b/msgNotificationService/NotificationService.swift
index de2092700..45a81b621 100644
--- a/msgNotificationService/NotificationService.swift
+++ b/msgNotificationService/NotificationService.swift
@@ -1,21 +1,21 @@
/*
-* Copyright (c) 2010-2020 Belledonne Communications SARL.
-*
-* This file is part of linphone-iphone
-*
-* This program is free software: you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation, either version 3 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program. If not, see .
-*/
+ * Copyright (c) 2010-2020 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-iphone
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
// swiftlint:disable identifier_name
@@ -28,23 +28,52 @@ import Firebase
var APP_GROUP_ID = "group.org.linphone.phone.msgNotification"
var LINPHONE_DUMMY_SUBJECT = "dummy subject"
+extension String {
+ func getDisplayNameFromSipAddress(lc: Core) -> String? {
+ Log.info("looking for display name for \(self)")
+
+ let defaults = UserDefaults.init(suiteName: APP_GROUP_ID)
+ let addressBook = defaults?.dictionary(forKey: "addressBook")
+
+ if addressBook == nil {
+ Log.info("address book not found in userDefaults")
+ return nil
+ }
+
+ var usePrefix = true
+ if let account = lc.defaultAccount, let params = account.params {
+ usePrefix = params.useInternationalPrefixForCallsAndChats
+ }
+
+ if let simpleAddr = lc.interpretUrl(url: self, applyInternationalPrefix: usePrefix) {
+ simpleAddr.clean()
+ let nomalSipaddr = simpleAddr.asString()
+ if let displayName = addressBook?[nomalSipaddr] as? String {
+ Log.info("display name for \(self): \(displayName)")
+ return displayName
+ }
+ }
+
+ Log.info("display name for \(self) not found in userDefaults")
+ return nil
+ }
+}
+
struct MsgData: Codable {
- var from: String?
- var body: String?
- var subtitle: String?
- var callId: String?
- var localAddr: String?
- var peerAddr: String?
+ var from: String?
+ var body: String?
+ var subtitle: String?
+ var callId: String?
+ var localAddr: String?
+ var peerAddr: String?
}
class NotificationService: UNNotificationServiceExtension {
-
- var contentHandler: ((UNNotificationContent) -> Void)?
- var bestAttemptContent: UNMutableNotificationContent?
-
- var lc: Core?
-// static var logDelegate: LinphoneLoggingServiceManager!
-// static var log: LoggingService!
+
+ var contentHandler: ((UNNotificationContent) -> Void)?
+ var bestAttemptContent: UNMutableNotificationContent?
+
+ var lc: Core?
override init() {
super.init()
@@ -52,20 +81,117 @@ class NotificationService: UNNotificationServiceExtension {
FirebaseApp.configure()
#endif
}
-
- override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
-
- }
-
- override func serviceExtensionTimeWillExpire() {
- // Called just before the extension will be terminated by the system.
- // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
- //NotificationService.log.warning(message: "serviceExtensionTimeWillExpire")
- //stopCore()
- if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
- NSLog("[msgNotificationService] serviceExtensionTimeWillExpire")
- bestAttemptContent.categoryIdentifier = "app_active"
-
+
+ override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
+
+ self.contentHandler = contentHandler
+ bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
+ Log.info("[msgNotificationService] start msgNotificationService extension")
+ /*
+ if (VFSUtil.vfsEnabled(groupName: APP_GROUP_ID) && !VFSUtil.activateVFS()) {
+ VFSUtil.log("[VFS] Error unable to activate.", .error)
+ }
+ */
+ if let bestAttemptContent = bestAttemptContent {
+ createCore()
+
+ if !lc!.config!.getBool(section: "app", key: "disable_chat_feature", defaultValue: true) {
+ Log.info("received push payload : \(bestAttemptContent.userInfo.debugDescription)")
+
+ /*
+ let defaults = UserDefaults.init(suiteName: APP_GROUP_ID)
+ if let chatroomsPushStatus = defaults?.dictionary(forKey: "chatroomsPushStatus") {
+ let aps = bestAttemptContent.userInfo["aps"] as? NSDictionary
+ let alert = aps?["alert"] as? NSDictionary
+ let fromAddresses = alert?["loc-args"] as? [String]
+
+ if let from = fromAddresses?.first {
+ if ((chatroomsPushStatus[from] as? String) == "disabled") {
+ NotificationService.log.message(message: "message comes from a muted chatroom, ignore it")
+ contentHandler(UNNotificationContent())
+ }
+ }
+ }
+ */
+ if let chatRoomInviteAddr = bestAttemptContent.userInfo["chat-room-addr"] as? String, !chatRoomInviteAddr.isEmpty {
+ Log.info("fetch chat room for invite, addr: \(chatRoomInviteAddr)")
+ let chatRoom = lc!.getNewChatRoomFromConfAddr(chatRoomAddr: chatRoomInviteAddr)
+
+ if let chatRoom = chatRoom {
+ stopCore()
+ Log.info("chat room invite received")
+ bestAttemptContent.title = NSLocalizedString("GC_MSG", comment: "")
+ if chatRoom.hasCapability(mask: ChatRoom.Capabilities.OneToOne.rawValue) {
+ if chatRoom.peerAddress?.displayName?.isEmpty != true {
+ bestAttemptContent.body = chatRoom.peerAddress!.displayName!
+ } else {
+ bestAttemptContent.body = chatRoom.peerAddress!.username!
+ }
+ } else {
+ bestAttemptContent.body = chatRoom.subject!
+ }
+
+ bestAttemptContent.sound = UNNotificationSound(named: UNNotificationSoundName("msg.caf")) // TODO : temporary fix, to be removed after flexisip release
+ contentHandler(bestAttemptContent)
+ return
+ }
+ } else if let callId = bestAttemptContent.userInfo["call-id"] as? String {
+ Log.info("fetch msg for callid ["+callId+"]")
+ let message = lc!.getNewMessageFromCallid(callId: callId)
+
+ if let message = message {
+ let msgData = parseMessage(message: message)
+
+ // Extension only upates app's badge when main shared core is Off = extension's core is On.
+ // Otherwise, the app will update the badge.
+ if lc?.globalState == GlobalState.On, let badge = updateBadge() as NSNumber? {
+ bestAttemptContent.badge = badge
+ }
+
+ stopCore()
+
+ bestAttemptContent.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "msg.caf"))
+ bestAttemptContent.title = NSLocalizedString("Message received", comment: "")
+ if let subtitle = msgData?.subtitle {
+ bestAttemptContent.subtitle = subtitle
+ }
+ if let body = msgData?.body {
+ bestAttemptContent.body = body
+ }
+
+ bestAttemptContent.categoryIdentifier = "msg_cat"
+
+ bestAttemptContent.userInfo.updateValue(msgData?.callId as Any, forKey: "CallId")
+ bestAttemptContent.userInfo.updateValue(msgData?.from as Any, forKey: "from")
+ bestAttemptContent.userInfo.updateValue(msgData?.peerAddr as Any, forKey: "peer_addr")
+ bestAttemptContent.userInfo.updateValue(msgData?.localAddr as Any, forKey: "local_addr")
+
+ if message.reactionContent != " " {
+ contentHandler(bestAttemptContent)
+ } else {
+ contentHandler(UNNotificationContent())
+ }
+
+ return
+ } else {
+ Log.info("Message not found for callid ["+callId+"]")
+ }
+ }
+ }
+ serviceExtensionTimeWillExpire()
+ }
+
+ }
+
+ override func serviceExtensionTimeWillExpire() {
+ // Called just before the extension will be terminated by the system.
+ // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
+ Log.warn("serviceExtensionTimeWillExpire")
+ stopCore()
+ if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
+ NSLog("[msgNotificationService] serviceExtensionTimeWillExpire")
+ bestAttemptContent.categoryIdentifier = "app_active"
+
if let chatRoomInviteAddr = bestAttemptContent.userInfo["chat-room-addr"] as? String, !chatRoomInviteAddr.isEmpty {
bestAttemptContent.title = NSLocalizedString("GC_MSG", comment: "")
bestAttemptContent.body = ""
@@ -74,10 +200,92 @@ class NotificationService: UNNotificationServiceExtension {
bestAttemptContent.title = NSLocalizedString("Message received", comment: "")
bestAttemptContent.body = NSLocalizedString("IM_MSG", comment: "")
}
- contentHandler(bestAttemptContent)
- }
- }
-
+ contentHandler(bestAttemptContent)
+ }
+ }
+
+ func parseMessage(message: PushNotificationMessage) -> MsgData? {
+
+ var content = ""
+ if message.isConferenceInvitationNew {
+ content = NSLocalizedString("📅 You are invited to a meeting", comment: "")
+ } else if message.isConferenceInvitationUpdate {
+ content = NSLocalizedString("📅 Meeting has been modified", comment: "")
+ } else if message.isConferenceInvitationCancellation {
+ content = NSLocalizedString("📅 Meeting has been cancelled", comment: "")
+ } else {
+ content = message.isText ? message.textContent! : "🗻"
+ }
+
+ let fromAddr = message.fromAddr?.username
+ let callId = message.callId
+ let localUri = message.localAddr?.asStringUriOnly()
+ let peerUri = message.peerAddr?.asStringUriOnly()
+ let reactionContent = message.reactionContent
+ let from: String
+ if let fromDisplayName = message.fromAddr?.asStringUriOnly().getDisplayNameFromSipAddress(lc: lc!) {
+ from = fromDisplayName
+ } else {
+ from = fromAddr!
+ }
+
+ var msgData = MsgData(from: fromAddr, body: "", subtitle: "", callId: callId, localAddr: localUri, peerAddr: peerUri)
+
+ if let showMsg = lc!.config?.getBool(section: "app", key: "show_msg_in_notif", defaultValue: true), showMsg == true {
+ if let subject = message.subject as String?, !subject.isEmpty {
+ msgData.subtitle = subject
+ if reactionContent == nil {
+ msgData.body = from + " : " + content
+ } else {
+ msgData.body = from + NSLocalizedString(" has reacted by ", comment: "") + reactionContent! + NSLocalizedString(" to: ", comment: "") + content
+ }
+ } else {
+ msgData.subtitle = from
+ msgData.body = content
+ }
+ } else {
+ if let subject = message.subject as String?, !subject.isEmpty {
+ msgData.body = subject + " : " + from
+ } else {
+ msgData.body = from
+ }
+ }
+
+ Log.info("received msg size : \(content.count) \n")
+ return msgData
+ }
+
+ func createCore() {
+ Log.info("[msgNotificationService] create core")
+
+ let config = Config.newForSharedCore(appGroupId: APP_GROUP_ID, configFilename: "linphonerc", factoryConfigFilename: "")
+
+ LoggingService.Instance.logLevel = LogLevel.Debug
+ let configDir = Factory.Instance.getConfigDir(context: nil)
+
+ Factory.Instance.logCollectionPath = configDir
+ Factory.Instance.enableLogCollection(state: LogCollectionState.Enabled)
+
+ lc = try? Factory.Instance.createSharedCoreWithConfig(config: config!, systemContext: nil, appGroupId: APP_GROUP_ID, mainCore: false)
+ }
+
+ func stopCore() {
+ Log.info("stop core")
+ if let lc = lc {
+ lc.stop()
+ }
+ }
+
+ func updateBadge() -> Int {
+ var count = 0
+ count += lc!.unreadChatMessageCount
+ count += lc!.missedCallsCount
+ count += lc!.callsNb
+ Log.info("badge: \(count)\n")
+
+ return count
+ }
+
}
// swiftlint:enable identifier_name