diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index b6394ac6..e3ff05c8 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ DD1925B928CDA93900720036 /* SerialConfigEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1925B828CDA93900720036 /* SerialConfigEnums.swift */; }; DD1933762B0835D500771CD5 /* PositionAltitudeChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1933752B0835D500771CD5 /* PositionAltitudeChart.swift */; }; DD1933782B084F4200771CD5 /* Measurement.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1933772B084F4200771CD5 /* Measurement.swift */; }; + DD1B8F402B35E2F10022AABC /* GPSStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1B8F3F2B35E2F10022AABC /* GPSStatus.swift */; }; DD1BF2F92776FE2E008C8D2F /* UserMessageList.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */; }; DD2160AF28C5552500C17253 /* MQTTConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD2160AE28C5552500C17253 /* MQTTConfig.swift */; }; DD23A50F26FD1B4400D9B90C /* PeripheralModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */; }; @@ -235,6 +236,7 @@ DD1925B828CDA93900720036 /* SerialConfigEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SerialConfigEnums.swift; sourceTree = ""; }; DD1933752B0835D500771CD5 /* PositionAltitudeChart.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionAltitudeChart.swift; sourceTree = ""; }; DD1933772B084F4200771CD5 /* Measurement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Measurement.swift; sourceTree = ""; }; + DD1B8F3F2B35E2F10022AABC /* GPSStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GPSStatus.swift; sourceTree = ""; }; DD1BF2F82776FE2E008C8D2F /* UserMessageList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserMessageList.swift; sourceTree = ""; }; DD2160AE28C5552500C17253 /* MQTTConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MQTTConfig.swift; sourceTree = ""; }; DD23A50E26FD1B4400D9B90C /* PeripheralModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = ""; }; @@ -552,6 +554,7 @@ DD3CC6B428E33FD100FA9159 /* ShareChannels.swift */, DDCE4E2B2869F92900BE9F8F /* UserConfig.swift */, DD61937A2863876A00E59241 /* Config */, + DD1B8F3F2B35E2F10022AABC /* GPSStatus.swift */, ); path = Settings; sourceTree = ""; @@ -1222,6 +1225,7 @@ DDB75A112A059258006ED576 /* Url.swift in Sources */, DDAD49ED2AFB39DC00B4425D /* MeshMap.swift in Sources */, DD8169FB271F1F3A00F4AB02 /* MeshLog.swift in Sources */, + DD1B8F402B35E2F10022AABC /* GPSStatus.swift in Sources */, DD8ED9C52898D51F00B3B0AB /* NetworkConfig.swift in Sources */, DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */, DDDE5A1029AFE69700490C6C /* MeshActivityAttributes.swift in Sources */, diff --git a/Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift b/Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift index c041a7dd..09673104 100644 --- a/Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift +++ b/Meshtastic/Extensions/CoreData/NodeInfoEntityExtension.swift @@ -14,13 +14,11 @@ extension NodeInfoEntity { } var hasDeviceMetrics: Bool { - let deviceMetrics = telemetries?.filter{ ($0 as AnyObject).metricsType == 0 } return deviceMetrics?.count ?? 0 > 0 } var hasEnvironmentMetrics: Bool { - let environmentMetrics = telemetries?.filter{ ($0 as AnyObject).metricsType == 1 } return environmentMetrics?.count ?? 0 > 0 } @@ -28,8 +26,11 @@ extension NodeInfoEntity { return user?.sensorMessageList.count ?? 0 > 0 } + var hasTraceRoutes: Bool { + return traceRoutes?.count ?? 0 > 0 + } + var isOnline: Bool { - let fifteenMinutesAgo = Calendar.current.date(byAdding: .minute, value: -15, to: Date()) if lastHeard?.compare(fifteenMinutesAgo!) == .orderedDescending { return true diff --git a/Meshtastic/MeshtasticApp.swift b/Meshtastic/MeshtasticApp.swift index 5dee2b41..a8195b42 100644 --- a/Meshtastic/MeshtasticApp.swift +++ b/Meshtastic/MeshtasticApp.swift @@ -8,9 +8,18 @@ import TipKit @main struct MeshtasticAppleApp: App { + + private static var documentsFolder: URL { + do { + return try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) + } catch { + fatalError("Can't find documents directory.") + } + } @UIApplicationDelegateAdaptor(MeshtasticAppDelegate.self) var appDelegate let persistenceController = PersistenceController.shared @ObservedObject private var bleManager: BLEManager = BLEManager() + @Environment(\.scenePhase) var scenePhase @State var saveChannels = false diff --git a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift index fec2e026..b8fafe88 100644 --- a/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift +++ b/Meshtastic/Views/Nodes/Helpers/NodeListItem.swift @@ -123,6 +123,11 @@ struct NodeListItem: View { .symbolRenderingMode(.hierarchical) .font(.callout) } + if node.hasTraceRoutes { + Image(systemName: "signpost.right.and.left") + .symbolRenderingMode(.hierarchical) + .font(.callout) + } } .padding(.top, 3) } diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index ffa51ba4..f6baab3d 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -33,44 +33,7 @@ struct AppSettings: View { } Section(header: Text("phone.gps")) { if #available(iOS 17.0, macOS 14.0, *) { - let horizontalAccuracy = Measurement(value: LocationsHandler.shared.lastLocation.horizontalAccuracy, unit: UnitLength.meters) - let verticalAccuracy = Measurement(value: LocationsHandler.shared.lastLocation.verticalAccuracy, unit: UnitLength.meters) - let altitiude = Measurement(value: LocationsHandler.shared.lastLocation.altitude, unit: UnitLength.meters) - let speed = Measurement(value: LocationsHandler.shared.lastLocation.speed, unit: UnitSpeed.kilometersPerHour) - let speedAccuracy = Measurement(value: LocationsHandler.shared.lastLocation.speedAccuracy, unit: UnitSpeed.metersPerSecond) - Label("Coordinate \(String(format: "%.5f", LocationsHandler.shared.lastLocation.coordinate.latitude)), \(String(format: "%.5f", LocationsHandler.shared.lastLocation.coordinate.longitude))", systemImage: "mappin") - .font(.footnote) - .textSelection(.enabled) - HStack { - Label("Accuracy \(horizontalAccuracy.formatted())", systemImage: "scope") - .font(.footnote) - Label("Sats \(LocationsHandler.satsInView)", systemImage: "sparkles") - .font(.footnote) - } - HStack { - if LocationsHandler.shared.lastLocation.verticalAccuracy > 0 { - Label("Altitude \(altitiude.formatted())", systemImage: "mountain.2") - .font(.footnote) - } - Label("Accuracy \(verticalAccuracy.formatted())", systemImage: "lines.measurement.vertical") - .font(.footnote) - } - if LocationsHandler.shared.lastLocation.courseAccuracy > 0 { - let degrees = Angle.degrees(Double(LocationsHandler.shared.lastLocation.course)) - Label { - let heading = Measurement(value: degrees.degrees, unit: UnitAngle.degrees) - Text("Heading: \(heading.formatted())") - } icon: { - Image(systemName: "location.north") - .symbolRenderingMode(.hierarchical) - .rotationEffect(degrees) - } - .font(.footnote) - } - if LocationsHandler.shared.lastLocation.speedAccuracy > 0 { - Label("Speed \(speed.formatted())", systemImage: "speedometer") - .font(.footnote) - } + GPSStatus() } else { let accuracy = Measurement(value: locationHelper.locationManager.location?.horizontalAccuracy ?? 300, unit: UnitLength.meters) let altitiude = Measurement(value: locationHelper.locationManager.location?.altitude ?? 0, unit: UnitLength.meters) diff --git a/Meshtastic/Views/Settings/GPSStatus.swift b/Meshtastic/Views/Settings/GPSStatus.swift new file mode 100644 index 00000000..72308fd9 --- /dev/null +++ b/Meshtastic/Views/Settings/GPSStatus.swift @@ -0,0 +1,62 @@ +// +// GPSStatus.swift +// Meshtastic +// +// Copyright(c) by Garth Vander Houwen 12/22/23. +// + +import SwiftUI +import CoreLocation + +@available(iOS 17.0, macOS 14.0, *) +struct GPSStatus: View { + + @ObservedObject var locationsHandler: LocationsHandler = LocationsHandler.shared + + var body: some View { + + let horizontalAccuracy = Measurement(value: locationsHandler.lastLocation.horizontalAccuracy, unit: UnitLength.meters) + let verticalAccuracy = Measurement(value: locationsHandler.lastLocation.verticalAccuracy, unit: UnitLength.meters) + let altitiude = Measurement(value: locationsHandler.lastLocation.altitude, unit: UnitLength.meters) + let speed = Measurement(value: locationsHandler.lastLocation.speed, unit: UnitSpeed.kilometersPerHour) + let speedAccuracy = Measurement(value: locationsHandler.lastLocation.speedAccuracy, unit: UnitSpeed.metersPerSecond) + let courseAccuracy = Measurement(value: locationsHandler.lastLocation.courseAccuracy, unit: UnitAngle.degrees) + Label("Coordinate \(String(format: "%.5f", locationsHandler.lastLocation.coordinate.latitude)), \(String(format: "%.5f", LocationsHandler.shared.lastLocation.coordinate.longitude))", systemImage: "mappin") + .font(.footnote) + .textSelection(.enabled) + HStack { + Label("Accuracy \(horizontalAccuracy.formatted())", systemImage: "scope") + .font(.footnote) + Label("Sats Estimate \(LocationsHandler.satsInView)", systemImage: "sparkles") + .font(.footnote) + } + HStack { + if locationsHandler.lastLocation.verticalAccuracy > 0 { + Label("Altitude \(altitiude.formatted())", systemImage: "mountain.2") + .font(.footnote) + } + Label("Accuracy \(verticalAccuracy.formatted())", systemImage: "lines.measurement.vertical") + .font(.caption2) + } + HStack { + let degrees = Angle.degrees(LocationsHandler.shared.lastLocation.course) + Label { + let heading = Measurement(value: degrees.degrees, unit: UnitAngle.degrees) + Text("Heading: \(heading.formatted())") + } icon: { + Image(systemName: "location.north") + .symbolRenderingMode(.hierarchical) + .rotationEffect(degrees) + } + .font(.footnote) + Label("Accuracy \(courseAccuracy.formatted())", systemImage: "safari") + .font(.caption2) + } + HStack { + Label("Speed \(speed.formatted())", systemImage: "speedometer") + .font(.footnote) + Label("Accuracy \(speedAccuracy.formatted())", systemImage: "gauge.with.dots.needle.bottom.50percent.badge.plus") + .font(.caption2) + } + } +}