From 54aabb78043987f3508a9d2baf64b30e06fbd238 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Thu, 28 Aug 2025 19:22:37 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=8F=90=E9=86=92=E6=8F=90?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ble_service/BleServicePlugin.kt | 4 +- .../chat_api/SystemFunctionHandler.swift | 161 ++++++++++-------- 2 files changed, 90 insertions(+), 75 deletions(-) diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt index de20dd940..12a29477d 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt @@ -302,8 +302,8 @@ class BleServicePlugin : FlutterPlugin, MethodCallHandler, ActivityAware, } } "startScan" -> { - val devicename = call.argument("devicename")?:"" - val connected = BleService.startScan(devicename) + val uuids = call.argument>("uuids")?:emptyList() + val connected = BleService.startScan(uuids) result.success(connected) } "getDeviceInfo" -> { diff --git a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift index 60ec78415..fbe0419d1 100644 --- a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift +++ b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift @@ -8,6 +8,7 @@ import user_portrait // 添加这行 import MediaPlayer import AVFoundation +import UserNotifications // 在文件顶部添加通知名称定义 extension Notification.Name { @@ -693,12 +694,12 @@ private class MakePhoneCallHandler: FunctionHandler { /// 设置提醒处理器 private class SetReminderHandler: FunctionHandler { - private let eventStore: EKEventStore? + private var eventStore: EKEventStore? = EKEventStore() init(eventStore: EKEventStore?) { self.eventStore = eventStore } - + func handle(arguments: [String: Any]) async throws -> String { guard let title = arguments["title"] as? String, let timeString = arguments["time"] as? String else { @@ -706,99 +707,113 @@ private class SetReminderHandler: FunctionHandler { } let content = arguments["content"] as? String ?? "" - let result = await setReminder(title: title, content: content, time: timeString) + let result = await setAlarmReminder(title: title, content: content, time: timeString) return "{\"result\": \"\(result.message)\"}" } - - private func setReminder(title: String, content: String, time: String) async -> Result { - guard let eventStore = eventStore else { - return Result(success: false, message: "事件存储未初始化") + + // 设置闹钟级提醒(主方法) + func setAlarmReminder(title: String, content: String, time: String) async -> Result { + // 1. 请求日历权限 + let calendarStatus = await EKEventStore.authorizationStatus(for: .reminder) + guard calendarStatus == .authorized else { + let message = calendarStatus == .denied + ? "日历权限已拒绝,请前往「设置 → 隐私与安全 → 日历」开启" + : "日历权限不足" + return Result(success: false, message: message) } - // 请求日历权限 - let status = await requestCalendarAccess() - print("[SetReminder] 权限检查结果: \(status.rawValue) - \(statusDescription(status))") - - switch status { - case .authorized: - // 权限已授权,继续执行 - break - case .denied: - return Result(success: false, message: "日历权限被拒绝,请前往 设置 > 隐私与安全 > 日历 中开启权限") - case .restricted: - return Result(success: false, message: "日历权限受限制,可能由于家长控制或企业策略限制") - case .notDetermined: - return Result(success: false, message: "日历权限未确定,请重试") - @unknown default: - return Result(success: false, message: "日历权限状态未知") + // 2. 请求关键通知权限(闹钟级提醒必需) + let notificationCenter = UNUserNotificationCenter.current() + do { + let granted = try await notificationCenter.requestAuthorization(options: [.alert, .sound, .criticalAlert]) + guard granted else { + return Result(success: false, message: "请在「设置 → 你的App → 通知」中开启关键通知权限") + } + } catch { + return Result(success: false, message: "通知权限请求失败:\(error.localizedDescription)") } - // 解析时间 + // 3. 解析并验证时间(必须在当前时间10秒后) let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd HH:mm" - guard let date = formatter.date(from: time) else { + formatter.timeZone = TimeZone.current + guard let targetDate = formatter.date(from: time), targetDate.timeIntervalSinceNow > 10 else { return Result(success: false, message: "时间格式错误") } - // 创建提醒 + // 4. 创建日历提醒(作为备份) + let calendarResult = await createCalendarReminder(title: title, content: content, date: targetDate) + guard calendarResult.success else { + return calendarResult + } + + // 5. 创建关键通知(闹钟级效果核心) + return await createCriticalNotification(title: title, content: content, date: targetDate) + } + + // 创建日历提醒(备份用) + private func createCalendarReminder(title: String, content: String, date: Date) async -> Result { + guard let eventStore = eventStore else { + return Result(success: false, message: "日历服务不可用") + } + let reminder = EKReminder(eventStore: eventStore) reminder.title = title reminder.notes = content - reminder.calendar = eventStore.defaultCalendarForNewReminders() - let alarm = EKAlarm(absoluteDate: date) - reminder.addAlarm(alarm) + // 获取可用日历 + guard let targetCalendar = eventStore.defaultCalendarForNewReminders() + ?? eventStore.calendars(for: .reminder).first(where: { $0.allowsContentModifications }) else { + return Result(success: false, message: "无可用日历,请先在系统日历中创建日历") + } + reminder.calendar = targetCalendar + reminder.addAlarm(EKAlarm(absoluteDate: date)) + // 保存提醒 do { try eventStore.save(reminder, commit: true) - return Result(success: true, message: "提醒设置成功") + return Result(success: true, message: "日历提醒已创建") } catch { - return Result(success: false, message: "设置提醒失败:\(error.localizedDescription)") - } - } - - private func requestCalendarAccess() async -> EKAuthorizationStatus { - return await withCheckedContinuation { continuation in - // 首先检查当前权限状态 - let currentStatus = EKEventStore.authorizationStatus(for: .reminder) - print("[Calendar] 当前权限状态: \(currentStatus.rawValue) - \(statusDescription(currentStatus))") - - // 如果已经授权,直接返回 - if currentStatus == .authorized { - continuation.resume(returning: currentStatus) - return - } - - // 如果被拒绝,也直接返回(避免重复请求) - if currentStatus == .denied { - print("[Calendar] 权限已被拒绝,需要用户手动在设置中开启") - continuation.resume(returning: currentStatus) - return - } - - // 请求权限 - eventStore?.requestAccess(to: .reminder) { granted, error in - let finalStatus = EKEventStore.authorizationStatus(for: .reminder) - print("[Calendar] 权限请求完成 - granted: \(granted), error: \(error?.localizedDescription ?? "无")") - print("[Calendar] 最终权限状态: \(finalStatus.rawValue) - \(self.statusDescription(finalStatus))") - continuation.resume(returning: finalStatus) - } + return Result(success: false, message: "日历提醒保存失败:\(error.localizedDescription)") } } - // 添加状态描述辅助方法 - private func statusDescription(_ status: EKAuthorizationStatus) -> String { - switch status { - case .notDetermined: - return "未确定" - case .restricted: - return "受限制" - case .denied: - return "被拒绝" - case .authorized: - return "已授权" - @unknown default: - return "未知状态" + // 创建关键通知(实现闹钟级效果) + private func createCriticalNotification(title: String, content: String, date: Date) async -> Result { + let notificationContent = UNMutableNotificationContent() + notificationContent.title = title + notificationContent.body = content + notificationContent.badge = 1 + + // 关键配置:闹钟级声音(突破静音,最大音量) + notificationContent.sound = UNNotificationSound.criticalSoundNamed( + UNNotificationSoundName("default"), + withAudioVolume: 1.0 + ) + + // 关键配置:最高优先级(突破勿扰,全屏弹窗) + notificationContent.interruptionLevel = .critical + notificationContent.relevanceScore = 1.0 // 最高相关性分数 + + // 设置触发时间(精确到秒) + let components = Calendar.current.dateComponents( + [.year, .month, .day, .hour, .minute, .second], + from: date + ) + let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false) + + // 发送通知请求 + let request = UNNotificationRequest( + identifier: "Alarm_\(UUID().uuidString)", + content: notificationContent, + trigger: trigger + ) + + do { + try await UNUserNotificationCenter.current().add(request) + return Result(success: true, message: "闹钟级提醒已设置,到点将强制响铃") + } catch { + return Result(success: false, message: "通知创建失败:\(error.localizedDescription)") } } }