Skip to content

Commit 1a4e28b

Browse files
author
DevelopLab
committed
1. Add Critical Alert Notifications
1 parent e283e35 commit 1a4e28b

10 files changed

Lines changed: 158 additions & 31 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ before-all::
99

1010
# 使用 Xcode 项目构建
1111
XCODEPROJ_NAME = TrollSIMSwitcher
12-
BUILD_VERSION = "1.0"
12+
BUILD_VERSION = "1.1"
1313
FILE_NAME = "com.developlab.trollsimswitcher"
1414

1515
# 指定 Theos 使用 xcodeproj 规则

TrollSIMSwitcher.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@
270270
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
271271
CLANG_ENABLE_MODULES = YES;
272272
CODE_SIGN_STYLE = Automatic;
273-
CURRENT_PROJECT_VERSION = 1;
273+
CURRENT_PROJECT_VERSION = 2;
274274
DEVELOPMENT_TEAM = "";
275275
GENERATE_INFOPLIST_FILE = YES;
276276
INFOPLIST_FILE = TrollSIMSwitcher/Info.plist;
@@ -284,7 +284,7 @@
284284
"$(inherited)",
285285
"@executable_path/Frameworks",
286286
);
287-
MARKETING_VERSION = 1.0;
287+
MARKETING_VERSION = 1.1;
288288
PRODUCT_BUNDLE_IDENTIFIER = com.developlab.TrollSIMSwitcher;
289289
PRODUCT_NAME = "$(TARGET_NAME)";
290290
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@@ -306,7 +306,7 @@
306306
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
307307
CLANG_ENABLE_MODULES = YES;
308308
CODE_SIGN_STYLE = Automatic;
309-
CURRENT_PROJECT_VERSION = 1;
309+
CURRENT_PROJECT_VERSION = 2;
310310
DEVELOPMENT_TEAM = "";
311311
GENERATE_INFOPLIST_FILE = YES;
312312
INFOPLIST_FILE = TrollSIMSwitcher/Info.plist;
@@ -320,7 +320,7 @@
320320
"$(inherited)",
321321
"@executable_path/Frameworks",
322322
);
323-
MARKETING_VERSION = 1.0;
323+
MARKETING_VERSION = 1.1;
324324
PRODUCT_BUNDLE_IDENTIFIER = com.developlab.TrollSIMSwitcher;
325325
PRODUCT_NAME = "$(TARGET_NAME)";
326326
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";

TrollSIMSwitcher/Controller/MGDeviceInfoController.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@ class MGDeviceInfoController {
1717
let handle = dlopen("/usr/lib/libMobileGestalt.dylib", RTLD_NOW)
1818
if let sym = dlsym(handle, "MGCopyAnswer") {
1919
self.mGCopyAnswer = unsafeBitCast(sym, to: MGCopyAnswerFunc.self)
20-
NSLog("[MGDeviceInfo] MGCopyAnswer 已加载")
2120
} else {
2221
self.mGCopyAnswer = nil
23-
NSLog("[MGDeviceInfo] MGCopyAnswer 加载失败")
2422
}
2523
getIMEIList()
2624
}

TrollSIMSwitcher/Controller/NotificationController.swift

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,13 @@ class NotificationController {
5656

5757
// MARK: - 申请权限
5858
func requestAuthorization(completion: @escaping (Bool) -> Void) {
59+
5960
if #available(iOS 15.0, *) { // 大于等于iOS 15需要申请timeSensitive 来获得更好的体验
60-
center.requestAuthorization(options: [.alert, .sound, .badge, .timeSensitive]) { granted, _ in
61+
center.requestAuthorization(options: [.alert, .sound, .badge, .timeSensitive, .criticalAlert]) { granted, _ in
6162
completion(granted)
6263
}
63-
} else {
64-
center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in
64+
} else { // 只申请普通通知权限和重要提醒的权限
65+
center.requestAuthorization(options: [.alert, .sound, .badge, .criticalAlert]) { granted, _ in
6566
completion(granted)
6667
}
6768
}
@@ -95,6 +96,13 @@ class NotificationController {
9596

9697
center.setNotificationCategories([category])
9798
}
99+
100+
// 获取是否已经拿到重要通知权利
101+
func isCriticalNotificationEnabled( completion: @escaping (Bool) -> Void) {
102+
center.getNotificationSettings { settings in
103+
completion(settings.criticalAlertSetting == .enabled)
104+
}
105+
}
98106

99107
// MARK: - 发送:快捷入口(参数化)
100108
/// - Parameters:
@@ -134,11 +142,20 @@ class NotificationController {
134142
content.sound = nil
135143
}
136144

137-
// 实效性 Time Sensitive(iOS 15+)
138-
if #available(iOS 15.0, *),
139-
preferTimeSensitive,
140-
settings?.timeSensitiveSetting == .enabled {
141-
content.interruptionLevel = .timeSensitive
145+
// 先判断是否开启紧急通知
146+
if SettingsUtils.instance.getUseCriticalNotifications(), settings?.criticalAlertSetting == .enabled {
147+
if #available(iOS 15.0, *) {
148+
content.sound = UNNotificationSound.defaultCriticalSound(withAudioVolume: 0.0) // 必须设置这个声音,但是其实是个没有声音的提示,但是这样可以激活重要通知
149+
} else {
150+
// 必须设置这个声音,但是其实是个没有声音的提示,但是这样可以激活重要通知 iOS 14可能无法可靠的发送重要通知
151+
content.sound = UNNotificationSound.criticalSoundNamed(UNNotificationSoundName(""), withAudioVolume: 0.0)
152+
}
153+
154+
} else if #available(iOS 15.0, *) { // 实效性通知 Time Sensitive(iOS 15+)
155+
// 再判断是否开启实效性通知
156+
if preferTimeSensitive, settings?.timeSensitiveSetting == .enabled {
157+
content.interruptionLevel = .timeSensitive
158+
}
142159
}
143160

144161
// 覆盖同一组通知,防止点击以后还存在别的

TrollSIMSwitcher/Entity/SettingsSwitchViewTag.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ enum SettingsSwitchViewTag: Int {
1111
case EnableNotifications = 6
1212
case EnableToggleCellularDataSlotNotifications = 7
1313
case EnableToggleNetworkTypeNotifications = 8
14+
case UseCriticalNotifications = 9
1415
}

TrollSIMSwitcher/Intents/TrollSIMSwitcherAppIntents.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ struct TrollSIMSwitcherSlot1Intent: AppIntent {
1212

1313
func perform() async throws -> some IntentResult & ReturnsValue<Bool> {
1414
let result = CoreTelephonyController.instance.setDataSlot(slot: 1)
15+
// 刷新通知
16+
NotificationController.instance.sendNotifications(silentNotifications: true)
1517
return .result(value: result)
1618
}
1719
}
@@ -25,6 +27,8 @@ struct TrollSIMSwitcherSlot2Intent: AppIntent {
2527

2628
func perform() async throws -> some IntentResult & ReturnsValue<Bool> {
2729
let result = CoreTelephonyController.instance.setDataSlot(slot: 2)
30+
// 刷新通知
31+
NotificationController.instance.sendNotifications(silentNotifications: true)
2832
return .result(value: result)
2933
}
3034
}
@@ -38,6 +42,8 @@ struct TrollSIMSwitcherToggleSlotIntent: AppIntent {
3842

3943
func perform() async throws -> some IntentResult & ReturnsValue<Bool> {
4044
let result = CoreTelephonyController.instance.toggleDataSlot()
45+
// 刷新通知
46+
NotificationController.instance.sendNotifications(silentNotifications: true)
4147
return .result(value: result)
4248
}
4349
}
@@ -52,6 +58,8 @@ struct TrollSIMSwitcherNetwork4GIntent: AppIntent {
5258

5359
func perform() async throws -> some IntentResult & ReturnsValue<Bool> {
5460
let result = CoreTelephonyController.instance.setDataPreferredRate(selectRate: ._4G)
61+
// 刷新通知
62+
NotificationController.instance.sendNotifications(silentNotifications: true)
5563
return .result(value: result)
5664
}
5765
}
@@ -66,6 +74,8 @@ struct TrollSIMSwitcherNetwork5GIntent: AppIntent {
6674

6775
func perform() async throws -> some IntentResult & ReturnsValue<Bool> {
6876
let result = CoreTelephonyController.instance.setDataPreferredRate(selectRate: ._5G)
77+
// 刷新通知
78+
NotificationController.instance.sendNotifications(silentNotifications: true)
6979
return .result(value: result)
7080
}
7181
}
@@ -80,6 +90,8 @@ struct TrollSIMSwitcherToggleNetworkTypeIntent: AppIntent {
8090

8191
func perform() async throws -> some IntentResult & ReturnsValue<Bool> {
8292
let result = CoreTelephonyController.instance.toggleDataPreferredRate()
93+
// 刷新通知
94+
NotificationController.instance.sendNotifications(silentNotifications: true)
8395
return .result(value: result)
8496
}
8597
}

TrollSIMSwitcher/Localizable.xcstrings

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,22 @@
9797
}
9898
}
9999
},
100+
"CriticalAlertMessage" : {
101+
"localizations" : {
102+
"en" : {
103+
"stringUnit" : {
104+
"state" : "translated",
105+
"value" : "Critical Alert Permission Notice\n\nDue to iOS system restrictions, System apps are limited when requesting Critical Alert permissions. When enabling Critical Alerts for the first time, please switch the app to 'User' registration in TrollStore, fully close the app, and then reopen it to complete the permission request.\nAfter authorization is granted, you may switch the app back to 'System' registration.\nThe permission will be preserved by the system, and this process only needs to be completed once."
106+
}
107+
},
108+
"zh-Hans" : {
109+
"stringUnit" : {
110+
"state" : "translated",
111+
"value" : "重要通知权限说明\n\niOS 对 System App 申请重要通知权限存在限制。\n请在首次启用时,在TrollStore 中将 App 切换为'User'状态,并且彻底关闭App再次打开App以完成权限请求,授权成功后可切换为'System'状态,权限将被系统保留,无需重复操作。"
112+
}
113+
}
114+
}
115+
},
100116
"DisableAllNotifications" : {
101117
"comment" : "禁用全部通知",
102118
"localizations" : {
@@ -323,6 +339,22 @@
323339
}
324340
}
325341
},
342+
"NotificationsFooterMessage" : {
343+
"localizations" : {
344+
"en" : {
345+
"stringUnit" : {
346+
"state" : "translated",
347+
"value" : "On iOS 16 and later, it is recommended to switch using Lock Screen Widgets for a better experience.\nOn iOS 14, enabling Critical Alerts is strongly recommended to improve usability. Notification will be displayed in silent mode."
348+
}
349+
},
350+
"zh-Hans" : {
351+
"stringUnit" : {
352+
"state" : "translated",
353+
"value" : "建议iOS 16以下用户使用,iOS 16及更高版本系统建议使用锁屏小组件切换,iOS 14用户建议开启重要通知提升使用体验,通知将以静音模式显示"
354+
}
355+
}
356+
}
357+
},
326358
"NotificationsPermissionDenied" : {
327359
"comment" : "通知权限已拒绝",
328360
"localizations" : {
@@ -695,6 +727,22 @@
695727
}
696728
}
697729
},
730+
"UseCriticalNotifications" : {
731+
"localizations" : {
732+
"en" : {
733+
"stringUnit" : {
734+
"state" : "translated",
735+
"value" : "Use Critical Notifications"
736+
}
737+
},
738+
"zh-Hans" : {
739+
"stringUnit" : {
740+
"state" : "translated",
741+
"value" : "使用重要通知"
742+
}
743+
}
744+
}
745+
},
698746
"Version" : {
699747
"localizations" : {
700748
"en" : {

TrollSIMSwitcher/MainViewController.swift

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import UIKit
22

33
class MainViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
44

5-
static let versionCode = "1.0"
5+
static let versionCode = "1.1"
66

77
private var tableView = UITableView()
88

@@ -117,7 +117,7 @@ class MainViewController: UIViewController, UITableViewDelegate, UITableViewData
117117
return NSLocalizedString("Options", comment: "")
118118
} else if section == 3 {
119119
return NSLocalizedString("Shortcuts", comment: "")
120-
} else if section == 4 {
120+
} else if section == MainViewController.notificationsAtSection {
121121
return NSLocalizedString("Notifications", comment: "")
122122
} else if section == 5 {
123123
return NSLocalizedString("About", comment: "")
@@ -127,18 +127,10 @@ class MainViewController: UIViewController, UITableViewDelegate, UITableViewData
127127

128128
// MARK: - 设置每个分组的底部标题 可以为分组设置尾部文本,如果没有尾部可以返回 nil
129129
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
130-
#if DEBUG
131-
// if section == 0 {
132-
// var text = ""
133-
// for slot in SIMSlotList {
134-
// text = text.appending(slot.toString()).appending("\n")
135-
// }
136-
// return text
137-
138-
// }
139-
#endif
140130
if section == 2 {
141131
return String.localizedStringWithFormat(NSLocalizedString("EnableCompatibilityModeMessage", comment: ""), NSLocalizedString("CompatibilitySwitchMode", comment: ""))
132+
} else if section == MainViewController.notificationsAtSection {
133+
return NSLocalizedString("NotificationsFooterMessage", comment: "")
142134
}
143135
return nil
144136
}
@@ -272,7 +264,11 @@ class MainViewController: UIViewController, UITableViewDelegate, UITableViewData
272264
switchView.isEnabled = false // 禁用开关
273265
}
274266
}
275-
} else if indexPath.row == 3 || indexPath.row == 4 {
267+
} else if indexPath.row == 3 {
268+
// 使用重要通知
269+
switchView.tag = SettingsSwitchViewTag.UseCriticalNotifications.rawValue // 设置识别id
270+
switchView.isOn = SettingsUtils.instance.getUseCriticalNotifications() // 从配置文件中获取状态
271+
} else if indexPath.row == 4 || indexPath.row == 5 {
276272
cell.textLabel?.textColor = .systemBlue // 文本设置成蓝色
277273
return cell
278274
}
@@ -336,9 +332,9 @@ class MainViewController: UIViewController, UITableViewDelegate, UITableViewData
336332
} else if indexPath.section == MainViewController.notificationsAtSection {
337333
if indexPath.row == 1 && tableCellList[MainViewController.notificationsAtSection].count < 3 { // 跳转到通知设置
338334
self.onClickToNotificationSettings()
339-
} else if indexPath.row == 3 { // 发送通知
335+
} else if indexPath.row == 4 { // 发送通知
340336
self.onClickSendNotification()
341-
} else if indexPath.row == 4 { // 跳转到通知设置
337+
} else if indexPath.row == 5 { // 跳转到通知设置
342338
self.onClickToNotificationSettings()
343339
}
344340
} else if indexPath.section == MainViewController.aboutAtSection { // 关于
@@ -425,8 +421,43 @@ class MainViewController: UIViewController, UITableViewDelegate, UITableViewData
425421
}
426422
} else if sender.tag == SettingsSwitchViewTag.EnableToggleCellularDataSlotNotifications.rawValue {
427423
SettingsUtils.instance.setEnableToggleCellularDataSlotNotifications(enable: sender.isOn)
424+
// 刷新通知
425+
NotificationController.instance.sendNotifications(silentNotifications: true)
428426
} else if sender.tag == SettingsSwitchViewTag.EnableToggleNetworkTypeNotifications.rawValue {
429427
SettingsUtils.instance.setEnableToggleNetworkTypeNotifications(enable: sender.isOn)
428+
// 刷新通知
429+
NotificationController.instance.sendNotifications(silentNotifications: true)
430+
} else if sender.tag == SettingsSwitchViewTag.UseCriticalNotifications.rawValue {
431+
if sender.isOn {
432+
433+
NotificationController.instance.requestAuthorization { _ in // 再次申请权限
434+
NotificationController.instance.isCriticalNotificationEnabled { enabled in
435+
436+
DispatchQueue.main.async { [weak self] in
437+
guard let self = self else {
438+
return
439+
}
440+
441+
if enabled { // 拿到了重要通知权限
442+
SettingsUtils.instance.setUseCriticalNotifications(enable: true)
443+
// 刷新通知
444+
NotificationController.instance.sendNotifications(silentNotifications: true)
445+
446+
} else { // 没拿到 提示用户开启
447+
sender.setOn(false, animated: true)
448+
SettingsUtils.instance.setUseCriticalNotifications(enable: false)
449+
UIUtils.showAlert(message: NSLocalizedString("CriticalAlertMessage", comment: ""), in: self)
450+
}
451+
}
452+
}
453+
}
454+
455+
} else {
456+
SettingsUtils.instance.setUseCriticalNotifications(enable: false)
457+
// 刷新通知
458+
NotificationController.instance.sendNotifications(silentNotifications: true)
459+
}
460+
430461
}
431462
}
432463

@@ -436,6 +467,15 @@ class MainViewController: UIViewController, UITableViewDelegate, UITableViewData
436467
tableCellList[MainViewController.notificationsAtSection] = []
437468
tableCellList[MainViewController.notificationsAtSection].append(NSLocalizedString("Enable", comment: ""))
438469

470+
// 判断是否关闭了重要通知
471+
if SettingsUtils.instance.getUseCriticalNotifications() {
472+
NotificationController.instance.isCriticalNotificationEnabled { enabled in
473+
if !enabled {
474+
SettingsUtils.instance.setUseCriticalNotifications(enable: false)
475+
}
476+
}
477+
}
478+
439479
// 必须等待权限查询完才能更新 UI
440480
NotificationController.instance.ensureAuthorization { status in
441481
DispatchQueue.main.async {
@@ -448,6 +488,7 @@ class MainViewController: UIViewController, UITableViewDelegate, UITableViewData
448488
self.tableCellList[MainViewController.notificationsAtSection].append(NSLocalizedString("ToggleCellularDataSlotNotifications", comment: ""))
449489
// 切换网络类型的通知
450490
self.tableCellList[MainViewController.notificationsAtSection].append(NSLocalizedString("ToggleNetworkTypeNotifications", comment: ""))
491+
self.tableCellList[MainViewController.notificationsAtSection].append(NSLocalizedString("UseCriticalNotifications", comment: ""))
451492
self.tableCellList[MainViewController.notificationsAtSection].append(NSLocalizedString("SendNotifications", comment: ""))
452493
self.tableCellList[MainViewController.notificationsAtSection].append(NSLocalizedString("GoToNotificationSettings", comment: ""))
453494
}

TrollSIMSwitcher/Utils/SettingsUtils.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,16 @@ class SettingsUtils {
123123
plistManager.apply()
124124
}
125125

126+
/// 获取是否启用重要通知
127+
func getUseCriticalNotifications() -> Bool {
128+
return plistManager.getBool(key: "UseCriticalNotifications", defaultValue: false)
129+
}
130+
131+
func setUseCriticalNotifications(enable: Bool) {
132+
plistManager.setBool(key: "UseCriticalNotifications", value: enable)
133+
plistManager.apply()
134+
}
135+
126136
/// 获取是否开启切换蜂窝数据流量卡通知
127137
func getEnableToggleCellularDataSlotNotifications() -> Bool {
128138
return plistManager.getBool(key: "EnableToggleCellularDataSlotNotifications", defaultValue: false)

control

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: com.developlab.trollsimswitcher
22
Name: TrollSIMSwitcher
3-
Version: 1.0
3+
Version: 1.1
44
Architecture: iphoneos-arm
55
Description: iOS TrollStore Switch SIM tool
66
Maintainer: developlab

0 commit comments

Comments
 (0)