Deep Links

Updated position precision
This commit is contained in:
Garth Vander Houwen 2024-05-06 23:03:51 -07:00
parent 4af2fbc749
commit 8bcf40f543
12 changed files with 173 additions and 15 deletions

View file

@ -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 */,

View 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
}
}

View file

@ -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()

View file

@ -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)")

View file

@ -35,7 +35,7 @@
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<string>Editor</string>
<key>CFBundleURLIconFile</key>
<string>alpha</string>
<key>CFBundleURLName</key>

View file

@ -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")")

View file

@ -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()

View file

@ -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

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -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) {