mirror of
https://github.com/meshtastic/Meshtastic-Apple.git
synced 2026-04-20 22:13:56 +00:00
Deep Links
Updated position precision
This commit is contained in:
parent
4af2fbc749
commit
8bcf40f543
12 changed files with 173 additions and 15 deletions
|
|
@ -128,6 +128,7 @@
|
|||
DDA9515A2BC6624100CEA535 /* TelemetryWeather.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA951592BC6624100CEA535 /* TelemetryWeather.swift */; };
|
||||
DDA9515C2BC6631200CEA535 /* TelemetryEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA9515B2BC6631200CEA535 /* TelemetryEnums.swift */; };
|
||||
DDA9515E2BC6F56F00CEA535 /* IndoorAirQuality.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA9515D2BC6F56F00CEA535 /* IndoorAirQuality.swift */; };
|
||||
DDAA632E2BE7F84E00CC22D3 /* DeepLinkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAA632D2BE7F84E00CC22D3 /* DeepLinkManager.swift */; };
|
||||
DDAB580D2B0DAA9E00147258 /* Routes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAB580C2B0DAA9E00147258 /* Routes.swift */; };
|
||||
DDAB580F2B0DAFBC00147258 /* LocationEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAB580E2B0DAFBC00147258 /* LocationEntityExtension.swift */; };
|
||||
DDAD49ED2AFB39DC00B4425D /* MeshMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDAD49EC2AFB39DC00B4425D /* MeshMap.swift */; };
|
||||
|
|
@ -386,6 +387,7 @@
|
|||
DDA951592BC6624100CEA535 /* TelemetryWeather.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TelemetryWeather.swift; sourceTree = "<group>"; };
|
||||
DDA9515B2BC6631200CEA535 /* TelemetryEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryEnums.swift; sourceTree = "<group>"; };
|
||||
DDA9515D2BC6F56F00CEA535 /* IndoorAirQuality.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IndoorAirQuality.swift; sourceTree = "<group>"; };
|
||||
DDAA632D2BE7F84E00CC22D3 /* DeepLinkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLinkManager.swift; sourceTree = "<group>"; };
|
||||
DDAB580B2B0D913500147258 /* MeshtasticDataModelV20.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeshtasticDataModelV20.xcdatamodel; sourceTree = "<group>"; };
|
||||
DDAB580C2B0DAA9E00147258 /* Routes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Routes.swift; sourceTree = "<group>"; };
|
||||
DDAB580E2B0DAFBC00147258 /* LocationEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEntityExtension.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -573,6 +575,13 @@
|
|||
path = CoreData;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DD17C4D42BE7EC2200D45AC7 /* SwiftData */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = SwiftData;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DD47E3CA26F0E50300029299 /* Nodes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -757,6 +766,14 @@
|
|||
path = Channels;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DDAA632C2BE7F83400CC22D3 /* DeepLinks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DDAA632D2BE7F84E00CC22D3 /* DeepLinkManager.swift */,
|
||||
);
|
||||
path = DeepLinks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
DDAD49EB2AFAE82500B4425D /* Map */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -818,6 +835,7 @@
|
|||
children = (
|
||||
DD7709392AA1ABA1007A8BF0 /* Tips */,
|
||||
DD90860A26F645B700DC5189 /* Meshtastic.entitlements */,
|
||||
DDAA632C2BE7F83400CC22D3 /* DeepLinks */,
|
||||
DD8ED9C6289CE4A100B3B0AB /* Enums */,
|
||||
DD86D40D2881BDB300BAEB7A /* Export */,
|
||||
DDDB443E29F79A9400EE2349 /* Extensions */,
|
||||
|
|
@ -950,6 +968,7 @@
|
|||
DDC4D5662754996200A4208E /* Persistence */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DD17C4D42BE7EC2200D45AC7 /* SwiftData */,
|
||||
DDC4D567275499A500A4208E /* Persistence.swift */,
|
||||
DD964FC52975DBFD007C176F /* QueryCoreData.swift */,
|
||||
DD3CC6C128EB9D4900FA9159 /* UpdateCoreData.swift */,
|
||||
|
|
@ -1234,6 +1253,7 @@
|
|||
DDAF8C5326EB1DF10058C060 /* BLEManager.swift in Sources */,
|
||||
DD15E4F32B8BA56E00654F61 /* PaxCounterConfig.swift in Sources */,
|
||||
DDDB445229F8ACF900EE2349 /* Date.swift in Sources */,
|
||||
DDAA632E2BE7F84E00CC22D3 /* DeepLinkManager.swift in Sources */,
|
||||
DDC4D568275499A500A4208E /* Persistence.swift in Sources */,
|
||||
DDD6EEAF29BC024700383354 /* Firmware.swift in Sources */,
|
||||
DD77093B2AA1ABB8007A8BF0 /* BluetoothTips.swift in Sources */,
|
||||
|
|
|
|||
100
Meshtastic/DeepLinks/DeepLinkManager.swift
Normal file
100
Meshtastic/DeepLinks/DeepLinkManager.swift
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// DeepLinkManager.swift
|
||||
// Meshtastic
|
||||
//
|
||||
// Copyright(c) Garth Vander Houwen 5/5/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol DeepLinkTabManager {
|
||||
func handle(deepLink: String, selectedTab: inout Tab) -> Bool
|
||||
}
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
@Observable
|
||||
class DeepLinkManager {
|
||||
var selectedTab: Tab = .ble
|
||||
var features: [DeepLinkTabManager]
|
||||
|
||||
init() {
|
||||
self.features = [
|
||||
DeepLinkManagerMessages(),
|
||||
DeepLinkManagerBluetooth(),
|
||||
DeepLinkManagerNodes(),
|
||||
DeepLinkManagerMap(),
|
||||
DeepLinkManagerSettings()
|
||||
]
|
||||
}
|
||||
|
||||
func handleDeepLink(deepLink: String) {
|
||||
for handler in features {
|
||||
if handler.handle(deepLink: deepLink, selectedTab: &selectedTab) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DeepLinkManagerBluetooth: DeepLinkTabManager {
|
||||
func handle(deepLink: String, selectedTab: inout Tab) -> Bool {
|
||||
if deepLink.contains("bluetooth") {
|
||||
selectedTab = .ble
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
class DeepLinkManagerMessages: DeepLinkTabManager {
|
||||
|
||||
var channel: String = ""
|
||||
var messageId: String = ""
|
||||
|
||||
func handle(deepLink: String, selectedTab: inout Tab) -> Bool {
|
||||
if deepLink.contains("messages") {
|
||||
selectedTab = .messages
|
||||
extractData(from: deepLink)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
private func extractData(from deepLink: String) {
|
||||
let temp = deepLink.replacingOccurrences(of: "meshtastic://messages?", with: "")
|
||||
let params = temp.components(separatedBy: "&")
|
||||
guard params.count == 2 else { return }
|
||||
channel = params[0].replacingOccurrences(of: "channel=", with: "")
|
||||
messageId = params[1].replacingOccurrences(of: "messageId=", with: "")
|
||||
}
|
||||
}
|
||||
|
||||
class DeepLinkManagerMap: DeepLinkTabManager {
|
||||
func handle(deepLink: String, selectedTab: inout Tab) -> Bool {
|
||||
if deepLink.contains("map") {
|
||||
selectedTab = .map
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
class DeepLinkManagerNodes: DeepLinkTabManager {
|
||||
func handle(deepLink: String, selectedTab: inout Tab) -> Bool {
|
||||
if deepLink.contains("nodes") {
|
||||
selectedTab = .nodes
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
class DeepLinkManagerSettings: DeepLinkTabManager {
|
||||
func handle(deepLink: String, selectedTab: inout Tab) -> Bool {
|
||||
if deepLink.contains("settings") {
|
||||
selectedTab = .settings
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,14 @@ import SwiftUI
|
|||
import MapKit
|
||||
import CocoaMQTT
|
||||
|
||||
public protocol DeviceConnection: AnyObject {
|
||||
var isSubscribed: Bool { get }
|
||||
var invalidVersion: Bool { get }
|
||||
//var handledDeepLinks: [DeepLink.Type] { get }
|
||||
//func canHandle(deepLink: DeepLink) -> Bool
|
||||
//func handle(deepLink: DeepLink)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------
|
||||
// Meshtastic BLE Device Manager
|
||||
// ---------------------------------------------------------------------------------------
|
||||
|
|
@ -20,7 +28,6 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
var context: NSManagedObjectContext?
|
||||
|
||||
static let shared = BLEManager()
|
||||
//var userSettings: UserSettings?
|
||||
private var centralManager: CBCentralManager!
|
||||
@Published var peripherals: [Peripheral] = []
|
||||
@Published var connectedPeripheral: Peripheral!
|
||||
|
|
@ -2633,6 +2640,7 @@ class BLEManager: NSObject, CBPeripheralDelegate, MqttClientProxyManagerDelegate
|
|||
newMessage.fromUser = fromUser
|
||||
newMessage.toUser = toUser
|
||||
|
||||
|
||||
do {
|
||||
connectedPeripheral.peripheral.writeValue(binaryData, for: TORADIO_characteristic, type: .withResponse)
|
||||
try context!.save()
|
||||
|
|
|
|||
|
|
@ -905,7 +905,7 @@ func textMessageAppPacket(packet: MeshPacket, wantRangeTestPackets: Bool, connec
|
|||
subtitle: "AKA \(newMessage.fromUser?.shortName ?? "?")",
|
||||
content: messageText!,
|
||||
target: "message",
|
||||
path: "meshtastic://messages/channel/\(newMessage.messageId)")
|
||||
path: "meshtastic://messages?channel=\(newMessage.channel)&messageId=\(newMessage.messageId)")
|
||||
]
|
||||
manager.schedule()
|
||||
print("💬 iOS Notification Scheduled for text message from \(newMessage.fromUser?.longName ?? "unknown".localized)")
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLIconFile</key>
|
||||
<string>alpha</string>
|
||||
<key>CFBundleURLName</key>
|
||||
|
|
|
|||
|
|
@ -6,9 +6,12 @@ import CoreData
|
|||
import TipKit
|
||||
#endif
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
@main
|
||||
struct MeshtasticAppleApp: App {
|
||||
|
||||
let deepLinkManager = DeepLinkManager()
|
||||
|
||||
@UIApplicationDelegateAdaptor(MeshtasticAppDelegate.self) var appDelegate
|
||||
let persistenceController = PersistenceController.shared
|
||||
@ObservedObject private var bleManager: BLEManager = BLEManager.shared
|
||||
|
|
@ -23,7 +26,7 @@ struct MeshtasticAppleApp: App {
|
|||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
ContentView(deepLinkManager: deepLinkManager)
|
||||
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
||||
.environmentObject(bleManager)
|
||||
.sheet(isPresented: $saveChannels) {
|
||||
|
|
@ -53,6 +56,10 @@ struct MeshtasticAppleApp: App {
|
|||
}
|
||||
}
|
||||
.onOpenURL(perform: { (url) in
|
||||
|
||||
if url.absoluteString.lowercased().contains("meshtastic://") {
|
||||
deepLinkManager.handleDeepLink(deepLink: url.absoluteString.lowercased())
|
||||
}
|
||||
|
||||
print("Some sort of URL was received \(url)")
|
||||
self.incomingUrl = url
|
||||
|
|
@ -62,6 +69,8 @@ struct MeshtasticAppleApp: App {
|
|||
}
|
||||
self.saveChannels = true
|
||||
print("User wants to open a Channel Settings URL: \(self.incomingUrl?.absoluteString ?? "No QR Code Link")")
|
||||
} else if url.absoluteString.lowercased().contains("meshtastic://") {
|
||||
deepLinkManager.handleDeepLink(deepLink: url.absoluteString.lowercased())
|
||||
} else {
|
||||
saveChannels = false
|
||||
print("User wants to import a MBTILES offline map file: \(self.incomingUrl?.absoluteString ?? "No Tiles link")")
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ func upsertNodeInfoPacket (packet: MeshPacket, context: NSManagedObjectContext)
|
|||
subtitle: "\(newUser.longName ?? "unknown".localized)",
|
||||
content: "New Node has been discovered",
|
||||
target: "nodeInfo",
|
||||
path: "meshtastic://nodeInfo"
|
||||
path: "meshtastic://nodes?num=\(newUser.num)"
|
||||
)
|
||||
]
|
||||
manager.schedule()
|
||||
|
|
|
|||
|
|
@ -4,11 +4,14 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
@available(iOS 17.0, *)
|
||||
struct ContentView: View {
|
||||
@State var deepLinkManager: DeepLinkManager
|
||||
|
||||
@StateObject var appState = AppState.shared
|
||||
var body: some View {
|
||||
TabView(selection: $appState.tabSelection) {
|
||||
Messages()
|
||||
Messages(deepLinkManager: deepLinkManager.features[0] as? DeepLinkManagerMessages)
|
||||
.tabItem {
|
||||
Label("messages", systemImage: "message")
|
||||
}
|
||||
|
|
@ -19,7 +22,7 @@ struct ContentView: View {
|
|||
Label("bluetooth", systemImage: "antenna.radiowaves.left.and.right")
|
||||
}
|
||||
.tag(Tab.ble)
|
||||
NodeList()
|
||||
NodeList(deepLinkManager: deepLinkManager.features[2] as? DeepLinkManagerNodes)
|
||||
.tabItem {
|
||||
Label("nodes", systemImage: "flipphone")
|
||||
}
|
||||
|
|
@ -54,14 +57,21 @@ struct ContentView: View {
|
|||
}
|
||||
}
|
||||
}
|
||||
//#Preview {
|
||||
// if #available(iOS 17.0, *) {
|
||||
// // ContentView(deepLinkManager: .init())
|
||||
// } else {
|
||||
// // Fallback on earlier versions
|
||||
// }
|
||||
//}
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
//struct ContentView_Previews: PreviewProvider {
|
||||
// static var previews: some View {
|
||||
// ContentView()
|
||||
// }
|
||||
//}
|
||||
|
||||
enum Tab {
|
||||
enum Tab: Hashable {
|
||||
case contacts
|
||||
case messages
|
||||
case map
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import TipKit
|
|||
#endif
|
||||
|
||||
struct Messages: View {
|
||||
|
||||
@State var deepLinkManager: DeepLinkManagerMessages
|
||||
|
||||
@StateObject var appState = AppState.shared
|
||||
@Environment(\.managedObjectContext) var context
|
||||
|
|
@ -27,6 +29,10 @@ struct Messages: View {
|
|||
case groupMessages
|
||||
case directMessages
|
||||
}
|
||||
|
||||
init (deepLinkManager: DeepLinkManagerMessages? = nil) {
|
||||
self.deepLinkManager = deepLinkManager ?? .init()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ struct MeshMapContent: MapContent {
|
|||
}
|
||||
}
|
||||
/// Reduced Precision Map Circles
|
||||
if 2...24 ~= position.precisionBits {
|
||||
if 10...19 ~= position.precisionBits {
|
||||
let pp = PositionPrecision(rawValue: Int(position.precisionBits))
|
||||
let radius : CLLocationDistance = pp?.precisionMeters ?? 0
|
||||
if radius > 0.0 {
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ struct NodeMapContent: MapContent {
|
|||
let pf = PositionFlags(rawValue: Int(position.nodePosition?.metadata?.positionFlags ?? 771))
|
||||
let headingDegrees = Angle.degrees(Double(position.heading))
|
||||
/// Reduced Precision Map Circle
|
||||
if position.latest && 2...24 ~= position.precisionBits {
|
||||
if position.latest && 10...19 ~= position.precisionBits {
|
||||
let pp = PositionPrecision(rawValue: Int(position.precisionBits))
|
||||
let radius : CLLocationDistance = pp?.precisionMeters ?? 0
|
||||
if radius > 0.0 {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import CoreLocation
|
|||
|
||||
struct NodeList: View {
|
||||
|
||||
@State var deepLinkManager: DeepLinkManagerNodes
|
||||
@State private var columnVisibility = NavigationSplitViewVisibility.all
|
||||
@State private var selectedNode: NodeInfoEntity?
|
||||
@State private var isPresentingTraceRouteSentAlert = false
|
||||
|
|
@ -41,6 +42,10 @@ struct NodeList: View {
|
|||
|
||||
var nodes: FetchedResults<NodeInfoEntity>
|
||||
|
||||
init (deepLinkManager: DeepLinkManagerNodes? = nil) {
|
||||
self.deepLinkManager = deepLinkManager ?? .init()
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationSplitView(columnVisibility: $columnVisibility) {
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue