NotificationManager.swift 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import Foundation
  2. import UserNotifications
  3. import DcCore
  4. import UIKit
  5. public class NotificationManager {
  6. var incomingMsgObserver: NSObjectProtocol?
  7. var msgsNoticedObserver: NSObjectProtocol?
  8. init() {
  9. initIncomingMsgsObserver()
  10. initMsgsNoticedObserver()
  11. }
  12. public static func updateApplicationIconBadge(reset: Bool) {
  13. var unreadMessages = 0
  14. if !reset {
  15. unreadMessages = DcContext.shared.getFreshMessages().count
  16. }
  17. DispatchQueue.main.async {
  18. UIApplication.shared.applicationIconBadgeNumber = unreadMessages
  19. }
  20. }
  21. public static func removeAllNotifications() {
  22. let nc = UNUserNotificationCenter.current()
  23. nc.removeAllDeliveredNotifications()
  24. nc.removeAllPendingNotificationRequests()
  25. }
  26. public static func removeNotificationsForChat(chatId: Int) {
  27. DispatchQueue.global(qos: .background).async {
  28. NotificationManager.removePendingNotificationsFor(chatId: chatId)
  29. NotificationManager.removeDeliveredNotificationsFor(chatId: chatId)
  30. NotificationManager.updateApplicationIconBadge(reset: false)
  31. }
  32. }
  33. private func initIncomingMsgsObserver() {
  34. incomingMsgObserver = NotificationCenter.default.addObserver(
  35. forName: dcNotificationIncoming,
  36. object: nil, queue: OperationQueue.main
  37. ) { notification in
  38. DispatchQueue.global(qos: .background).async {
  39. if let ui = notification.userInfo,
  40. let chatId = ui["chat_id"] as? Int,
  41. let messageId = ui["message_id"] as? Int,
  42. !UserDefaults.standard.bool(forKey: "notifications_disabled") {
  43. NotificationManager.updateApplicationIconBadge(reset: false)
  44. let chat = DcContext.shared.getChat(chatId: chatId)
  45. if chat.isMuted {
  46. return
  47. }
  48. let content = UNMutableNotificationContent()
  49. let msg = DcMsg(id: messageId)
  50. content.title = chat.isGroup ? chat.name : msg.getSenderName(msg.fromContact)
  51. content.body = msg.summary(chars: 80) ?? ""
  52. content.subtitle = chat.isGroup ? msg.getSenderName(msg.fromContact) : ""
  53. content.userInfo = ui
  54. content.sound = .default
  55. if msg.type == DC_MSG_IMAGE || msg.type == DC_MSG_GIF,
  56. let url = msg.fileURL {
  57. do {
  58. // make a copy of the file first since UNNotificationAttachment will move attached files into the attachment data store
  59. // so that they can be accessed by all of the appropriate processes
  60. let tempUrl = url.deletingLastPathComponent()
  61. .appendingPathComponent("notification_tmp")
  62. .appendingPathExtension(url.pathExtension)
  63. try FileManager.default.copyItem(at: url, to: tempUrl)
  64. if let attachment = try? UNNotificationAttachment(identifier: Constants.notificationIdentifier, url: tempUrl, options: nil) {
  65. content.attachments = [attachment]
  66. }
  67. } catch let error {
  68. logger.error("Failed to copy file \(url) for notification preview generation: \(error)")
  69. }
  70. }
  71. let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
  72. let accountEmail = DcContact(id: Int(DC_CONTACT_ID_SELF)).email
  73. if #available(iOS 12.0, *) {
  74. content.threadIdentifier = "\(accountEmail)\(chatId)"
  75. }
  76. let request = UNNotificationRequest(identifier: "\(Constants.notificationIdentifier).\(accountEmail).\(chatId).\(msg.messageId)",
  77. content: content,
  78. trigger: trigger)
  79. UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
  80. logger.info("notifications: added \(content.title) \(content.body) \(content.userInfo)")
  81. }
  82. }
  83. }
  84. }
  85. private func initMsgsNoticedObserver() {
  86. msgsNoticedObserver = NotificationCenter.default.addObserver(
  87. forName: dcMsgsNoticed,
  88. object: nil, queue: OperationQueue.main
  89. ) { notification in
  90. if !UserDefaults.standard.bool(forKey: "notifications_disabled"),
  91. let ui = notification.userInfo,
  92. let chatId = ui["chat_id"] as? Int {
  93. NotificationManager.removeNotificationsForChat(chatId: chatId)
  94. }
  95. }
  96. }
  97. private static func removeDeliveredNotificationsFor(chatId: Int) {
  98. var identifiers = [String]()
  99. let nc = UNUserNotificationCenter.current()
  100. nc.getDeliveredNotifications { notifications in
  101. let accountEmail = DcContact(id: Int(DC_CONTACT_ID_SELF)).email
  102. for notification in notifications {
  103. if !notification.request.identifier.containsExact(subSequence: "\(Constants.notificationIdentifier).\(accountEmail).\(chatId)").isEmpty {
  104. identifiers.append(notification.request.identifier)
  105. }
  106. }
  107. nc.removeDeliveredNotifications(withIdentifiers: identifiers)
  108. }
  109. }
  110. private static func removePendingNotificationsFor(chatId: Int) {
  111. var identifiers = [String]()
  112. let nc = UNUserNotificationCenter.current()
  113. nc.getPendingNotificationRequests { notificationRequests in
  114. let accountEmail = DcContact(id: Int(DC_CONTACT_ID_SELF)).email
  115. for request in notificationRequests {
  116. if !request.identifier.containsExact(subSequence: "\(Constants.notificationIdentifier).\(accountEmail).\(chatId)").isEmpty {
  117. identifiers.append(request.identifier)
  118. }
  119. }
  120. nc.removePendingNotificationRequests(withIdentifiers: identifiers)
  121. }
  122. }
  123. deinit {
  124. NotificationCenter.default.removeObserver(self)
  125. }
  126. }