mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
* Add SiriKit intent handlers for CarPlay messaging (INSendMessageIntent, INSearchForMessagesIntent, INSetMessageAttributeIntent) Agent-Logs-Url: https://github.com/meshtastic/Meshtastic-Apple/sessions/8ef2a78b-83ee-4d9f-82b9-17b766c96312 Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> * Address code review: consolidate intent routing, support multiple recipients, improve error for long messages Agent-Logs-Url: https://github.com/meshtastic/Meshtastic-Apple/sessions/8ef2a78b-83ee-4d9f-82b9-17b766c96312 Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> * Fix unnecessary nil-coalescing in conversationIdentifier Agent-Logs-Url: https://github.com/meshtastic/Meshtastic-Apple/sessions/8ef2a78b-83ee-4d9f-82b9-17b766c96312 Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> * Restrict INSendMessageIntent to single recipient (channel or direct message, not both) Agent-Logs-Url: https://github.com/meshtastic/Meshtastic-Apple/sessions/1798a03a-53b3-4a97-94e1-8281b552217a Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> * Fix Mac Catalyst build errors in SiriKit intent handlers - SendMessageIntentHandler: guard `.noHandlesForValue` with #if targetEnvironment(macCatalyst) since the reason enum is iOS-only - IntentMessageConverters: use .text instead of .tapback; INMessageType.tapback is unavailable on Mac Catalyst - SearchForMessagesIntentHandler: replace .startDate/.endDate (iOS-only) with .startDateComponents/.endDateComponents + Calendar.date(from:) which work on all platforms Agent-Logs-Url: https://github.com/meshtastic/Meshtastic-Apple/sessions/9b61aad5-652c-4330-83b3-2303f10e4f12 Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> * Add Siri authorization request at startup and NSSiriUsageDescription in Info.plist Agent-Logs-Url: https://github.com/meshtastic/Meshtastic-Apple/sessions/be245ecb-2f0a-48d4-b931-4df889a6b6cc Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: garthvh <1795163+garthvh@users.noreply.github.com>
139 lines
4.7 KiB
Swift
139 lines
4.7 KiB
Swift
//
|
|
// MeshtasticAppDelegate.swift
|
|
// Meshtastic
|
|
//
|
|
// Created by Ben on 8/20/23.
|
|
//
|
|
|
|
import Intents
|
|
import SwiftUI
|
|
import OSLog
|
|
|
|
class MeshtasticAppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, ObservableObject {
|
|
|
|
var router: Router?
|
|
|
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
|
|
Logger.services.info("🚀 [App] Meshtstic Apple App launched!")
|
|
// Default User Default Values
|
|
UserDefaults.standard.register(defaults: ["meshMapRecentering": true])
|
|
UserDefaults.standard.register(defaults: ["meshMapShowNodeHistory": true])
|
|
UserDefaults.standard.register(defaults: ["meshMapShowRouteLines": true])
|
|
UNUserNotificationCenter.current().delegate = self
|
|
let locationsHandler = LocationsHandler.shared
|
|
locationsHandler.startLocationUpdates()
|
|
// If a background activity session was previously active, reinstantiate it after the background launch.
|
|
if locationsHandler.backgroundActivity {
|
|
locationsHandler.backgroundActivity = true
|
|
}
|
|
// Initialize TAK Server if enabled
|
|
Task { @MainActor in
|
|
TAKServerManager.shared.initializeOnStartup()
|
|
}
|
|
// Request Siri authorization so intent donations work and CarPlay messaging is available.
|
|
#if !targetEnvironment(macCatalyst)
|
|
INPreferences.requestSiriAuthorization { status in
|
|
Logger.services.info("Siri authorization status: \(String(describing: status))")
|
|
}
|
|
#endif
|
|
return true
|
|
}
|
|
|
|
// MARK: - SiriKit Intent Handling
|
|
|
|
/// Routes incoming SiriKit intents to the appropriate handler for CarPlay and Siri messaging support.
|
|
func application(_ application: UIApplication, handlerFor intent: INIntent) -> Any? {
|
|
IntentHandler().handler(for: intent)
|
|
}
|
|
|
|
// Lets us show the notification in the app in the foreground
|
|
func userNotificationCenter(
|
|
_ center: UNUserNotificationCenter,
|
|
willPresent notification: UNNotification,
|
|
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
|
|
) {
|
|
completionHandler([.list, .banner, .sound])
|
|
}
|
|
|
|
// This method is called when a user clicks on the notification
|
|
func userNotificationCenter(
|
|
_ center: UNUserNotificationCenter,
|
|
didReceive response: UNNotificationResponse,
|
|
withCompletionHandler completionHandler: @escaping () -> Void
|
|
) {
|
|
let userInfo = response.notification.request.content.userInfo
|
|
|
|
switch response.actionIdentifier {
|
|
case UNNotificationDefaultActionIdentifier:
|
|
break
|
|
case "messageNotification.thumbsUpAction":
|
|
if let channel = userInfo["channel"] as? Int32,
|
|
let replyID = userInfo["messageId"] as? Int64 {
|
|
Task {
|
|
do {
|
|
try await AccessoryManager.shared.sendMessage(
|
|
message: Tapbacks.thumbsUp.emojiString,
|
|
toUserNum: userInfo["userNum"] as? Int64 ?? 0,
|
|
channel: channel,
|
|
isEmoji: true,
|
|
replyID: replyID
|
|
)
|
|
Logger.services.info("Tapback response sent")
|
|
} catch {
|
|
Logger.services.error("Failed to retrieve channel or messageId from userInfo")
|
|
}
|
|
}
|
|
}
|
|
case "messageNotification.thumbsDownAction":
|
|
if let channel = userInfo["channel"] as? Int32,
|
|
let replyID = userInfo["messageId"] as? Int64 {
|
|
Task {
|
|
do {
|
|
try await AccessoryManager.shared.sendMessage(
|
|
message: Tapbacks.thumbsDown.emojiString,
|
|
toUserNum: userInfo["userNum"] as? Int64 ?? 0,
|
|
channel: channel,
|
|
isEmoji: true,
|
|
replyID: replyID
|
|
)
|
|
Logger.services.info("Tapback response sent")
|
|
} catch {
|
|
Logger.services.error("Failed to retrieve channel or messageId from userInfo")
|
|
}
|
|
}
|
|
}
|
|
case "messageNotification.replyInputAction":
|
|
if let userInput = (response as? UNTextInputNotificationResponse)?.userText,
|
|
let channel = userInfo["channel"] as? Int32,
|
|
let replyID = userInfo["messageId"] as? Int64 {
|
|
Task {
|
|
do {
|
|
try await AccessoryManager.shared.sendMessage(
|
|
message: userInput,
|
|
toUserNum: userInfo["userNum"] as? Int64 ?? 0,
|
|
channel: channel,
|
|
isEmoji: false,
|
|
replyID: replyID
|
|
)
|
|
|
|
Logger.services.info("Actionable notification reply sent")
|
|
} catch {
|
|
Logger.services.error("Failed to retrieve user input, channel, or messageId from userInfo")
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
|
|
if let targetValue = userInfo["target"] as? String,
|
|
let deepLink = userInfo["path"] as? String,
|
|
let url = URL(string: deepLink) {
|
|
Logger.services.info("userNotificationCenter didReceiveResponse handling deeplink: \(targetValue, privacy: .public) \(deepLink, privacy: .public)")
|
|
router?.route(url: url)
|
|
} else {
|
|
Logger.services.error("Failed to handle notification response: \(userInfo, privacy: .public)")
|
|
}
|
|
completionHandler()
|
|
}
|
|
}
|