/* * 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 import UserNotifications import linphonesw #if USE_CRASHLYTICS import Firebase #endif 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: Config.appGroupName) 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? } class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? var lc: Core? override init() { super.init() #if USE_CRASHLYTICS FirebaseApp.configure() #endif } override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) LoggingService.Instance.logLevel = LogLevel.Debug Factory.Instance.logCollectionPath = Factory.Instance.getConfigDir(context: nil) Factory.Instance.enableLogCollection(state: LogCollectionState.Enabled) Log.info("[msgNotificationService] start msgNotificationService extension") /* if (VFSUtil.vfsEnabled(groupName: Config.appGroupName) && !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: false) { Log.info("received push payload : \(bestAttemptContent.userInfo.debugDescription)") /* let defaults = UserDefaults.init(suiteName: Config.appGroupName) 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! } 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 = "" bestAttemptContent.sound = UNNotificationSound(named: UNNotificationSoundName("msg.caf")) } else { bestAttemptContent.title = NSLocalizedString("Message received", comment: "") bestAttemptContent.body = NSLocalizedString("IM_MSG", comment: "") } 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") lc = try? Factory.Instance.createSharedCoreWithConfig(config: Config.get(), systemContext: nil, appGroupId: Config.appGroupName, 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