From f1445a219127d1759341ad38d05a256aee1a9eda Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Thu, 7 Apr 2022 15:10:38 +1000 Subject: [PATCH] add missed call notification --- Session/Meta/AppDelegate.swift | 4 ++ Session/Notifications/AppNotifications.swift | 33 ++++++++++++++++ .../Notifications/NotificationsProtocol.h | 5 +++ .../NSENotificationPresenter.swift | 39 +++++++++++++++++++ .../NotificationServiceExtension.swift | 2 + .../Utilities/NoopNotificationsManager.swift | 4 ++ 6 files changed, 87 insertions(+) diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index 80edaae96..7e2985087 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -77,11 +77,15 @@ extension AppDelegate { // Add missed call message for call offer messages from more than one minute let infoMessage = self.insertCallInfoMessage(for: message, using: transaction) infoMessage.updateCallInfoMessage(.missed, using: transaction) + let thread = TSContactThread.getOrCreateThread(withContactSessionID: message.sender!, transaction: transaction) + SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction) return } guard SSKPreferences.areCallsEnabled else { let infoMessage = self.insertCallInfoMessage(for: message, using: transaction) infoMessage.updateCallInfoMessage(.permissionDenied, using: transaction) + let thread = TSContactThread.getOrCreateThread(withContactSessionID: message.sender!, transaction: transaction) + SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction) let contactName = Storage.shared.getContact(with: message.sender!, using: transaction)?.displayName(for: Contact.Context.regular) ?? message.sender! DispatchQueue.main.async { self.showMissedCallTipsIfNeeded(caller: contactName) diff --git a/Session/Notifications/AppNotifications.swift b/Session/Notifications/AppNotifications.swift index 1c68a4d06..a39574cce 100644 --- a/Session/Notifications/AppNotifications.swift +++ b/Session/Notifications/AppNotifications.swift @@ -269,6 +269,39 @@ public class NotificationPresenter: NSObject, NotificationsProtocol { ) } } + + public func notifyUser(forIncomingCall callInfoMessage: TSInfoMessage, in thread: TSThread, transaction: YapDatabaseReadTransaction) { + guard !thread.isMuted else { return } + guard !thread.isGroupThread() else { return } // Calls shouldn't happen in groups + guard let threadId = thread.uniqueId else { return } + guard [ .missed, .permissionDenied ].contains(callInfoMessage.callState) else { return } // Only notify missed call + + let category = AppNotificationCategory.errorMessage + + let userInfo = [ + AppNotificationUserInfoKey.threadId: threadId + ] + + let notificationTitle = callInfoMessage.previewText(with: transaction) + var notificationBody: String? + + if callInfoMessage.callState == .permissionDenied { + notificationBody = String(format: "modal_call_missed_tips_explanation".localized(), thread.name(with: transaction)) + } + + DispatchQueue.main.async { + let sound = self.requestSound(thread: thread) + + self.adaptee.notify( + category: category, + title: notificationTitle, + body: notificationBody ?? "", + userInfo: userInfo, + sound: sound, + replacingIdentifier: UUID().uuidString + ) + } + } public func notifyForFailedSend(inThread thread: TSThread) { let notificationTitle: String? diff --git a/SessionMessagingKit/Sending & Receiving/Notifications/NotificationsProtocol.h b/SessionMessagingKit/Sending & Receiving/Notifications/NotificationsProtocol.h index 4408ea47d..576adb737 100644 --- a/SessionMessagingKit/Sending & Receiving/Notifications/NotificationsProtocol.h +++ b/SessionMessagingKit/Sending & Receiving/Notifications/NotificationsProtocol.h @@ -8,6 +8,7 @@ NS_ASSUME_NONNULL_BEGIN @class TSErrorMessage; @class TSIncomingMessage; +@class TSInfoMessage; @class TSThread; @class YapDatabaseReadTransaction; @class YapDatabaseReadWriteTransaction; @@ -20,6 +21,10 @@ NS_ASSUME_NONNULL_BEGIN inThread:(TSThread *)thread transaction:(YapDatabaseReadTransaction *)transaction; +- (void)notifyUserForIncomingCall:(TSInfoMessage *)callInfoMessage + inThread:(TSThread *)thread + transaction:(YapDatabaseReadTransaction *)transaction; + - (void)cancelNotification:(NSString *)identifier; - (void)clearAllNotifications; diff --git a/SessionNotificationServiceExtension/NSENotificationPresenter.swift b/SessionNotificationServiceExtension/NSENotificationPresenter.swift index 148876540..13b7727b5 100644 --- a/SessionNotificationServiceExtension/NSENotificationPresenter.swift +++ b/SessionNotificationServiceExtension/NSENotificationPresenter.swift @@ -105,6 +105,45 @@ public class NSENotificationPresenter: NSObject, NotificationsProtocol { SNLog("Finish adding remote notification request") } + public func notifyUser(forIncomingCall callInfoMessage: TSInfoMessage, in thread: TSThread, transaction: YapDatabaseReadTransaction) { + guard !thread.isMuted else { return } + guard !thread.isGroupThread() else { return } // Calls shouldn't happen in groups + guard let threadID = thread.uniqueId else { return } + guard [ .missed, .permissionDenied ].contains(callInfoMessage.callState) else { return } // Only notify missed call + + var userInfo: [String:Any] = [ NotificationServiceExtension.isFromRemoteKey : true ] + userInfo[NotificationServiceExtension.threadIdKey] = threadID + + let notificationContent = UNMutableNotificationContent() + notificationContent.userInfo = userInfo + notificationContent.sound = OWSSounds.notificationSound(for: thread).notificationSound(isQuiet: false) + + // Badge Number + let newBadgeNumber = CurrentAppContext().appUserDefaults().integer(forKey: "currentBadgeNumber") + 1 + notificationContent.badge = NSNumber(value: newBadgeNumber) + CurrentAppContext().appUserDefaults().set(newBadgeNumber, forKey: "currentBadgeNumber") + + notificationContent.title = callInfoMessage.previewText(with: transaction) + notificationContent.body = "" + if callInfoMessage.callState == .permissionDenied { + notificationContent.body = String(format: "modal_call_missed_tips_explanation".localized(), thread.name(with: transaction)) + } + + // Add request + let identifier = UUID().uuidString + let request = UNNotificationRequest(identifier: identifier, content: notificationContent, trigger: nil) + SNLog("Add remote notification request: \(notificationContent.body)") + let semaphore = DispatchSemaphore(value: 0) + UNUserNotificationCenter.current().add(request) { error in + if let error = error { + SNLog("Failed to add notification request due to error:\(error)") + } + semaphore.signal() + } + semaphore.wait() + SNLog("Finish adding remote notification request") + } + public func cancelNotification(_ identifier: String) { let notificationCenter = UNUserNotificationCenter.current() notificationCenter.removePendingNotificationRequests(withIdentifiers: [ identifier ]) diff --git a/SessionNotificationServiceExtension/NotificationServiceExtension.swift b/SessionNotificationServiceExtension/NotificationServiceExtension.swift index 00122f971..748a3c508 100644 --- a/SessionNotificationServiceExtension/NotificationServiceExtension.swift +++ b/SessionNotificationServiceExtension/NotificationServiceExtension.swift @@ -79,6 +79,7 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension if let sender = callMessage.sender, let thread = TSContactThread.fetch(for: sender, using: transaction), !thread.isMessageRequest(using: transaction) { let infoMessage = TSInfoMessage.from(callMessage, associatedWith: thread) infoMessage.updateCallInfoMessage(.permissionDenied, using: transaction) + SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction) } break } @@ -92,6 +93,7 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension MessageSender.sendNonDurably(message, in: thread, using: transaction).retainUntilComplete() let infoMessage = TSInfoMessage.from(callMessage, associatedWith: thread) infoMessage.updateCallInfoMessage(.missed, using: transaction) + SSKEnvironment.shared.notificationsManager?.notifyUser(forIncomingCall: infoMessage, in: thread, transaction: transaction) } break } diff --git a/SignalUtilitiesKit/Utilities/NoopNotificationsManager.swift b/SignalUtilitiesKit/Utilities/NoopNotificationsManager.swift index ffe624689..86b11b0b9 100644 --- a/SignalUtilitiesKit/Utilities/NoopNotificationsManager.swift +++ b/SignalUtilitiesKit/Utilities/NoopNotificationsManager.swift @@ -9,6 +9,10 @@ public class NoopNotificationsManager: NSObject, NotificationsProtocol { owsFailDebug("") } + public func notifyUser(forIncomingCall callInfoMessage: TSInfoMessage, in thread: TSThread, transaction: YapDatabaseReadTransaction) { + owsFailDebug("") + } + public func cancelNotification(_ identifier: String) { owsFailDebug("") }