From 58d56a2d5590b4c8d993e0583d0f22fcd982e6b0 Mon Sep 17 00:00:00 2001 From: Fin Date: Wed, 6 Nov 2024 14:16:09 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8B=E6=8B=89=E6=88=96=E9=95=BF=E6=8C=89?= =?UTF-8?q?=E6=8E=A8=E9=80=81=EF=BC=8C=E5=8F=AF=E4=BB=A5=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E5=AF=B9=E6=8E=A8=E9=80=81=E5=88=86=E7=BB=84=E6=9A=82=E6=97=B6?= =?UTF-8?q?=E9=9D=99=E9=9F=B31=E5=B0=8F=E6=97=B6=E3=80=82=20close:=20#138?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Bark.xcodeproj/project.pbxproj | 16 ++++++++ Bark/AppDelegate.swift | 10 +++-- Bark/Localizable.xcstrings | 34 ++++++++++++++++ Common/GroupMuteSettingManager.swift | 35 +++++++++++++++++ .../NotificationService.swift | 1 + .../Processor/MuteProcessor.swift | 23 +++++++++++ .../NotificationContentProcessor.swift | 3 ++ .../NotificationContentExtension.entitlements | 10 +++++ .../NotificationViewController.swift | 39 +++++++++++++++---- 9 files changed, 160 insertions(+), 11 deletions(-) create mode 100644 Common/GroupMuteSettingManager.swift create mode 100644 NotificationServiceExtension/Processor/MuteProcessor.swift create mode 100644 notificationContentExtension/NotificationContentExtension.entitlements diff --git a/Bark.xcodeproj/project.pbxproj b/Bark.xcodeproj/project.pbxproj index 0ecf93b..31b1186 100644 --- a/Bark.xcodeproj/project.pbxproj +++ b/Bark.xcodeproj/project.pbxproj @@ -122,6 +122,10 @@ 06BBB8C12567B3EF0076F63E /* BaseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BBB8C02567B3EF0076F63E /* BaseTableViewCell.swift */; }; 06BBB8C92567B6730076F63E /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BBB8C82567B6730076F63E /* Operators.swift */; }; 06BBB8CE2567B8E60076F63E /* silence.caf in Resources */ = {isa = PBXBuildFile; fileRef = 06BBB8CD2567B8E60076F63E /* silence.caf */; }; + 06BCAE562CDB19260092867A /* GroupMuteSettingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BCAE552CDB19260092867A /* GroupMuteSettingManager.swift */; }; + 06BCAE572CDB19420092867A /* GroupMuteSettingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BCAE552CDB19260092867A /* GroupMuteSettingManager.swift */; }; + 06BCAE582CDB19420092867A /* GroupMuteSettingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BCAE552CDB19260092867A /* GroupMuteSettingManager.swift */; }; + 06BCAE5B2CDB25120092867A /* MuteProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BCAE5A2CDB25120092867A /* MuteProcessor.swift */; }; 06BD4DAA2901352E003364DB /* Object+Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06BD4DA92901352E003364DB /* Object+Dictionary.swift */; }; 06C2CF232685B88D0034B127 /* TextCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2CF222685B88D0034B127 /* TextCell.swift */; }; 06C2CF252685BDB80034B127 /* SpacerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06C2CF242685BDB80034B127 /* SpacerCell.swift */; }; @@ -348,6 +352,9 @@ 06BBB8C02567B3EF0076F63E /* BaseTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTableViewCell.swift; sourceTree = ""; }; 06BBB8C82567B6730076F63E /* Operators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = ""; }; 06BBB8CD2567B8E60076F63E /* silence.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = silence.caf; sourceTree = ""; }; + 06BCAE552CDB19260092867A /* GroupMuteSettingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMuteSettingManager.swift; sourceTree = ""; }; + 06BCAE592CDB19590092867A /* NotificationContentExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NotificationContentExtension.entitlements; sourceTree = ""; }; + 06BCAE5A2CDB25120092867A /* MuteProcessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MuteProcessor.swift; sourceTree = ""; }; 06BD4DA92901352E003364DB /* Object+Dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Object+Dictionary.swift"; sourceTree = ""; }; 06C2CF222685B88D0034B127 /* TextCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextCell.swift; sourceTree = ""; }; 06C2CF242685BDB80034B127 /* SpacerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpacerCell.swift; sourceTree = ""; }; @@ -555,6 +562,7 @@ 0632CE2120EC9098003FDF46 /* NotificationContentExtension */ = { isa = PBXGroup; children = ( + 06BCAE592CDB19590092867A /* NotificationContentExtension.entitlements */, 0632CE2220EC9098003FDF46 /* NotificationViewController.swift */, 0632CE2420EC9098003FDF46 /* MainInterface.storyboard */, 0632CE2720EC9098003FDF46 /* Info.plist */, @@ -587,6 +595,7 @@ 06F08EAB29B1DECD006AB9CA /* NSLocalizedString+Extension.swift */, 06E944742C07012E00AC86AB /* RealmConfiguration.swift */, 0687F2A72CCB791A00B2A52F /* UIFont+Extension.swift */, + 06BCAE552CDB19260092867A /* GroupMuteSettingManager.swift */, ); path = Common; sourceTree = ""; @@ -667,6 +676,7 @@ 06E9447B2C07052F00AC86AB /* ImageProcessor.swift */, 06D69E402C11983E00161A35 /* CallProcessor.swift */, 06E944792C0704E500AC86AB /* ImageDownloader.swift */, + 06BCAE5A2CDB25120092867A /* MuteProcessor.swift */, ); path = Processor; sourceTree = ""; @@ -1110,6 +1120,7 @@ files = ( 0687F2AA2CCB7FA500B2A52F /* UIFont+Extension.swift in Sources */, 0632CE2320EC9098003FDF46 /* NotificationViewController.swift in Sources */, + 06BCAE572CDB19420092867A /* GroupMuteSettingManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1146,6 +1157,7 @@ 06F08EA729B1DDFE006AB9CA /* Error+Extension.swift in Sources */, 0687F2A82CCB791A00B2A52F /* UIFont+Extension.swift in Sources */, 06B1158F247BB1FB006D91FB /* Message.swift in Sources */, + 06BCAE562CDB19260092867A /* GroupMuteSettingManager.swift in Sources */, 06172FDA27F6DAEF002333A4 /* ServerListTableViewCell.swift in Sources */, 0653677829B727A60038BDB8 /* CryptoSettingRelay.swift in Sources */, 06FB04042C53575400F3A213 /* SharedDefines.swift in Sources */, @@ -1215,6 +1227,8 @@ 06F08EAD29B1DED6006AB9CA /* NSLocalizedString+Extension.swift in Sources */, 0653677629B719BC0038BDB8 /* CryptoSettingManager.swift in Sources */, 06E9446F2C06FF1E00AC86AB /* BadgeProcessor.swift in Sources */, + 06BCAE582CDB19420092867A /* GroupMuteSettingManager.swift in Sources */, + 06BCAE5B2CDB25120092867A /* MuteProcessor.swift in Sources */, 06E9446D2C06FEC900AC86AB /* LevelProcessor.swift in Sources */, 06E944682C06E40600AC86AB /* NotificationContentProcessor.swift in Sources */, 06F08EA529B1DDA7006AB9CA /* Algorithm.swift in Sources */, @@ -1283,6 +1297,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_ENTITLEMENTS = NotificationContentExtension/NotificationContentExtension.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; @@ -1304,6 +1319,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_ENTITLEMENTS = NotificationContentExtension/NotificationContentExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; diff --git a/Bark/AppDelegate.swift b/Bark/AppDelegate.swift index 0d2fc85..85cde1d 100644 --- a/Bark/AppDelegate.swift +++ b/Bark/AppDelegate.swift @@ -90,10 +90,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD self.window?.makeKeyAndVisible() UNUserNotificationCenter.current().delegate = self + var actions = [ + UNNotificationAction(identifier: "copy", title: NSLocalizedString("Copy2"), options: UNNotificationActionOptions.foreground) + ] + if #available(iOSApplicationExtension 15.0, *) { + actions.append(UNNotificationAction(identifier: "mute", title: NSLocalizedString("muteGroup1Hour"), options: UNNotificationActionOptions.foreground)) + } UNUserNotificationCenter.current().setNotificationCategories([ - UNNotificationCategory(identifier: "myNotificationCategory", actions: [ - UNNotificationAction(identifier: "copy", title: NSLocalizedString("Copy2"), options: UNNotificationActionOptions.foreground) - ], intentIdentifiers: [], options: .customDismissAction) + UNNotificationCategory(identifier: "myNotificationCategory", actions: actions, intentIdentifiers: [], options: .customDismissAction) ]) UNUserNotificationCenter.current().getNotificationSettings { settings in diff --git a/Bark/Localizable.xcstrings b/Bark/Localizable.xcstrings index 5399224..ed95a3b 100644 --- a/Bark/Localizable.xcstrings +++ b/Bark/Localizable.xcstrings @@ -1350,6 +1350,23 @@ } } }, + "groupMuted" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Group \"%@\" muted!" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "needs_review", + "value" : "分组 \"%@\" 已静音" + } + } + } + }, "hideAllGroups" : { "extractionState" : "manual", "localizations" : { @@ -1764,6 +1781,23 @@ } } }, + "muteGroup1Hour" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mute this group for 1 Hour" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "1小时内不再提醒此分组" + } + } + } + }, "Notice1" : { "extractionState" : "manual", "localizations" : { diff --git a/Common/GroupMuteSettingManager.swift b/Common/GroupMuteSettingManager.swift new file mode 100644 index 0000000..a52cfb3 --- /dev/null +++ b/Common/GroupMuteSettingManager.swift @@ -0,0 +1,35 @@ +// +// GroupMuteSettingManager.swift +// Bark +// +// Created by huangfeng on 11/6/24. +// Copyright © 2024 Fin. All rights reserved. +// + +import UIKit + +let kGroupMuteSettingKey = "groupMuteSettings" + +/// 保存各分组的静音截止时间,注意 NotificationServiceExtension 和 NotificationContentExtension 是不同进程,不共享单例的(别用单例) +class GroupMuteSettingManager: NSObject { + let defaults = UserDefaults(suiteName: "group.bark") + + var settings: [String: Date] = [:] { + didSet { + defaults?.set(settings, forKey: kGroupMuteSettingKey) + } + } + + override init() { + super.init() + if let settings = defaults?.dictionary(forKey: kGroupMuteSettingKey) as? [String: Date] { + self.settings = settings + } + // 清理过期的设置 + for setting in settings { + if setting.value < Date() { + self.settings.removeValue(forKey: setting.key) + } + } + } +} diff --git a/NotificationServiceExtension/NotificationService.swift b/NotificationServiceExtension/NotificationService.swift index fcaa133..da6b600 100644 --- a/NotificationServiceExtension/NotificationService.swift +++ b/NotificationServiceExtension/NotificationService.swift @@ -33,6 +33,7 @@ class NotificationService: UNNotificationServiceExtension { .archive, .setIcon, .setImage, + .mute, .call ] diff --git a/NotificationServiceExtension/Processor/MuteProcessor.swift b/NotificationServiceExtension/Processor/MuteProcessor.swift new file mode 100644 index 0000000..ca1032e --- /dev/null +++ b/NotificationServiceExtension/Processor/MuteProcessor.swift @@ -0,0 +1,23 @@ +// +// MuteProcessor.swift +// NotificationServiceExtension +// +// Created by huangfeng on 11/6/24. +// Copyright © 2024 Fin. All rights reserved. +// + +import UIKit + +class MuteProcessor: NotificationContentProcessor { + func process(identifier: String, content bestAttemptContent: UNMutableNotificationContent) async throws -> UNMutableNotificationContent { + let groupName = bestAttemptContent.threadIdentifier + guard let date = GroupMuteSettingManager().settings[groupName], date > Date() else { + return bestAttemptContent + } + // 需要静音 + if #available(iOSApplicationExtension 15.0, *) { + bestAttemptContent.interruptionLevel = .passive + } + return bestAttemptContent + } +} diff --git a/NotificationServiceExtension/Processor/NotificationContentProcessor.swift b/NotificationServiceExtension/Processor/NotificationContentProcessor.swift index ef6dea1..b8c9a03 100644 --- a/NotificationServiceExtension/Processor/NotificationContentProcessor.swift +++ b/NotificationServiceExtension/Processor/NotificationContentProcessor.swift @@ -18,6 +18,7 @@ enum NotificationContentProcessorItem { case setIcon case setImage case call + case mute var processor: NotificationContentProcessor { switch self { @@ -37,6 +38,8 @@ enum NotificationContentProcessorItem { return ImageProcessor() case .call: return CallProcessor() + case .mute: + return MuteProcessor() } } } diff --git a/notificationContentExtension/NotificationContentExtension.entitlements b/notificationContentExtension/NotificationContentExtension.entitlements new file mode 100644 index 0000000..59eb47c --- /dev/null +++ b/notificationContentExtension/NotificationContentExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.bark + + + diff --git a/notificationContentExtension/NotificationViewController.swift b/notificationContentExtension/NotificationViewController.swift index e0ae9cf..15583d6 100644 --- a/notificationContentExtension/NotificationViewController.swift +++ b/notificationContentExtension/NotificationViewController.swift @@ -14,7 +14,6 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi let noticeLabel: UILabel = { let label = UILabel() label.textColor = UIColor(named: "notification_copy_color") - label.text = NSLocalizedString("Copy", comment: "") label.font = UIFont.preferredFont(ofSize: 16) label.adjustsFontForContentSizeCategory = true label.textAlignment = .center @@ -45,25 +44,49 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi } if let copy = notification.request.content.userInfo["copy"] as? String { UIPasteboard.general.string = copy - } - else { + } else { UIPasteboard.general.string = notification.request.content.body } } func didReceive(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) { + switch response.actionIdentifier { + case "copy": + self.copyAction(response, completionHandler: completion) + case "mute": + self.muteAction(response, completionHandler: completion) + default: + completion(.dismiss) + } + } + + /// 复制 + func copyAction(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) { let userInfo = response.notification.request.content.userInfo if let copy = userInfo["copy"] as? String { UIPasteboard.general.string = copy - } - else { + } else { UIPasteboard.general.string = response.notification.request.content.body } - self.preferredContentSize = CGSize(width: 0, height: 40) - self.noticeLabel.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 40) - + showTips(text: NSLocalizedString("Copy", comment: "")) completion(.doNotDismiss) } + + /// 静音分组 + func muteAction(_ response: UNNotificationResponse, completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void) { + let groupName = response.notification.request.content.threadIdentifier + // 静音一小时 + GroupMuteSettingManager().settings[groupName] = Date() + 60 * 60 + + showTips(text: String(format: NSLocalizedString("groupMuted", comment: ""), groupName.isEmpty ? "default" : groupName)) + completion(.doNotDismiss) + } + + func showTips(text: String) { + self.preferredContentSize = CGSize(width: 0, height: 40) + self.noticeLabel.text = text + self.noticeLabel.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 40) + } }