NotificationResponse.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2018-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. import Foundation
  21. @objc class NotificationResponse: NSObject {
  22. @objc var threemaDict: [AnyHashable : Any]?
  23. @objc var categoryIdentifier: String
  24. @objc var actionIdentifier: String
  25. @objc var userText: String?
  26. @objc var identity: String?
  27. @objc var messageId: String?
  28. @objc var userInfo: [AnyHashable : Any]
  29. @objc var notificationIdentifier: String
  30. @objc var completionHandler: (()->Void)
  31. @objc init(response: UNNotificationResponse, completion: @escaping (()->Void)) {
  32. if let tmpThreemaDict = response.notification.request.content.userInfo["threema"] as? [AnyHashable : Any] {
  33. threemaDict = PushPayloadDecryptor.decryptPushPayload(tmpThreemaDict)
  34. categoryIdentifier = response.notification.request.content.categoryIdentifier
  35. actionIdentifier = response.actionIdentifier
  36. completionHandler = completion
  37. userInfo = response.notification.request.content.userInfo
  38. if let mid = threemaDict!["messageId"] as? String {
  39. messageId = mid
  40. notificationIdentifier = String.init(format: "%@%@", kAppPushReplyBackgroundTask, messageId!)
  41. } else {
  42. notificationIdentifier = kAppPushReplyBackgroundTask
  43. }
  44. identity = response.notification.request.identifier
  45. if let replyText = (response as? UNTextInputNotificationResponse)?.userText {
  46. userText = replyText
  47. }
  48. } else {
  49. threemaDict = nil
  50. categoryIdentifier = response.notification.request.content.categoryIdentifier
  51. actionIdentifier = response.actionIdentifier
  52. userInfo = response.notification.request.content.userInfo
  53. notificationIdentifier = kAppPushReplyBackgroundTask
  54. completionHandler = completion
  55. }
  56. }
  57. @objc func handleNotificationResponse() {
  58. BackgroundTaskManager.shared.newBackgroundTask(key: notificationIdentifier, timeout: Int(kAppPushReplyBackgroundTaskTime)) {
  59. // connect all running sessions
  60. if WCSessionManager.shared.isRunningWCSession() {
  61. ValidationLogger.shared()?.logString("Threema Web: handleNotificationResponse --> connect all running sessions")
  62. }
  63. WCSessionManager.shared.connectAllRunningSessions()
  64. self.handleResponse()
  65. }
  66. }
  67. private func handleResponse() {
  68. if self.categoryIdentifier == "SINGLE" || self.categoryIdentifier == "GROUP" {
  69. AppGroup.setActive(false, for: AppGroupTypeShareExtension)
  70. ServerConnector.shared().connect()
  71. if self.actionIdentifier == "THUMB_UP" {
  72. self.handleThumbUp()
  73. }
  74. else if self.actionIdentifier == "THUMB_DOWN" {
  75. self.handleThumbDown()
  76. }
  77. else if self.actionIdentifier == "REPLY_MESSAGE" {
  78. self.handleReplyMessage()
  79. } else {
  80. AppDelegate.shared().handleRemoteNotification(self.userInfo, receivedWhileRunning: AppDelegate.shared().active, notification: nil)
  81. self.finishResponse()
  82. }
  83. }
  84. else if self.categoryIdentifier == "CALL" {
  85. if self.actionIdentifier == "REPLY_MESSAGE" {
  86. self.handleReplyMessage()
  87. }
  88. else if self.actionIdentifier == "CALL" {
  89. self.handleCallMessage()
  90. }
  91. else {
  92. self.finishResponse()
  93. }
  94. }
  95. else if self.categoryIdentifier == "INCOMCALL" {
  96. if self.actionIdentifier == "ACCEPTCALL" {
  97. self.handleAcceptCall()
  98. }
  99. else if self.actionIdentifier == "REJECTCALL" {
  100. self.handleRejectCall()
  101. } else {
  102. self.finishResponse()
  103. }
  104. }
  105. else if self.categoryIdentifier == "SAFE_SETUP" {
  106. self.handleSafeSetup()
  107. self.finishResponse()
  108. }
  109. else {
  110. AppDelegate.shared().handleRemoteNotification(self.userInfo, receivedWhileRunning: false, notification: nil)
  111. self.finishResponse()
  112. }
  113. }
  114. private func finishResponse() {
  115. NotificationManager.sharedInstance()?.updateUnreadMessagesCount(false)
  116. BackgroundTaskManager.shared.cancelBackgroundTask(key: notificationIdentifier)
  117. self.completionHandler()
  118. }
  119. private func handleThumbUp() {
  120. waitUntilConnectedTimeout(count: 20, onConnect: {
  121. let entityManager = EntityManager()
  122. let baseMessage = entityManager.entityFetcher.message(withId: self.messageId!.decodeHex())
  123. if baseMessage != nil {
  124. let conversation = baseMessage!.conversation
  125. if baseMessage!.userackDate == nil || baseMessage!.userack.boolValue != true {
  126. let unreadMessageCount = conversation!.unreadMessageCount
  127. entityManager.performSyncBlockAndSafe({
  128. baseMessage!.read = NSNumber.init(value: true)
  129. baseMessage!.readDate = Date()
  130. conversation?.unreadMessageCount = NSNumber.init(value: unreadMessageCount!.intValue - 1)
  131. })
  132. MessageSender.sendReadReceipt(forMessages: [baseMessage!], toIdentity: conversation?.contact.identity, async: false, quickReply: true)
  133. MessageSender.sendUserAck(forMessages: [baseMessage!], toIdentity: conversation?.contact.identity, async: false, quickReply: true)
  134. entityManager.performSyncBlockAndSafe({
  135. baseMessage!.userack = NSNumber.init(value: true)
  136. baseMessage!.userackDate = Date()
  137. if baseMessage!.id == conversation?.lastMessage.id {
  138. conversation?.lastMessage = baseMessage!
  139. }
  140. })
  141. self.finishResponse()
  142. return
  143. } else {
  144. self.sendThumbUpError()
  145. self.finishResponse()
  146. return
  147. }
  148. } else {
  149. self.sendThumbUpError()
  150. self.finishResponse()
  151. return
  152. }
  153. }) {
  154. self.sendThumbUpError()
  155. self.finishResponse()
  156. return
  157. }
  158. }
  159. private func sendThumbUpError() {
  160. Utils.sendErrorLocalNotification(NSLocalizedString("send_notification_message_error_title", comment: ""), body: NSLocalizedString("send_notification_message_error_agree", comment: ""), userInfo:self.userInfo)
  161. }
  162. private func handleThumbDown() {
  163. waitUntilConnectedTimeout(count: 20, onConnect: {
  164. let entityManager = EntityManager()
  165. let baseMessage = entityManager.entityFetcher.message(withId: self.messageId!.decodeHex())
  166. if baseMessage != nil {
  167. let conversation = baseMessage!.conversation
  168. if baseMessage!.userackDate == nil || baseMessage!.userack.boolValue != false {
  169. let unreadMessageCount = conversation!.unreadMessageCount
  170. entityManager.performSyncBlockAndSafe({
  171. baseMessage!.read = NSNumber.init(value: true)
  172. baseMessage!.readDate = Date()
  173. conversation?.unreadMessageCount = NSNumber.init(value: unreadMessageCount!.intValue - 1)
  174. })
  175. MessageSender.sendReadReceipt(forMessages: [baseMessage!], toIdentity: conversation?.contact.identity, async: false, quickReply: true)
  176. MessageSender.sendUserDecline(forMessages: [baseMessage!], toIdentity: conversation?.contact.identity, async: false, quickReply: true)
  177. entityManager.performSyncBlockAndSafe({
  178. baseMessage!.userack = NSNumber.init(value: false)
  179. baseMessage!.userackDate = Date()
  180. if baseMessage!.id == conversation?.lastMessage.id {
  181. conversation?.lastMessage = baseMessage!
  182. }
  183. })
  184. self.finishResponse()
  185. return
  186. } else {
  187. self.sendThumbDownError()
  188. self.finishResponse()
  189. return
  190. }
  191. } else {
  192. self.sendThumbDownError()
  193. self.finishResponse()
  194. return
  195. }
  196. }) {
  197. self.sendThumbDownError()
  198. self.finishResponse()
  199. return
  200. }
  201. }
  202. private func sendThumbDownError() {
  203. Utils.sendErrorLocalNotification(NSLocalizedString("send_notification_message_error_title", comment: ""), body: NSLocalizedString("send_notification_message_error_disagree", comment: ""), userInfo: self.userInfo)
  204. }
  205. private func handleReplyMessage() {
  206. waitUntilConnectedTimeout(count: 20, onConnect: {
  207. let entityManager = EntityManager()
  208. let baseMessage = entityManager.entityFetcher.message(withId: self.messageId!.decodeHex())
  209. if baseMessage != nil {
  210. let conversation = baseMessage!.conversation
  211. let unreadMessageCount = conversation!.unreadMessageCount
  212. entityManager.performSyncBlockAndSafe({
  213. baseMessage!.read = NSNumber.init(value: true)
  214. baseMessage!.readDate = Date()
  215. conversation?.unreadMessageCount = NSNumber.init(value: unreadMessageCount!.intValue - 1)
  216. })
  217. if !baseMessage!.conversation.isGroup() {
  218. MessageSender.sendReadReceipt(forMessages: [baseMessage!], toIdentity: conversation?.contact.identity, async: false, quickReply: true)
  219. }
  220. let trimmedMessages = Utils.getTrimmedMessages(self.userText) as? [String]
  221. if trimmedMessages == nil {
  222. let trimmedMessageData = self.userText!.data(using: .utf8)
  223. if trimmedMessageData!.count > Int(kMaxMessageLen) {
  224. self.sendReplyError()
  225. self.finishResponse()
  226. return
  227. }
  228. MessageSender.sendMessage(self.userText, in: conversation!, async: false, quickReply: true, requestId: nil, onCompletion: { (message, conv) in
  229. self.finishResponse()
  230. return
  231. })
  232. } else {
  233. for (index, object) in trimmedMessages!.enumerated() {
  234. MessageSender.sendMessage(object, in: conversation!, async: false, quickReply: true, requestId: nil, onCompletion: { (message, conv) in
  235. if index == trimmedMessages!.count - 1 {
  236. self.finishResponse()
  237. return
  238. }
  239. })
  240. }
  241. }
  242. } else {
  243. self.sendReplyError()
  244. self.finishResponse()
  245. return
  246. }
  247. }) {
  248. let entityManager = EntityManager()
  249. let baseMessage = entityManager.entityFetcher.message(withId: self.messageId!.decodeHex())
  250. if baseMessage != nil {
  251. let conversation = baseMessage!.conversation
  252. let trimmedMessages = Utils.getTrimmedMessages(self.userText) as? [String]
  253. if trimmedMessages == nil {
  254. MessageSender.sendMessage(self.userText, in: conversation!, async: false, quickReply: true, requestId: nil, onCompletion: { (message, conv) in
  255. self.sendReplyError()
  256. self.finishResponse()
  257. return
  258. })
  259. } else {
  260. for (index, object) in trimmedMessages!.enumerated() {
  261. MessageSender.sendMessage(object, in: conversation!, async: false, quickReply: true, requestId: nil, onCompletion: { (message, conv) in
  262. if index == trimmedMessages!.count - 1 {
  263. self.sendReplyError()
  264. self.finishResponse()
  265. return
  266. }
  267. })
  268. }
  269. }
  270. } else {
  271. self.sendReplyError()
  272. self.finishResponse()
  273. return
  274. }
  275. }
  276. }
  277. private func sendReplyError() {
  278. Utils.sendErrorLocalNotification(NSLocalizedString("send_notification_message_error_title", comment: ""), body: NSLocalizedString("send_notification_message_error_failed", comment: ""), userInfo: self.userInfo)
  279. }
  280. private func handleCallMessage() {
  281. waitUntilConnectedTimeout(count: 20, onConnect: {
  282. let entityManager = EntityManager()
  283. if let contact = entityManager.entityFetcher.contact(forId: self.identity!) {
  284. var callId: VoIPCallId?
  285. if let threemaDict = self.threemaDict {
  286. if let tmpCallId = threemaDict["callId"] {
  287. callId = VoIPCallId(callId: tmpCallId as? UInt32)
  288. }
  289. }
  290. let action = VoIPCallUserAction.init(action: .call, contact: contact, callId: callId, completion: {
  291. self.finishResponse()
  292. return
  293. })
  294. VoIPCallStateManager.shared.processUserAction(action)
  295. } else {
  296. self.finishResponse()
  297. return
  298. }
  299. }) {
  300. self.finishResponse()
  301. return
  302. }
  303. }
  304. private func handleAcceptCall() {
  305. waitUntilConnectedTimeout(count: 20, onConnect: {
  306. let entityManager = EntityManager()
  307. if let contact = entityManager.entityFetcher.contact(forId: self.identity!) {
  308. var callId: VoIPCallId?
  309. if let threemaDict = self.threemaDict {
  310. if let tmpCallId = threemaDict["callId"] {
  311. callId = VoIPCallId(callId: tmpCallId as? UInt32)
  312. }
  313. }
  314. let action = VoIPCallUserAction.init(action: .accept, contact: contact, callId: callId, completion: {
  315. self.finishResponse()
  316. return
  317. })
  318. VoIPCallStateManager.shared.processUserAction(action)
  319. } else {
  320. self.finishResponse()
  321. return
  322. }
  323. }) {
  324. self.finishResponse()
  325. return
  326. }
  327. }
  328. private func handleRejectCall() {
  329. waitUntilConnectedTimeout(count: 20, onConnect: {
  330. let entityManager = EntityManager()
  331. if let contact = entityManager.entityFetcher.contact(forId: self.identity!) {
  332. var callId: VoIPCallId?
  333. if let threemaDict = self.threemaDict {
  334. if let tmpCallId = threemaDict["callId"] {
  335. callId = VoIPCallId(callId: tmpCallId as? UInt32)
  336. }
  337. }
  338. let action = VoIPCallUserAction.init(action: .reject, contact: contact, callId: callId, completion: {
  339. self.finishResponse()
  340. return
  341. })
  342. VoIPCallStateManager.shared.processUserAction(action)
  343. } else {
  344. self.finishResponse()
  345. return
  346. }
  347. }) {
  348. self.finishResponse()
  349. return
  350. }
  351. }
  352. private func waitUntilConnectedTimeout(count: Int, onConnect: @escaping (()->Void), onTimeout: @escaping (()->Void)) {
  353. if ServerConnector.shared().connectionState == ConnectionStateLoggedIn {
  354. onConnect()
  355. return
  356. }
  357. if count > 0 && AppGroup.getActiveType() == AppGroupTypeApp {
  358. DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
  359. self.waitUntilConnectedTimeout(count: count-1, onConnect: onConnect, onTimeout: onTimeout)
  360. }
  361. } else {
  362. onTimeout()
  363. }
  364. }
  365. private func handleSafeSetup() {
  366. let safeConfig = SafeConfigManager()
  367. let safeManager = SafeManager(safeConfigManager: safeConfig, safeStore: SafeStore(safeConfigManager: safeConfig, serverApiConnector: ServerAPIConnector()), safeApiService: SafeApiService())
  368. if safeManager.isActivated {
  369. NotificationCenter.default.post(name: Notification.Name(kSafeSetupUI), object: nil)
  370. }
  371. }
  372. }