推送支持持续响铃

This commit is contained in:
Fin 2024-07-26 16:32:00 +08:00
parent 9252cfa8e1
commit 78f2c8a5c8
14 changed files with 287 additions and 37 deletions

View File

@ -126,6 +126,39 @@
06C595362481160F006B98F3 /* BKLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C595352481160F006B98F3 /* BKLabel.swift */; };
06CF784721C7A50300A052D7 /* NotificationServiceExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 06CF784021C7A50300A052D7 /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
06CF784C21C7A51200A052D7 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06CF784B21C7A51200A052D7 /* NotificationService.swift */; };
06D69E202C1159E200161A35 /* glass.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06320500250B6DD3001561EC /* glass.caf */; };
06D69E212C1159E200161A35 /* sherwoodforest.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204F5250B6DD2001561EC /* sherwoodforest.caf */; };
06D69E222C1159E200161A35 /* ladder.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204F9250B6DD2001561EC /* ladder.caf */; };
06D69E232C1159E200161A35 /* chime.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06320509250B6DD4001561EC /* chime.caf */; };
06D69E242C1159E200161A35 /* anticipate.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204FE250B6DD2001561EC /* anticipate.caf */; };
06D69E252C1159E200161A35 /* update.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06320504250B6DD3001561EC /* update.caf */; };
06D69E262C1159E200161A35 /* suspense.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06320506250B6DD3001561EC /* suspense.caf */; };
06D69E272C1159E200161A35 /* newmail.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06320503250B6DD3001561EC /* newmail.caf */; };
06D69E282C1159E200161A35 /* noir.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06320508250B6DD3001561EC /* noir.caf */; };
06D69E292C1159E200161A35 /* birdsong.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204FC250B6DD2001561EC /* birdsong.caf */; };
06D69E2A2C1159E200161A35 /* minuet.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06320505250B6DD3001561EC /* minuet.caf */; };
06D69E2B2C1159E200161A35 /* shake.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204F2250B6DD1001561EC /* shake.caf */; };
06D69E2C2C1159E200161A35 /* newsflash.caf in Resources */ = {isa = PBXBuildFile; fileRef = 0632050E250B6DD4001561EC /* newsflash.caf */; };
06D69E2D2C1159E200161A35 /* paymentsuccess.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204F1250B6DD1001561EC /* paymentsuccess.caf */; };
06D69E2E2C1159E200161A35 /* descent.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204F8250B6DD2001561EC /* descent.caf */; };
06D69E2F2C1159E200161A35 /* mailsent.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06320507250B6DD3001561EC /* mailsent.caf */; };
06D69E302C1159E200161A35 /* tiptoes.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204FA250B6DD2001561EC /* tiptoes.caf */; };
06D69E312C1159E200161A35 /* telegraph.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06320501250B6DD3001561EC /* telegraph.caf */; };
06D69E322C1159E200161A35 /* healthnotification.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204F6250B6DD2001561EC /* healthnotification.caf */; };
06D69E332C1159E200161A35 /* typewriters.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204FD250B6DD2001561EC /* typewriters.caf */; };
06D69E342C1159E200161A35 /* bell.caf in Resources */ = {isa = PBXBuildFile; fileRef = 0632050C250B6DD4001561EC /* bell.caf */; };
06D69E352C1159E200161A35 /* bloom.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204F4250B6DD1001561EC /* bloom.caf */; };
06D69E362C1159E200161A35 /* spell.caf in Resources */ = {isa = PBXBuildFile; fileRef = 0632050A250B6DD4001561EC /* spell.caf */; };
06D69E372C1159E200161A35 /* choo.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204FF250B6DD3001561EC /* choo.caf */; };
06D69E382C1159E200161A35 /* multiwayinvitation.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06320502250B6DD3001561EC /* multiwayinvitation.caf */; };
06D69E392C1159E200161A35 /* horn.caf in Resources */ = {isa = PBXBuildFile; fileRef = 0632050D250B6DD4001561EC /* horn.caf */; };
06D69E3A2C1159E200161A35 /* electronic.caf in Resources */ = {isa = PBXBuildFile; fileRef = 0632050B250B6DD4001561EC /* electronic.caf */; };
06D69E3B2C1159E200161A35 /* calypso.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204F7250B6DD2001561EC /* calypso.caf */; };
06D69E3C2C1159E200161A35 /* gotosleep.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204F0250B6DD1001561EC /* gotosleep.caf */; };
06D69E3D2C1159E200161A35 /* silence.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06BBB8CD2567B8E60076F63E /* silence.caf */; };
06D69E3E2C1159E200161A35 /* alarm.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204F3250B6DD1001561EC /* alarm.caf */; };
06D69E3F2C1159E200161A35 /* fanfare.caf in Resources */ = {isa = PBXBuildFile; fileRef = 063204FB250B6DD2001561EC /* fanfare.caf */; };
06D69E412C11983E00161A35 /* CallProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06D69E402C11983E00161A35 /* CallProcessor.swift */; };
06E944682C06E40600AC86AB /* NotificationContentProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06E944672C06E40600AC86AB /* NotificationContentProcessor.swift */; };
06E9446A2C06E4A200AC86AB /* CiphertextProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06E944692C06E4A200AC86AB /* CiphertextProcessor.swift */; };
06E9446D2C06FEC900AC86AB /* LevelProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06E9446C2C06FEC900AC86AB /* LevelProcessor.swift */; };
@ -149,6 +182,8 @@
06F08EAD29B1DED6006AB9CA /* NSLocalizedString+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F08EAB29B1DECD006AB9CA /* NSLocalizedString+Extension.swift */; };
06F08EAF29B5D9FF006AB9CA /* HUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F08EAE29B5D9FF006AB9CA /* HUD.swift */; };
06F11E7727D9D5FB00F00298 /* QRScannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06F11E7627D9D5FB00F00298 /* QRScannerViewController.swift */; };
06FB04042C53575400F3A213 /* SharedDefines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FB04032C53575400F3A213 /* SharedDefines.swift */; };
06FB04052C53575400F3A213 /* SharedDefines.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06FB04032C53575400F3A213 /* SharedDefines.swift */; };
3428272069AFAFE2C683FEB0 /* libPods-Bark.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CCC722470308049D180876C7 /* libPods-Bark.a */; };
879AE4D4178855A9672009E4 /* libPods-NotificationServiceExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B7F8BDFAA047451561798F58 /* libPods-NotificationServiceExtension.a */; };
B963F7D5BA7AC2571E71EF66 /* libPods-BarkTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 76381A752CCCD4DA6BB2A566 /* libPods-BarkTests.a */; };
@ -316,6 +351,7 @@
06CF784021C7A50300A052D7 /* NotificationServiceExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationServiceExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
06CF784421C7A50300A052D7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
06CF784B21C7A51200A052D7 /* NotificationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
06D69E402C11983E00161A35 /* CallProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallProcessor.swift; sourceTree = "<group>"; };
06E944672C06E40600AC86AB /* NotificationContentProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationContentProcessor.swift; sourceTree = "<group>"; };
06E944692C06E4A200AC86AB /* CiphertextProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CiphertextProcessor.swift; sourceTree = "<group>"; };
06E9446C2C06FEC900AC86AB /* LevelProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LevelProcessor.swift; sourceTree = "<group>"; };
@ -336,6 +372,7 @@
06F08EAB29B1DECD006AB9CA /* NSLocalizedString+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSLocalizedString+Extension.swift"; sourceTree = "<group>"; };
06F08EAE29B5D9FF006AB9CA /* HUD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUD.swift; sourceTree = "<group>"; };
06F11E7627D9D5FB00F00298 /* QRScannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRScannerViewController.swift; sourceTree = "<group>"; };
06FB04032C53575400F3A213 /* SharedDefines.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedDefines.swift; sourceTree = "<group>"; };
121D9B1ED4E8D26F345BC5C0 /* Pods-BarkTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BarkTests.release.xcconfig"; path = "Target Support Files/Pods-BarkTests/Pods-BarkTests.release.xcconfig"; sourceTree = "<group>"; };
138CE8CB688587E893BC5C44 /* Pods-Bark.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Bark.debug.xcconfig"; path = "Target Support Files/Pods-Bark/Pods-Bark.debug.xcconfig"; sourceTree = "<group>"; };
519481D715B40109627E1B49 /* Pods-NotificationServiceExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NotificationServiceExtension.release.xcconfig"; path = "Target Support Files/Pods-NotificationServiceExtension/Pods-NotificationServiceExtension.release.xcconfig"; sourceTree = "<group>"; };
@ -519,6 +556,7 @@
065BE44A2563D8E1002A8CA4 /* Reusable.swift */,
0637FA8520E0AB6600E80174 /* UIColor+Extension.swift */,
0603706620E1E31600F4CA05 /* Defines.swift */,
06FB04032C53575400F3A213 /* SharedDefines.swift */,
0603706A20E20A7C00F4CA05 /* String+Extension.swift */,
0637FA7F20E0981E00E80174 /* BarkSettings.swift */,
0604F7DE20620D4900B32F09 /* ServerManager.swift */,
@ -612,6 +650,7 @@
06E944722C06FF9200AC86AB /* ArchiveProcessor.swift */,
06E944772C0701F300AC86AB /* IconProcessor.swift */,
06E9447B2C07052F00AC86AB /* ImageProcessor.swift */,
06D69E402C11983E00161A35 /* CallProcessor.swift */,
06E944792C0704E500AC86AB /* ImageDownloader.swift */,
);
path = Processor;
@ -850,6 +889,38 @@
buildActionMask = 2147483647;
files = (
06F08EAA29B1DE9F006AB9CA /* Localizable.strings in Resources */,
06D69E202C1159E200161A35 /* glass.caf in Resources */,
06D69E212C1159E200161A35 /* sherwoodforest.caf in Resources */,
06D69E222C1159E200161A35 /* ladder.caf in Resources */,
06D69E232C1159E200161A35 /* chime.caf in Resources */,
06D69E242C1159E200161A35 /* anticipate.caf in Resources */,
06D69E252C1159E200161A35 /* update.caf in Resources */,
06D69E262C1159E200161A35 /* suspense.caf in Resources */,
06D69E272C1159E200161A35 /* newmail.caf in Resources */,
06D69E282C1159E200161A35 /* noir.caf in Resources */,
06D69E292C1159E200161A35 /* birdsong.caf in Resources */,
06D69E2A2C1159E200161A35 /* minuet.caf in Resources */,
06D69E2B2C1159E200161A35 /* shake.caf in Resources */,
06D69E2C2C1159E200161A35 /* newsflash.caf in Resources */,
06D69E2D2C1159E200161A35 /* paymentsuccess.caf in Resources */,
06D69E2E2C1159E200161A35 /* descent.caf in Resources */,
06D69E2F2C1159E200161A35 /* mailsent.caf in Resources */,
06D69E302C1159E200161A35 /* tiptoes.caf in Resources */,
06D69E312C1159E200161A35 /* telegraph.caf in Resources */,
06D69E322C1159E200161A35 /* healthnotification.caf in Resources */,
06D69E332C1159E200161A35 /* typewriters.caf in Resources */,
06D69E342C1159E200161A35 /* bell.caf in Resources */,
06D69E352C1159E200161A35 /* bloom.caf in Resources */,
06D69E362C1159E200161A35 /* spell.caf in Resources */,
06D69E372C1159E200161A35 /* choo.caf in Resources */,
06D69E382C1159E200161A35 /* multiwayinvitation.caf in Resources */,
06D69E392C1159E200161A35 /* horn.caf in Resources */,
06D69E3A2C1159E200161A35 /* electronic.caf in Resources */,
06D69E3B2C1159E200161A35 /* calypso.caf in Resources */,
06D69E3C2C1159E200161A35 /* gotosleep.caf in Resources */,
06D69E3D2C1159E200161A35 /* silence.caf in Resources */,
06D69E3E2C1159E200161A35 /* alarm.caf in Resources */,
06D69E3F2C1159E200161A35 /* fanfare.caf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1053,6 +1124,7 @@
06B1158F247BB1FB006D91FB /* Message.swift in Sources */,
06172FDA27F6DAEF002333A4 /* ServerListTableViewCell.swift in Sources */,
0653677829B727A60038BDB8 /* CryptoSettingRelay.swift in Sources */,
06FB04042C53575400F3A213 /* SharedDefines.swift in Sources */,
061894C529962EB900E001C2 /* GradientButton.swift in Sources */,
06C595362481160F006B98F3 /* BKLabel.swift in Sources */,
0637FA7820E0926D00E80174 /* BarkTargetType.swift in Sources */,
@ -1112,6 +1184,7 @@
06E944732C06FF9200AC86AB /* ArchiveProcessor.swift in Sources */,
06E9447A2C0704E500AC86AB /* ImageDownloader.swift in Sources */,
06CF784C21C7A51200A052D7 /* NotificationService.swift in Sources */,
06FB04052C53575400F3A213 /* SharedDefines.swift in Sources */,
06F08EAD29B1DED6006AB9CA /* NSLocalizedString+Extension.swift in Sources */,
0653677629B719BC0038BDB8 /* CryptoSettingManager.swift in Sources */,
06E9446F2C06FF1E00AC86AB /* BadgeProcessor.swift in Sources */,
@ -1120,6 +1193,7 @@
06F08EA529B1DDA7006AB9CA /* Algorithm.swift in Sources */,
06E944782C0701F300AC86AB /* IconProcessor.swift in Sources */,
06BBB89125650CCF0076F63E /* ArchiveSettingManager.swift in Sources */,
06D69E412C11983E00161A35 /* CallProcessor.swift in Sources */,
06B11591247BC132006D91FB /* Message.swift in Sources */,
06E9447C2C07052F00AC86AB /* ImageProcessor.swift in Sources */,
06E9446A2C06E4A200AC86AB /* CiphertextProcessor.swift in Sources */,

View File

@ -8,7 +8,6 @@
import CloudKit
import CrashReporter
//import IceCream
import IQKeyboardManagerSwift
import Material
import UIKit
@ -34,7 +33,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.backgroundColor = UIColor.black
@ -140,24 +138,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
ServerManager.shared.syncAllServers()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
notificatonHandler(userInfo: notification.request.content.userInfo)
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
notificatonHandler(userInfo: response.notification.request.content.userInfo)
}
// func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// if let dict = userInfo as? [String: NSObject],
// let notification = CKNotification(fromRemoteNotificationDictionary: dict),
// let subscriptionID = notification.subscriptionID, IceCreamSubscription.allIDs.contains(subscriptionID)
// {
// NotificationCenter.default.post(name: Notifications.cloudKitDataDidChangeRemotely.name, object: nil, userInfo: userInfo)
// completionHandler(.newData)
// }
// }
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
if UIApplication.shared.applicationState == .active {
stopCallNotificationProcessor()
}
return .alert
}
private func notificatonHandler(userInfo: [AnyHashable: Any]) {
let navigationController = Client.shared.currentNavigationController
func presentController() {
@ -239,6 +230,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// -1
// 0
UIApplication.shared.applicationIconBadgeNumber = -1
//
stopCallNotificationProcessor()
}
func applicationDidBecomeActive(_ application: UIApplication) {
@ -248,4 +241,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
///
func stopCallNotificationProcessor() {
CFNotificationCenterPostNotification(CFNotificationCenterGetDarwinNotifyCenter(), CFNotificationName(kStopCallProcessorKey as CFString), nil, nil, true)
}
}

View File

@ -0,0 +1,12 @@
//
// SharedDefines.swift
// Bark
//
// Created by huangfeng on 2024/7/26.
// Copyright © 2024 Fin. All rights reserved.
//
import Foundation
let kStopCallProcessorKey = "stopCallProcessorNotification"

View File

@ -60,7 +60,7 @@ class MessageListViewModel: ViewModel, ViewModelType {
return []
}
var messages: [Message] = []
for i in startIndex ..< endIndex {
for i in startIndex..<endIndex {
messages.append(result[i])
}
page += 1
@ -113,8 +113,7 @@ class MessageListViewModel: ViewModel, ViewModelType {
.subscribe(onNext: { filterGroups in
if filterGroups.count <= 0 {
titleRelay.accept(NSLocalizedString("historyMessage"))
}
else {
} else {
titleRelay.accept(filterGroups.map { $0 ?? NSLocalizedString("default") }.joined(separator: " , "))
}
}).disposed(by: rx.disposeBag)
@ -157,8 +156,7 @@ class MessageListViewModel: ViewModel, ViewModelType {
if var section = messagesRelay.value.first {
section.messages.append(contentsOf: cellViewModels)
messagesRelay.accept([section])
}
else {
} else {
messagesRelay.accept([MessageSection(header: "model", messages: cellViewModels)])
}
}).disposed(by: rx.disposeBag)
@ -177,7 +175,6 @@ class MessageListViewModel: ViewModel, ViewModelType {
}
}).disposed(by: rx.disposeBag)
//
input.delete.drive(onNext: { [weak self] type in
guard let strongSelf = self else { return }
@ -199,7 +196,6 @@ class MessageListViewModel: ViewModel, ViewModelType {
return
}
// 使icecream
//
// try? realm.write {
@ -208,6 +204,9 @@ class MessageListViewModel: ViewModel, ViewModelType {
// }
// }
try? realm.write {
for msg in messages {
msg.isDeleted = true
}
realm.delete(messages)
}
}

View File

@ -9,6 +9,11 @@
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
/// Processor
var currentNotificationProcessor: NotificationContentProcessor? = nil
/// ContentHandler serviceExtensionTimeWillExpire Processor
var currentContentHandler: ((UNNotificationContent) -> Void)? = nil
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
Task {
guard var bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) else {
@ -16,6 +21,9 @@ class NotificationService: UNNotificationServiceExtension {
return
}
// processor
// ciphertext
// call Processor 退 ServiceExtension
let processors: [NotificationContentProcessorItem] = [
.ciphertext,
.level,
@ -23,19 +31,30 @@ class NotificationService: UNNotificationServiceExtension {
.autoCopy,
.archive,
.setIcon,
.setImage
.setImage,
.call
]
for item in processors {
// processor
for processor in processors.map({ $0.processor }) {
do {
bestAttemptContent = try await item.processor.process(content: bestAttemptContent)
self.currentNotificationProcessor = processor
bestAttemptContent = try await processor.process(identifier: request.identifier, content: bestAttemptContent)
} catch NotificationContentProcessorError.error(let content) {
contentHandler(content)
return
}
}
//
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
if let handler = self.currentContentHandler {
self.currentNotificationProcessor?.serviceExtensionTimeWillExpire(contentHandler: handler)
}
super.serviceExtensionTimeWillExpire()
}
}

View File

@ -15,7 +15,7 @@ class ArchiveProcessor: NotificationContentProcessor {
return try? Realm()
}()
func process(content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
func process(identifier: String, content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
let userInfo = bestAttemptContent.userInfo
var isArchive: Bool = ArchiveSettingManager.shared.isArchive

View File

@ -9,7 +9,7 @@
import Foundation
class AutoCopyProcessor: NotificationContentProcessor {
func process(content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
func process(identifier: String, content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
let userInfo = bestAttemptContent.userInfo
if userInfo["autocopy"] as? String == "1"
|| userInfo["automaticallycopy"] as? String == "1"

View File

@ -10,7 +10,7 @@ import Foundation
///
class BadgeProcessor: NotificationContentProcessor {
func process(content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
func process(identifier: String, content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
if let badgeStr = bestAttemptContent.userInfo["badge"] as? String, let badge = Int(badgeStr) {
bestAttemptContent.badge = NSNumber(value: badge)
}

View File

@ -0,0 +1,130 @@
//
// CallProcessor.swift
// NotificationServiceExtension
//
// Created by huangfeng on 2024/6/6.
// Copyright © 2024 Fin. All rights reserved.
//
import AudioToolbox
import Foundation
class CallProcessor: NotificationContentProcessor {
///
var soundID: SystemSoundID = 0
/// content
var content: UNMutableNotificationContent? = nil
/// APP
var needsStop = false
func process(identifier: String, content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
guard let call = bestAttemptContent.userInfo["call"] as? String, call == "1" else {
return bestAttemptContent
}
self.content = bestAttemptContent
self.registerObserver()
self.sendLocalNotification(identifier: identifier, content: bestAttemptContent)
await startAudioWork()
return bestAttemptContent
}
func serviceExtensionTimeWillExpire(contentHandler: (UNNotificationContent) -> Void) {
if let content {
contentHandler(content)
}
}
///
private func sendLocalNotification(identifier: String, content: UNMutableNotificationContent) {
// id使APNS
content.sound = nil
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request)
}
// startAudioWork(completion:)
private func startAudioWork() async {
return await withCheckedContinuation { continuation in
self.startAudioWork {
continuation.resume()
}
}
}
///
var startAudioWorkCompletion: (() -> Void)? = nil
///
private func startAudioWork(completion: @escaping () -> Void) {
guard let content else {
completion()
return
}
self.startAudioWorkCompletion = completion
let sound = ((content.userInfo["aps"] as? [String: Any])?["sound"] as? String)?.split(separator: ".")
let soundName = sound?.first ?? "multiwayinvitation"
let soundType = sound?.last ?? "caf"
//
guard let audioPath = getSoundInCustomSoundsDirectory(soundName: "\(soundName).\(soundType)") ??
Bundle.main.path(forResource: String(soundName), ofType: String(soundType))
else {
completion()
return
}
let fileUrl = URL(string: audioPath)
//
AudioServicesCreateSystemSoundID(fileUrl! as CFURL, &soundID)
//
AudioServicesPlayAlertSound(soundID)
//
let selfPointer = unsafeBitCast(self, to: UnsafeMutableRawPointer.self)
AudioServicesAddSystemSoundCompletion(soundID, nil, nil, { sound, clientData in
guard let pointer = clientData else { return }
let processor = unsafeBitCast(pointer, to: CallProcessor.self)
if processor.needsStop {
processor.startAudioWorkCompletion?()
return
}
//
AudioServicesPlayAlertSound(sound)
}, selfPointer)
}
///
private func stopAudioWork() {
AudioServicesRemoveSystemSoundCompletion(soundID)
AudioServicesDisposeSystemSoundID(soundID)
}
///
func registerObserver() {
let notification = CFNotificationCenterGetDarwinNotifyCenter()
let observer = Unmanaged.passUnretained(self).toOpaque()
CFNotificationCenterAddObserver(notification, observer, { _, pointer, _, _, _ in
guard let observer = pointer else { return }
let processor = Unmanaged<CallProcessor>.fromOpaque(observer).takeUnretainedValue()
processor.needsStop = true
}, kStopCallProcessorKey as CFString, nil, .deliverImmediately)
}
func getSoundInCustomSoundsDirectory(soundName: String) -> String? {
// 访APP
guard let soundsDirectoryUrl = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true).first?.appending("/Sounds") else {
return nil
}
let path = soundsDirectoryUrl.appending("/\(soundName)")
if FileManager.default.fileExists(atPath: path) {
return path
}
return nil
}
deinit {
let observer = Unmanaged.passUnretained(self).toOpaque()
let name = CFNotificationName(kStopCallProcessorKey as CFString)
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), observer, name, nil)
}
}

View File

@ -11,7 +11,7 @@ import SwiftyJSON
///
class CiphertextProcessor: NotificationContentProcessor {
func process(content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
func process(identifier: String, content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
var userInfo = bestAttemptContent.userInfo
guard let ciphertext = userInfo["ciphertext"] as? String else {
return bestAttemptContent
@ -22,6 +22,7 @@ class CiphertextProcessor: NotificationContentProcessor {
var map = try decrypt(ciphertext: ciphertext, iv: userInfo["iv"] as? String)
var alert = [String: Any]()
var soundName: String? = nil
if let title = map["title"] as? String {
bestAttemptContent.title = title
alert["title"] = title
@ -37,13 +38,18 @@ class CiphertextProcessor: NotificationContentProcessor {
if !sound.hasSuffix(".caf") {
sound = "\(sound).caf"
}
soundName = sound
bestAttemptContent.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: sound))
}
if let badge = map["badge"] as? Int {
bestAttemptContent.badge = badge as NSNumber
}
map["aps"] = ["alert": alert]
var aps: [String: Any] = ["alert": alert]
if let soundName {
aps["sound"] = soundName
}
map["aps"] = aps
userInfo = map
bestAttemptContent.userInfo = userInfo
return bestAttemptContent

View File

@ -10,7 +10,7 @@ import Foundation
import Intents
class IconProcessor: NotificationContentProcessor {
func process(content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
func process(identifier: String, content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
if #available(iOSApplicationExtension 15.0, *) {
let userInfo = bestAttemptContent.userInfo

View File

@ -10,7 +10,7 @@ import Foundation
import MobileCoreServices
class ImageProcessor: NotificationContentProcessor {
func process(content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
func process(identifier: String, content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
let userInfo = bestAttemptContent.userInfo
guard let imageUrl = userInfo["image"] as? String,
let imageFileUrl = await ImageDownloader.downloadImage(imageUrl)

View File

@ -10,7 +10,7 @@ import Foundation
///
class LevelProcessor: NotificationContentProcessor {
func process(content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
func process(identifier: String, content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent {
if #available(iOSApplicationExtension 15.0, *) {
if let level = bestAttemptContent.userInfo["level"] as? String {
let interruptionLevels: [String: UNNotificationInterruptionLevel] = [

View File

@ -17,6 +17,7 @@ enum NotificationContentProcessorItem {
case archive
case setIcon
case setImage
case call
var processor: NotificationContentProcessor {
switch self {
@ -34,6 +35,8 @@ enum NotificationContentProcessorItem {
return IconProcessor()
case .setImage:
return ImageProcessor()
case .call:
return CallProcessor()
}
}
}
@ -44,8 +47,17 @@ enum NotificationContentProcessorError: Swift.Error {
public protocol NotificationContentProcessor {
/// UNMutableNotificationContent
/// - Parameter bestAttemptContent: UNMutableNotificationContent
/// - Parameters:
/// - identifier: request.identifier, Processor CallProcessor LocalNotification
/// - bestAttemptContent: UNMutableNotificationContent
/// - Returns: UNMutableNotificationContent
/// - Throws:
func process(content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent
func process(identifier: String, content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent
/// serviceExtension processor contentHandler
func serviceExtensionTimeWillExpire(contentHandler: (UNNotificationContent) -> Void)
}
extension NotificationContentProcessor {
func serviceExtensionTimeWillExpire(contentHandler: (UNNotificationContent) -> Void) {}
}