123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- // _____ _
- // |_ _| |_ _ _ ___ ___ _ __ __ _
- // | | | ' \| '_/ -_) -_) ' \/ _` |_
- // |_| |_||_|_| \___\___|_|_|_\__,_(_)
- //
- // Threema iOS Client
- // Copyright (c) 2018-2020 Threema GmbH
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU Affero General Public License, version 3,
- // as published by the Free Software Foundation.
- //
- // 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 Affero General Public License for more details.
- //
- // You should have received a copy of the GNU Affero General Public License
- // along with this program. If not, see <https://www.gnu.org/licenses/>.
- import Foundation
- @objc class NotificationResponse: NSObject {
- @objc var threemaDict: [AnyHashable : Any]?
- @objc var categoryIdentifier: String
- @objc var actionIdentifier: String
- @objc var userText: String?
- @objc var identity: String?
- @objc var messageId: String?
- @objc var userInfo: [AnyHashable : Any]
- @objc var notificationIdentifier: String
- @objc var completionHandler: (()->Void)
- @objc init(response: UNNotificationResponse, completion: @escaping (()->Void)) {
- if let tmpThreemaDict = response.notification.request.content.userInfo["threema"] as? [AnyHashable : Any] {
- threemaDict = PushPayloadDecryptor.decryptPushPayload(tmpThreemaDict)
- categoryIdentifier = response.notification.request.content.categoryIdentifier
- actionIdentifier = response.actionIdentifier
- completionHandler = completion
- userInfo = response.notification.request.content.userInfo
- if let mid = threemaDict!["messageId"] as? String {
- messageId = mid
- notificationIdentifier = String.init(format: "%@%@", kAppPushReplyBackgroundTask, messageId!)
- } else {
- notificationIdentifier = kAppPushReplyBackgroundTask
- }
-
- identity = response.notification.request.identifier
-
- if let replyText = (response as? UNTextInputNotificationResponse)?.userText {
- userText = replyText
- }
- } else {
- threemaDict = nil
- categoryIdentifier = response.notification.request.content.categoryIdentifier
- actionIdentifier = response.actionIdentifier
- userInfo = response.notification.request.content.userInfo
- notificationIdentifier = kAppPushReplyBackgroundTask
- completionHandler = completion
- }
- }
- @objc func handleNotificationResponse() {
- BackgroundTaskManager.shared.newBackgroundTask(key: notificationIdentifier, timeout: Int(kAppPushReplyBackgroundTaskTime)) {
- // connect all running sessions
- if WCSessionManager.shared.isRunningWCSession() {
- ValidationLogger.shared()?.logString("Threema Web: handleNotificationResponse --> connect all running sessions")
- }
- WCSessionManager.shared.connectAllRunningSessions()
-
- self.handleResponse()
- }
- }
- private func handleResponse() {
- if self.categoryIdentifier == "SINGLE" || self.categoryIdentifier == "GROUP" {
- AppGroup.setActive(false, for: AppGroupTypeShareExtension)
- ServerConnector.shared().connect()
- if self.actionIdentifier == "THUMB_UP" {
- self.handleThumbUp()
- }
- else if self.actionIdentifier == "THUMB_DOWN" {
- self.handleThumbDown()
- }
- else if self.actionIdentifier == "REPLY_MESSAGE" {
- self.handleReplyMessage()
- } else {
- AppDelegate.shared().handleRemoteNotification(self.userInfo, receivedWhileRunning: AppDelegate.shared().active, notification: nil)
- self.finishResponse()
- }
- }
- else if self.categoryIdentifier == "CALL" {
- if self.actionIdentifier == "REPLY_MESSAGE" {
- self.handleReplyMessage()
- }
- else if self.actionIdentifier == "CALL" {
- self.handleCallMessage()
- }
- else {
- self.finishResponse()
- }
- }
- else if self.categoryIdentifier == "INCOMCALL" {
- if self.actionIdentifier == "ACCEPTCALL" {
- self.handleAcceptCall()
- }
- else if self.actionIdentifier == "REJECTCALL" {
- self.handleRejectCall()
- } else {
- self.finishResponse()
- }
- }
- else if self.categoryIdentifier == "SAFE_SETUP" {
- self.handleSafeSetup()
- self.finishResponse()
- }
- else {
- AppDelegate.shared().handleRemoteNotification(self.userInfo, receivedWhileRunning: false, notification: nil)
- self.finishResponse()
- }
- }
- private func finishResponse() {
- NotificationManager.sharedInstance()?.updateUnreadMessagesCount(false)
- BackgroundTaskManager.shared.cancelBackgroundTask(key: notificationIdentifier)
- self.completionHandler()
- }
- private func handleThumbUp() {
- waitUntilConnectedTimeout(count: 20, onConnect: {
- let entityManager = EntityManager()
- let baseMessage = entityManager.entityFetcher.message(withId: self.messageId!.decodeHex())
- if baseMessage != nil {
- let conversation = baseMessage!.conversation
- if baseMessage!.userackDate == nil || baseMessage!.userack.boolValue != true {
- let unreadMessageCount = conversation!.unreadMessageCount
- entityManager.performSyncBlockAndSafe({
- baseMessage!.read = NSNumber.init(value: true)
- baseMessage!.readDate = Date()
- conversation?.unreadMessageCount = NSNumber.init(value: unreadMessageCount!.intValue - 1)
- })
- MessageSender.sendReadReceipt(forMessages: [baseMessage!], toIdentity: conversation?.contact.identity, async: false, quickReply: true)
- MessageSender.sendUserAck(forMessages: [baseMessage!], toIdentity: conversation?.contact.identity, async: false, quickReply: true)
- entityManager.performSyncBlockAndSafe({
- baseMessage!.userack = NSNumber.init(value: true)
- baseMessage!.userackDate = Date()
- if baseMessage!.id == conversation?.lastMessage.id {
- conversation?.lastMessage = baseMessage!
- }
- })
- self.finishResponse()
- return
- } else {
- self.sendThumbUpError()
- self.finishResponse()
- return
- }
- } else {
- self.sendThumbUpError()
- self.finishResponse()
- return
- }
- }) {
- self.sendThumbUpError()
- self.finishResponse()
- return
- }
- }
- private func sendThumbUpError() {
- Utils.sendErrorLocalNotification(NSLocalizedString("send_notification_message_error_title", comment: ""), body: NSLocalizedString("send_notification_message_error_agree", comment: ""), userInfo:self.userInfo)
- }
- private func handleThumbDown() {
- waitUntilConnectedTimeout(count: 20, onConnect: {
- let entityManager = EntityManager()
- let baseMessage = entityManager.entityFetcher.message(withId: self.messageId!.decodeHex())
- if baseMessage != nil {
- let conversation = baseMessage!.conversation
- if baseMessage!.userackDate == nil || baseMessage!.userack.boolValue != false {
- let unreadMessageCount = conversation!.unreadMessageCount
- entityManager.performSyncBlockAndSafe({
- baseMessage!.read = NSNumber.init(value: true)
- baseMessage!.readDate = Date()
- conversation?.unreadMessageCount = NSNumber.init(value: unreadMessageCount!.intValue - 1)
- })
- MessageSender.sendReadReceipt(forMessages: [baseMessage!], toIdentity: conversation?.contact.identity, async: false, quickReply: true)
- MessageSender.sendUserDecline(forMessages: [baseMessage!], toIdentity: conversation?.contact.identity, async: false, quickReply: true)
- entityManager.performSyncBlockAndSafe({
- baseMessage!.userack = NSNumber.init(value: false)
- baseMessage!.userackDate = Date()
- if baseMessage!.id == conversation?.lastMessage.id {
- conversation?.lastMessage = baseMessage!
- }
- })
- self.finishResponse()
- return
- } else {
- self.sendThumbDownError()
- self.finishResponse()
- return
- }
- } else {
- self.sendThumbDownError()
- self.finishResponse()
- return
- }
- }) {
- self.sendThumbDownError()
- self.finishResponse()
- return
- }
- }
- private func sendThumbDownError() {
- Utils.sendErrorLocalNotification(NSLocalizedString("send_notification_message_error_title", comment: ""), body: NSLocalizedString("send_notification_message_error_disagree", comment: ""), userInfo: self.userInfo)
- }
- private func handleReplyMessage() {
- waitUntilConnectedTimeout(count: 20, onConnect: {
- let entityManager = EntityManager()
- let baseMessage = entityManager.entityFetcher.message(withId: self.messageId!.decodeHex())
- if baseMessage != nil {
- let conversation = baseMessage!.conversation
- let unreadMessageCount = conversation!.unreadMessageCount
- entityManager.performSyncBlockAndSafe({
- baseMessage!.read = NSNumber.init(value: true)
- baseMessage!.readDate = Date()
- conversation?.unreadMessageCount = NSNumber.init(value: unreadMessageCount!.intValue - 1)
- })
- if !baseMessage!.conversation.isGroup() {
- MessageSender.sendReadReceipt(forMessages: [baseMessage!], toIdentity: conversation?.contact.identity, async: false, quickReply: true)
- }
- let trimmedMessages = Utils.getTrimmedMessages(self.userText) as? [String]
- if trimmedMessages == nil {
- let trimmedMessageData = self.userText!.data(using: .utf8)
- if trimmedMessageData!.count > Int(kMaxMessageLen) {
- self.sendReplyError()
- self.finishResponse()
- return
- }
- MessageSender.sendMessage(self.userText, in: conversation!, async: false, quickReply: true, requestId: nil, onCompletion: { (message, conv) in
- self.finishResponse()
- return
- })
- } else {
- for (index, object) in trimmedMessages!.enumerated() {
- MessageSender.sendMessage(object, in: conversation!, async: false, quickReply: true, requestId: nil, onCompletion: { (message, conv) in
- if index == trimmedMessages!.count - 1 {
- self.finishResponse()
- return
- }
- })
- }
- }
- } else {
- self.sendReplyError()
- self.finishResponse()
- return
- }
- }) {
- let entityManager = EntityManager()
- let baseMessage = entityManager.entityFetcher.message(withId: self.messageId!.decodeHex())
- if baseMessage != nil {
- let conversation = baseMessage!.conversation
- let trimmedMessages = Utils.getTrimmedMessages(self.userText) as? [String]
- if trimmedMessages == nil {
- MessageSender.sendMessage(self.userText, in: conversation!, async: false, quickReply: true, requestId: nil, onCompletion: { (message, conv) in
- self.sendReplyError()
- self.finishResponse()
- return
- })
- } else {
- for (index, object) in trimmedMessages!.enumerated() {
- MessageSender.sendMessage(object, in: conversation!, async: false, quickReply: true, requestId: nil, onCompletion: { (message, conv) in
- if index == trimmedMessages!.count - 1 {
- self.sendReplyError()
- self.finishResponse()
- return
- }
- })
- }
- }
- } else {
- self.sendReplyError()
- self.finishResponse()
- return
- }
- }
- }
- private func sendReplyError() {
- Utils.sendErrorLocalNotification(NSLocalizedString("send_notification_message_error_title", comment: ""), body: NSLocalizedString("send_notification_message_error_failed", comment: ""), userInfo: self.userInfo)
- }
- private func handleCallMessage() {
- waitUntilConnectedTimeout(count: 20, onConnect: {
- let entityManager = EntityManager()
- if let contact = entityManager.entityFetcher.contact(forId: self.identity!) {
- var callId: VoIPCallId?
- if let threemaDict = self.threemaDict {
- if let tmpCallId = threemaDict["callId"] {
- callId = VoIPCallId(callId: tmpCallId as? UInt32)
- }
- }
- let action = VoIPCallUserAction.init(action: .call, contact: contact, callId: callId, completion: {
- self.finishResponse()
- return
- })
- VoIPCallStateManager.shared.processUserAction(action)
- } else {
- self.finishResponse()
- return
- }
- }) {
- self.finishResponse()
- return
- }
- }
- private func handleAcceptCall() {
- waitUntilConnectedTimeout(count: 20, onConnect: {
- let entityManager = EntityManager()
- if let contact = entityManager.entityFetcher.contact(forId: self.identity!) {
- var callId: VoIPCallId?
- if let threemaDict = self.threemaDict {
- if let tmpCallId = threemaDict["callId"] {
- callId = VoIPCallId(callId: tmpCallId as? UInt32)
- }
- }
- let action = VoIPCallUserAction.init(action: .accept, contact: contact, callId: callId, completion: {
- self.finishResponse()
- return
- })
- VoIPCallStateManager.shared.processUserAction(action)
- } else {
- self.finishResponse()
- return
- }
- }) {
- self.finishResponse()
- return
- }
- }
- private func handleRejectCall() {
- waitUntilConnectedTimeout(count: 20, onConnect: {
- let entityManager = EntityManager()
- if let contact = entityManager.entityFetcher.contact(forId: self.identity!) {
- var callId: VoIPCallId?
- if let threemaDict = self.threemaDict {
- if let tmpCallId = threemaDict["callId"] {
- callId = VoIPCallId(callId: tmpCallId as? UInt32)
- }
- }
- let action = VoIPCallUserAction.init(action: .reject, contact: contact, callId: callId, completion: {
- self.finishResponse()
- return
- })
- VoIPCallStateManager.shared.processUserAction(action)
- } else {
- self.finishResponse()
- return
- }
- }) {
- self.finishResponse()
- return
- }
- }
- private func waitUntilConnectedTimeout(count: Int, onConnect: @escaping (()->Void), onTimeout: @escaping (()->Void)) {
- if ServerConnector.shared().connectionState == ConnectionStateLoggedIn {
- onConnect()
- return
- }
- if count > 0 && AppGroup.getActiveType() == AppGroupTypeApp {
- DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
- self.waitUntilConnectedTimeout(count: count-1, onConnect: onConnect, onTimeout: onTimeout)
- }
- } else {
- onTimeout()
- }
- }
- private func handleSafeSetup() {
- let safeConfig = SafeConfigManager()
- let safeManager = SafeManager(safeConfigManager: safeConfig, safeStore: SafeStore(safeConfigManager: safeConfig, serverApiConnector: ServerAPIConnector()), safeApiService: SafeApiService())
- if safeManager.isActivated {
- NotificationCenter.default.post(name: Notification.Name(kSafeSetupUI), object: nil)
- }
- }
- }
|