From fe12a9f2fd568a9b1816b3db70e4815c50ca316a Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 19 Mar 2023 21:14:28 -0700 Subject: [PATCH 1/9] Bump version, show route line and not history on node details --- Meshtastic.xcodeproj/project.pbxproj | 4 ++-- Meshtastic/Views/Nodes/NodeDetail.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index 7c131f75..1353fce8 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -1188,7 +1188,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.1.2; + MARKETING_VERSION = 2.1.3; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; @@ -1222,7 +1222,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 2.1.2; + MARKETING_VERSION = 2.1.3; PRODUCT_BUNDLE_IDENTIFIER = gvh.MeshtasticClient; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTS_MACCATALYST = YES; diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index 3f3f518a..bfe67a30 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -72,8 +72,8 @@ struct NodeDetail: View { mapViewType: mapType, userTrackingMode: MKUserTrackingMode.none, centeringMode: .allPositions, - showRouteLines: false, - showNodeHistory: true, + showRouteLines: true, + showNodeHistory: false, centerOnPositionsOnly: true, customMapOverlay: self.customMapOverlay, overlays: self.overlays From c084961c199d8fc6197b7b64b3b8cada7d875a6f Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sun, 19 Mar 2023 22:11:46 -0700 Subject: [PATCH 2/9] Delete Centering Mode Add show node history toggle add show route lines toggle --- Meshtastic/Enums/AppSettingsEnums.swift | 16 --------- Meshtastic/Model/UserSettings.swift | 14 ++++++++ .../Views/Map/Custom/MapViewSwiftUI.swift | 35 ++++--------------- Meshtastic/Views/Nodes/NodeDetail.swift | 8 ++--- Meshtastic/Views/Nodes/NodeMap.swift | 8 ++--- Meshtastic/Views/Settings/AppSettings.swift | 17 +++++---- 6 files changed, 37 insertions(+), 61 deletions(-) diff --git a/Meshtastic/Enums/AppSettingsEnums.swift b/Meshtastic/Enums/AppSettingsEnums.swift index c9274bf0..e0772c35 100644 --- a/Meshtastic/Enums/AppSettingsEnums.swift +++ b/Meshtastic/Enums/AppSettingsEnums.swift @@ -33,22 +33,6 @@ enum KeyboardType: Int, CaseIterable, Identifiable { } } -enum CenteringMode: Int, CaseIterable, Identifiable { - - case allAnnotations = 0 - case allPositions = 1 - - var id: Int { self.rawValue } - var description: String { - switch self { - case .allAnnotations: - return "All Annotations"// NSLocalizedString("default", comment: "Default Keyboard") - case .allPositions: - return "All Node Postions"// NSLocalizedString("ascii.capable", comment: "ASCII Capable Keyboard") - } - } -} - enum MeshMapType: String, CaseIterable, Identifiable { case standard diff --git a/Meshtastic/Model/UserSettings.swift b/Meshtastic/Model/UserSettings.swift index bb8de9ee..585559ee 100644 --- a/Meshtastic/Model/UserSettings.swift +++ b/Meshtastic/Model/UserSettings.swift @@ -62,6 +62,18 @@ class UserSettings: ObservableObject { UserDefaults.standard.synchronize() } } + @Published var meshMapShowNodeHistory: Bool { + didSet { + UserDefaults.standard.set(meshMapShowNodeHistory, forKey: "meshMapShowNodeHistory") + UserDefaults.standard.synchronize() + } + } + @Published var meshMapShowRouteLines: Bool { + didSet { + UserDefaults.standard.set(meshMapShowRouteLines, forKey: "meshMapShowRouteLines") + UserDefaults.standard.synchronize() + } + } init() { @@ -75,5 +87,7 @@ class UserSettings: ObservableObject { self.meshMapRecentering = UserDefaults.standard.object(forKey: "meshMapRecentering") as? Bool ?? false self.meshMapCustomTileServer = UserDefaults.standard.string(forKey: "meshMapCustomTileServer") ?? "" self.meshMapUserTrackingMode = UserDefaults.standard.object(forKey: "meshMapUserTrackingMode") as? Int ?? 0 + self.meshMapShowNodeHistory = UserDefaults.standard.object(forKey: "meshMapShowNodeHistory") as? Bool ?? true + self.meshMapShowRouteLines = UserDefaults.standard.object(forKey: "meshMapShowRouteLines") as? Bool ?? false } } diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 44fc5c5c..51c0421b 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -20,10 +20,8 @@ struct MapViewSwiftUI: UIViewRepresentable { let waypoints: [WaypointEntity] let mapViewType: MKMapType let userTrackingMode: MKUserTrackingMode - let centeringMode: CenteringMode let showRouteLines: Bool let showNodeHistory: Bool - let centerOnPositionsOnly: Bool @AppStorage("meshMapRecentering") private var recenter: Bool = false // Offline Maps @@ -49,24 +47,10 @@ struct MapViewSwiftUI: UIViewRepresentable { mapView.setUserTrackingMode(userTrackingMode, animated: true) if userTrackingMode == MKUserTrackingMode.none { mapView.showsUserLocation = false - switch centeringMode { - case .allAnnotations: - mapView.addAnnotations(showNodeHistory ? positions : latest) - if userTrackingMode == MKUserTrackingMode.none { - mapView.fitAllAnnotations() - } - case .allPositions: - if userTrackingMode == MKUserTrackingMode.none { - mapView.addAnnotations(showNodeHistory ? positions : latest) - mapView.fit(annotations: positions, andShow: false) - } else { - mapView.addAnnotations(showNodeHistory ? positions : latest) - } - } } else { - mapView.addAnnotations(showNodeHistory ? positions : latest) mapView.showsUserLocation = true } + mapView.addAnnotations(showNodeHistory ? positions : latest) // Other MKMapView Settings mapView.preferredConfiguration.elevationStyle = .realistic// .flat mapView.isPitchEnabled = true @@ -147,22 +131,17 @@ struct MapViewSwiftUI: UIViewRepresentable { if userTrackingMode == MKUserTrackingMode.none { mapView.showsUserLocation = false - switch centeringMode { - case .allAnnotations: - mapView.addAnnotations(showNodeHistory ? positions : latest) - if recenter && userTrackingMode == MKUserTrackingMode.none { - mapView.fitAllAnnotations() - } - case .allPositions: - if recenter && userTrackingMode == MKUserTrackingMode.none { - mapView.fit(annotations: showNodeHistory ? positions : latest, andShow: true) + mapView.addAnnotations(showNodeHistory ? positions : latest) + if recenter { + if showRouteLines || showNodeHistory { + mapView.fit(annotations: showNodeHistory ? positions : positions, andShow: false) } else { - mapView.addAnnotations(showNodeHistory ? positions : latest) + mapView.fitAllAnnotations() } } } else { // Centering Done by tracking mode - mapView.addAnnotations(latest) + mapView.addAnnotations(showNodeHistory ? positions : latest) mapView.showsUserLocation = true } } diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index bfe67a30..ced57163 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -14,6 +14,8 @@ struct NodeDetail: View { @EnvironmentObject var bleManager: BLEManager @Environment(\.colorScheme) var colorScheme: ColorScheme @AppStorage("meshMapType") private var meshMapType = "standard" + @AppStorage("meshMapShowNodeHistory") private var meshMapShowNodeHistory = false + @AppStorage("meshMapShowRouteLines") private var meshMapShowRouteLines = false @State private var mapType: MKMapType = .standard @State var waypointCoordinate: CLLocationCoordinate2D? @State var editingWaypoint: Int = 0 @@ -71,10 +73,8 @@ struct NodeDetail: View { }, positions: annotations, waypoints: Array(waypoints), mapViewType: mapType, userTrackingMode: MKUserTrackingMode.none, - centeringMode: .allPositions, - showRouteLines: true, - showNodeHistory: false, - centerOnPositionsOnly: true, + showRouteLines: meshMapShowRouteLines, + showNodeHistory: meshMapShowNodeHistory, customMapOverlay: self.customMapOverlay, overlays: self.overlays ) diff --git a/Meshtastic/Views/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index 297d8136..b125fc96 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -31,7 +31,7 @@ struct NodeMap: View { } @AppStorage("meshMapType") private var meshMapType = "hybridFlyover" @AppStorage("meshMapUserTrackingMode") private var meshMapUserTrackingMode = 0 - @AppStorage("meshMapCenteringMode") private var meshMapCenteringMode = 0 + @AppStorage("meshMapShowNodeHistory") private var meshMapShowNodeHistory = false // && nodePosition != nil @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "time", ascending: true)], @@ -46,7 +46,6 @@ struct NodeMap: View { @State private var mapType: MKMapType = .standard @State private var userTrackingMode: MKUserTrackingMode = .none - @State private var mapCenteringMode: CenteringMode = .allAnnotations @State var waypointCoordinate: CLLocationCoordinate2D = LocationHelper.DefaultLocation @State var editingWaypoint: Int = 0 @State private var presentingWaypointForm = false @@ -83,10 +82,8 @@ struct NodeMap: View { waypoints: Array(waypoints), mapViewType: mapType, userTrackingMode: userTrackingMode, - centeringMode: mapCenteringMode, showRouteLines: false, - showNodeHistory: true, - centerOnPositionsOnly: false, + showNodeHistory: meshMapShowNodeHistory, customMapOverlay: self.customMapOverlay, overlays: self.overlays ) @@ -124,7 +121,6 @@ struct NodeMap: View { UIApplication.shared.isIdleTimerDisabled = true self.bleManager.context = context self.bleManager.userSettings = userSettings - mapCenteringMode = CenteringMode(rawValue: meshMapCenteringMode) ?? CenteringMode.allAnnotations userTrackingMode = UserTrackingModes(rawValue: meshMapUserTrackingMode)?.MKUserTrackingModeValue() ?? MKUserTrackingMode.none switch meshMapType { case "standard": diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index 70b525cc..ec83fae9 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -80,19 +80,22 @@ struct AppSettings: View { if userSettings.meshMapUserTrackingMode == 0 { - Picker("map.centering", selection: $userSettings.meshMapCenteringMode) { - ForEach(CenteringMode.allCases) { cm in - Text(cm.description) - } - } - .pickerStyle(DefaultPickerStyle()) - Toggle(isOn: $userSettings.meshMapRecentering) { Label("map.recentering", systemImage: "camera.metering.center.weighted") } .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } + Toggle(isOn: $userSettings.meshMapShowNodeHistory) { + + Label("Show Node History", systemImage: "building.columns.fill") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) + Toggle(isOn: $userSettings.meshMapShowRouteLines) { + + Label("Show Route Lines", systemImage: "road.lanes") + } + .toggleStyle(SwitchToggleStyle(tint: .accentColor)) } } HStack { From f867a180419af292be24456c362cc8083721433f Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 20 Mar 2023 13:10:59 -0700 Subject: [PATCH 3/9] Fix battery gauge bug, make route lines real time --- .../Views/Map/Custom/MapViewSwiftUI.swift | 87 ++++++++++--------- Meshtastic/Views/Nodes/NodeDetail.swift | 23 ++--- Meshtastic/Views/Nodes/NodeMap.swift | 9 +- 3 files changed, 59 insertions(+), 60 deletions(-) diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 51c0421b..62471651 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -23,15 +23,14 @@ struct MapViewSwiftUI: UIViewRepresentable { let showRouteLines: Bool let showNodeHistory: Bool @AppStorage("meshMapRecentering") private var recenter: Bool = false - // Offline Maps // make this view dependent on the UserDefault that is updated when importing a new map file @AppStorage("lastUpdatedLocalMapFile") private var lastUpdatedLocalMapFile = 0 @State private var loadedLastUpdatedLocalMapFile = 0 var customMapOverlay: CustomMapOverlay? @State private var presentCustomMapOverlayHash: CustomMapOverlay? - var overlays: [Overlay] = [] - let dynamicRegion: Bool = true + + //let dynamicRegion: Bool = true func makeUIView(context: Context) -> MKMapView { // Map View Parameters @@ -107,20 +106,26 @@ struct MapViewSwiftUI: UIViewRepresentable { DispatchQueue.main.async { self.presentCustomMapOverlayHash = self.customMapOverlay self.loadedLastUpdatedLocalMapFile = self.lastUpdatedLocalMapFile - - if showRouteLines { - let nodePositions = positions.filter { $0.time! >= Calendar.current.startOfDay(for: Date()) } - let lineCoords = nodePositions.map ({ - (position) -> CLLocationCoordinate2D in - return position.nodeCoordinate! - }) - let polyline = MKPolyline(coordinates: lineCoords, count: nodePositions.count) - mapView.addOverlay(polyline) - } } } DispatchQueue.main.async { + + if showRouteLines { + // Remove all existing PolyLine Overlays + for overlay in mapView.overlays { + if overlay is MKPolyline { + mapView.removeOverlay(overlay) + } + } + let nodePositions = positions// positions.filter { $0.time! >= Calendar.current.startOfDay(for: Date()) } + let lineCoords = nodePositions.map ({ + (position) -> CLLocationCoordinate2D in + return position.nodeCoordinate! + }) + let polyline = MKPolyline(coordinates: lineCoords, count: nodePositions.count) + mapView.addOverlay(polyline) + } let annotationCount = waypoints.count + positions.count if annotationCount != mapView.annotations.count { @@ -156,7 +161,7 @@ struct MapViewSwiftUI: UIViewRepresentable { var parent: MapViewSwiftUI var longPressRecognizer = UILongPressGestureRecognizer() - var overlays: [Overlay] = [] + //var overlays: [Overlay] = [] init(_ parent: MapViewSwiftUI) { self.parent = parent @@ -166,7 +171,7 @@ struct MapViewSwiftUI: UIViewRepresentable { self.longPressRecognizer.cancelsTouchesInView = true self.longPressRecognizer.delegate = self self.parent.mapView.addGestureRecognizer(longPressRecognizer) - self.overlays = [] + //self.overlays = [] } func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { @@ -443,30 +448,30 @@ struct MapViewSwiftUI: UIViewRepresentable { } } - public struct Overlay { - - public static func == (lhs: MapViewSwiftUI.Overlay, rhs: MapViewSwiftUI.Overlay) -> Bool { - // maybe to use in the future for comparison of full array - lhs.shape.coordinate.latitude == rhs.shape.coordinate.latitude && - lhs.shape.coordinate.longitude == rhs.shape.coordinate.longitude && - lhs.fillColor == rhs.fillColor - } - - var shape: MKOverlay - var fillColor: UIColor? - var strokeColor: UIColor? - var lineWidth: CGFloat - - public init( - shape: MKOverlay, - fillColor: UIColor? = nil, - strokeColor: UIColor? = nil, - lineWidth: CGFloat = 0 - ) { - self.shape = shape - self.fillColor = fillColor - self.strokeColor = strokeColor - self.lineWidth = lineWidth - } - } +// public struct Overlay { +// +// public static func == (lhs: MapViewSwiftUI.Overlay, rhs: MapViewSwiftUI.Overlay) -> Bool { +// // maybe to use in the future for comparison of full array +// lhs.shape.coordinate.latitude == rhs.shape.coordinate.latitude && +// lhs.shape.coordinate.longitude == rhs.shape.coordinate.longitude && +// lhs.fillColor == rhs.fillColor +// } +// +// var shape: MKOverlay +// var fillColor: UIColor? +// var strokeColor: UIColor? +// var lineWidth: CGFloat +// +// public init( +// shape: MKOverlay, +// fillColor: UIColor? = nil, +// strokeColor: UIColor? = nil, +// lineWidth: CGFloat = 0 +// ) { +// self.shape = shape +// self.fillColor = fillColor +// self.strokeColor = strokeColor +// self.lineWidth = lineWidth +// } +// } } diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index ced57163..8ea57cb1 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -26,7 +26,6 @@ struct NodeDetail: View { @State private var showingRebootConfirm: Bool = false @State private var presentingWaypointForm = false @State private var showOverlays: Bool = true - @State private var overlays: [MapViewSwiftUI.Overlay] = [] @State private var customMapOverlay: MapViewSwiftUI.CustomMapOverlay? = MapViewSwiftUI.CustomMapOverlay( mapName: "offlinemap", tileType: "png", @@ -49,7 +48,8 @@ struct NodeDetail: View { @State private var attributionLink: URL? @State private var attributionLogo: URL? - + + var body: some View { let hwModelString = node.user?.hwModel ?? "UNSET" @@ -75,8 +75,7 @@ struct NodeDetail: View { userTrackingMode: MKUserTrackingMode.none, showRouteLines: meshMapShowRouteLines, showNodeHistory: meshMapShowNodeHistory, - customMapOverlay: self.customMapOverlay, - overlays: self.overlays + customMapOverlay: self.customMapOverlay ) VStack(alignment: .leading) { Spacer() @@ -177,14 +176,14 @@ struct NodeDetail: View { .fixedSize() } } - - if node.telemetries?.count ?? 0 >= 1 { - - let mostRecent = node.telemetries?.lastObject as? TelemetryEntity + let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0")) + if deviceMetrics?.count ?? 0 >= 1 { + + let mostRecent = deviceMetrics?.lastObject as? TelemetryEntity Divider() VStack(alignment: .center) { BatteryGauge(batteryLevel: Double(mostRecent?.batteryLevel ?? 0)) - if mostRecent?.voltage ?? 0 > 0 { + if mostRecent?.voltage ?? 0 > 0.0 { Text(String(format: "%.2f", mostRecent?.voltage ?? 0.0) + " V") .font(.title) @@ -288,8 +287,10 @@ struct NodeDetail: View { } } - if node.telemetries?.count ?? 0 >= 1 { - let mostRecent = node.telemetries?.lastObject as? TelemetryEntity + let deviceMetrics = node.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0")) + if deviceMetrics?.count ?? 0 >= 1 { + + let mostRecent = deviceMetrics?.lastObject as? TelemetryEntity Divider() VStack(alignment: .center) { BatteryGauge(batteryLevel: Double(mostRecent?.batteryLevel ?? 0)) diff --git a/Meshtastic/Views/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index b125fc96..590bbace 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -33,7 +33,6 @@ struct NodeMap: View { @AppStorage("meshMapUserTrackingMode") private var meshMapUserTrackingMode = 0 @AppStorage("meshMapShowNodeHistory") private var meshMapShowNodeHistory = false - // && nodePosition != nil @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "time", ascending: true)], predicate: NSPredicate(format: "time >= %@ && nodePosition != nil", Calendar.current.startOfDay(for: Date()) as NSDate), animation: .none) private var positions: FetchedResults @@ -54,11 +53,6 @@ struct NodeMap: View { tileType: "png", canReplaceMapContent: true ) - @State private var overlays: [MapViewSwiftUI.Overlay] = [] - -// init() { -// _positions = FetchRequest(sortDescriptors: [NSSortDescriptor(key: "time", ascending: true)], predicate: NSPredicate(format: "time >= %@ && nodePosition != nil", Calendar.current.startOfDay(for: Date()) as NSDate), animation: .none) -// } var body: some View { @@ -84,8 +78,7 @@ struct NodeMap: View { userTrackingMode: userTrackingMode, showRouteLines: false, showNodeHistory: meshMapShowNodeHistory, - customMapOverlay: self.customMapOverlay, - overlays: self.overlays + customMapOverlay: self.customMapOverlay ) VStack { Spacer() From b8db2411101e81cbed00cc52779817d01152d685 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Mon, 20 Mar 2023 17:49:29 -0700 Subject: [PATCH 4/9] Route lines for the mesh map --- .../Views/Map/Custom/MapViewSwiftUI.swift | 36 ++++++++++++------- Meshtastic/Views/Nodes/NodeMap.swift | 3 +- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 62471651..616ee1de 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -30,7 +30,7 @@ struct MapViewSwiftUI: UIViewRepresentable { var customMapOverlay: CustomMapOverlay? @State private var presentCustomMapOverlayHash: CustomMapOverlay? - //let dynamicRegion: Bool = true + let colors: [UIColor] = [UIColor.systemIndigo, UIColor.blue, UIColor.purple, UIColor.green, UIColor.brown, UIColor.purple, UIColor.systemMint, UIColor.cyan, UIColor.magenta, UIColor.systemPink] func makeUIView(context: Context) -> MKMapView { // Map View Parameters @@ -110,6 +110,9 @@ struct MapViewSwiftUI: UIViewRepresentable { } DispatchQueue.main.async { + var latest = positions + .filter { $0.latest == true } + .sorted { $0.nodePosition?.num ?? 0 > $1.nodePosition?.num ?? -1 } if showRouteLines { // Remove all existing PolyLine Overlays @@ -118,19 +121,28 @@ struct MapViewSwiftUI: UIViewRepresentable { mapView.removeOverlay(overlay) } } - let nodePositions = positions// positions.filter { $0.time! >= Calendar.current.startOfDay(for: Date()) } - let lineCoords = nodePositions.map ({ - (position) -> CLLocationCoordinate2D in - return position.nodeCoordinate! - }) - let polyline = MKPolyline(coordinates: lineCoords, count: nodePositions.count) - mapView.addOverlay(polyline) + var lineIndex = 0 + for position in latest { + + let nodePositions = positions.filter { $0.time! >= Calendar.current.startOfDay(for: Date()) && $0.nodePosition?.num ?? 0 == position.nodePosition?.num ?? -1 } + let lineCoords = nodePositions.map ({ + (position) -> CLLocationCoordinate2D in + return position.nodeCoordinate! + }) + let polyline = MKPolyline(coordinates: lineCoords, count: nodePositions.count) + polyline.title = "\(String(position.nodePosition?.num ?? 0))-\(String(lineIndex))" + mapView.addOverlay(polyline) + lineIndex += 1 + // There are 10 colors for lines, start over if we are at index 11 + if lineIndex > 9 { + lineIndex = 0 + } + } } let annotationCount = waypoints.count + positions.count if annotationCount != mapView.annotations.count { mapView.removeAnnotations(mapView.annotations) - let latest = positions.filter { $0.latest == true } mapView.addAnnotations(waypoints) mapView.setUserTrackingMode(userTrackingMode, animated: true) @@ -161,7 +173,6 @@ struct MapViewSwiftUI: UIViewRepresentable { var parent: MapViewSwiftUI var longPressRecognizer = UILongPressGestureRecognizer() - //var overlays: [Overlay] = [] init(_ parent: MapViewSwiftUI) { self.parent = parent @@ -171,7 +182,6 @@ struct MapViewSwiftUI: UIViewRepresentable { self.longPressRecognizer.cancelsTouchesInView = true self.longPressRecognizer.delegate = self self.parent.mapView.addGestureRecognizer(longPressRecognizer) - //self.overlays = [] } func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { @@ -340,8 +350,10 @@ struct MapViewSwiftUI: UIViewRepresentable { } else { if let routePolyline = overlay as? MKPolyline { + let titleString = routePolyline.title ?? "None-0" + let index = Int(titleString.components(separatedBy: "-").last ?? "0") let renderer = MKPolylineRenderer(polyline: routePolyline) - renderer.strokeColor = UIColor.systemIndigo + renderer.strokeColor = parent.colors[index ?? 0] renderer.lineWidth = 5 return renderer } diff --git a/Meshtastic/Views/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index 590bbace..42545bd1 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -32,6 +32,7 @@ struct NodeMap: View { @AppStorage("meshMapType") private var meshMapType = "hybridFlyover" @AppStorage("meshMapUserTrackingMode") private var meshMapUserTrackingMode = 0 @AppStorage("meshMapShowNodeHistory") private var meshMapShowNodeHistory = false + @AppStorage("meshMapShowRouteLines") private var meshMapShowRouteLines = false @FetchRequest(sortDescriptors: [NSSortDescriptor(key: "time", ascending: true)], predicate: NSPredicate(format: "time >= %@ && nodePosition != nil", Calendar.current.startOfDay(for: Date()) as NSDate), animation: .none) @@ -76,7 +77,7 @@ struct NodeMap: View { waypoints: Array(waypoints), mapViewType: mapType, userTrackingMode: userTrackingMode, - showRouteLines: false, + showRouteLines: meshMapShowRouteLines, showNodeHistory: meshMapShowNodeHistory, customMapOverlay: self.customMapOverlay ) From 9096dbc72680463c4d036467301efd6a21f81cb6 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 21 Mar 2023 17:53:53 -0700 Subject: [PATCH 5/9] Map Cleanup Allow qr code to share only secondary channels --- Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift | 11 ++++++----- Meshtastic/Views/Nodes/NodeDetail.swift | 2 +- Meshtastic/Views/Nodes/NodeMap.swift | 2 +- Meshtastic/Views/Settings/ShareChannels.swift | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 616ee1de..2fb8b422 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -20,8 +20,10 @@ struct MapViewSwiftUI: UIViewRepresentable { let waypoints: [WaypointEntity] let mapViewType: MKMapType let userTrackingMode: MKUserTrackingMode - let showRouteLines: Bool let showNodeHistory: Bool + let showRouteLines: Bool + let colors: [UIColor] = [UIColor.systemIndigo, UIColor.orange, UIColor.green, UIColor.brown, UIColor.purple, UIColor.systemMint, UIColor.cyan, UIColor.magenta, UIColor.systemPink, UIColor.blue] + @AppStorage("meshMapRecentering") private var recenter: Bool = false // Offline Maps // make this view dependent on the UserDefault that is updated when importing a new map file @@ -30,8 +32,7 @@ struct MapViewSwiftUI: UIViewRepresentable { var customMapOverlay: CustomMapOverlay? @State private var presentCustomMapOverlayHash: CustomMapOverlay? - let colors: [UIColor] = [UIColor.systemIndigo, UIColor.blue, UIColor.purple, UIColor.green, UIColor.brown, UIColor.purple, UIColor.systemMint, UIColor.cyan, UIColor.magenta, UIColor.systemPink] - + func makeUIView(context: Context) -> MKMapView { // Map View Parameters mapView.mapType = mapViewType @@ -110,7 +111,7 @@ struct MapViewSwiftUI: UIViewRepresentable { } DispatchQueue.main.async { - var latest = positions + let latest = positions .filter { $0.latest == true } .sorted { $0.nodePosition?.num ?? 0 > $1.nodePosition?.num ?? -1 } @@ -133,7 +134,7 @@ struct MapViewSwiftUI: UIViewRepresentable { polyline.title = "\(String(position.nodePosition?.num ?? 0))-\(String(lineIndex))" mapView.addOverlay(polyline) lineIndex += 1 - // There are 10 colors for lines, start over if we are at index 11 + // There are 10 colors for lines, start over if we are at index 10 if lineIndex > 9 { lineIndex = 0 } diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index 8ea57cb1..e69913a2 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -73,8 +73,8 @@ struct NodeDetail: View { }, positions: annotations, waypoints: Array(waypoints), mapViewType: mapType, userTrackingMode: MKUserTrackingMode.none, - showRouteLines: meshMapShowRouteLines, showNodeHistory: meshMapShowNodeHistory, + showRouteLines: meshMapShowRouteLines, customMapOverlay: self.customMapOverlay ) VStack(alignment: .leading) { diff --git a/Meshtastic/Views/Nodes/NodeMap.swift b/Meshtastic/Views/Nodes/NodeMap.swift index 42545bd1..4cba5089 100644 --- a/Meshtastic/Views/Nodes/NodeMap.swift +++ b/Meshtastic/Views/Nodes/NodeMap.swift @@ -77,8 +77,8 @@ struct NodeMap: View { waypoints: Array(waypoints), mapViewType: mapType, userTrackingMode: userTrackingMode, + showNodeHistory: meshMapShowNodeHistory, showRouteLines: meshMapShowRouteLines, - showNodeHistory: meshMapShowNodeHistory, customMapOverlay: self.customMapOverlay ) VStack { diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index 44d9cbfc..5ca297dd 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -75,7 +75,6 @@ struct ShareChannels: View { Toggle("Channel 0 Included", isOn: $includeChannel0) .toggleStyle(.switch) .labelsHidden() - .disabled(channel.role == 1) Text(((channel.name!.isEmpty ? "Primary" : channel.name) ?? "Primary").camelCaseToWords()) if channel.psk?.hexDescription.count ?? 0 < 3 { Image(systemName: "lock.slash") @@ -264,6 +263,7 @@ struct ShareChannels: View { bleManager.context = context generateChannelSet() } + .onChange(of: includeChannel0) { _ in generateChannelSet() } .onChange(of: includeChannel1) { _ in generateChannelSet() } .onChange(of: includeChannel2) { _ in generateChannelSet() } .onChange(of: includeChannel3) { _ in generateChannelSet() } From fd502e271f88ab1ed49c3c955cf06684eff648a3 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 21 Mar 2023 18:13:17 -0700 Subject: [PATCH 6/9] Set node info app packet channel to 0 to test cross mesh secondary channels --- Meshtastic/Helpers/MeshPackets.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index d329dceb..daa6744f 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -251,7 +251,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje let newNode = NodeInfoEntity(context: context) newNode.id = Int64(nodeInfo.num) newNode.num = Int64(nodeInfo.num) - newNode.channel = Int32(channel) + newNode.channel = 0//Int32(channel) if nodeInfo.hasDeviceMetrics { let telemetry = TelemetryEntity(context: context) From 67f532f17023658302c1f5f9036b386efdfefde6 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Tue, 21 Mar 2023 20:21:48 -0700 Subject: [PATCH 7/9] Fix rebroadcast type enum --- Meshtastic/Enums/DeviceEnums.swift | 2 +- Meshtastic/Protobufs/meshtastic/config.pb.swift | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Meshtastic/Enums/DeviceEnums.swift b/Meshtastic/Enums/DeviceEnums.swift index a53a9893..186502ab 100644 --- a/Meshtastic/Enums/DeviceEnums.swift +++ b/Meshtastic/Enums/DeviceEnums.swift @@ -101,7 +101,7 @@ enum RebroadcastModes: Int, CaseIterable, Identifiable { case .allSkipDecoding: return "Same as behavior as ALL but skips packet decoding and simply rebroadcasts them. Only available in Repeater role. Setting this on any other roles will result in ALL behavior." case .localOnly: - return "Inverted top bar for 2 Color display" + return "Ignores observed messages from foreign meshes that are open or those which it cannot decrypt. Only rebroadcasts message on the nodes local primary / secondary channels." } } func protoEnumValue() -> Config.DeviceConfig.RebroadcastMode { diff --git a/Meshtastic/Protobufs/meshtastic/config.pb.swift b/Meshtastic/Protobufs/meshtastic/config.pb.swift index 04f17cdb..7be86a36 100644 --- a/Meshtastic/Protobufs/meshtastic/config.pb.swift +++ b/Meshtastic/Protobufs/meshtastic/config.pb.swift @@ -681,6 +681,10 @@ struct Config { /// Print first line in pseudo-bold? FALSE is original style, TRUE is bold var headingBold: Bool = false + /// + /// Should we wake the screen up on accelerometer detected motion or tap + var wakeOnTapOrMotion: Bool = false + var unknownFields = SwiftProtobuf.UnknownStorage() /// @@ -1970,6 +1974,7 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp 7: .same(proto: "oled"), 8: .same(proto: "displaymode"), 9: .standard(proto: "heading_bold"), + 10: .standard(proto: "wake_on_tap_or_motion"), ] mutating func decodeMessage(decoder: inout D) throws { @@ -1987,6 +1992,7 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp case 7: try { try decoder.decodeSingularEnumField(value: &self.oled) }() case 8: try { try decoder.decodeSingularEnumField(value: &self.displaymode) }() case 9: try { try decoder.decodeSingularBoolField(value: &self.headingBold) }() + case 10: try { try decoder.decodeSingularBoolField(value: &self.wakeOnTapOrMotion) }() default: break } } @@ -2020,6 +2026,9 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp if self.headingBold != false { try visitor.visitSingularBoolField(value: self.headingBold, fieldNumber: 9) } + if self.wakeOnTapOrMotion != false { + try visitor.visitSingularBoolField(value: self.wakeOnTapOrMotion, fieldNumber: 10) + } try unknownFields.traverse(visitor: &visitor) } @@ -2033,6 +2042,7 @@ extension Config.DisplayConfig: SwiftProtobuf.Message, SwiftProtobuf._MessageImp if lhs.oled != rhs.oled {return false} if lhs.displaymode != rhs.displaymode {return false} if lhs.headingBold != rhs.headingBold {return false} + if lhs.wakeOnTapOrMotion != rhs.wakeOnTapOrMotion {return false} if lhs.unknownFields != rhs.unknownFields {return false} return true } From 147721dfdf5d02fdc5b1cab0c5e606523b9a3599 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 22 Mar 2023 09:58:46 -0700 Subject: [PATCH 8/9] Fix battery level bug on live activity Restrict node history to the current day --- Meshtastic/Views/Bluetooth/Connect.swift | 5 +++-- Meshtastic/Views/Nodes/NodeDetail.swift | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 32b25c29..58711e4e 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -295,8 +295,9 @@ struct Connect: View { if #available(iOS 16.2, *) { liveActivityStarted = true let timerSeconds = 60 - - let mostRecent = node?.telemetries?.lastObject as? TelemetryEntity + + let deviceMetrics = node?.telemetries?.filtered(using: NSPredicate(format: "metricsType == 0")) + let mostRecent = deviceMetrics?.lastObject as? TelemetryEntity let activityAttributes = MeshActivityAttributes(nodeNum: Int(node?.num ?? 0), name: node?.user?.longName ?? "unknown") diff --git a/Meshtastic/Views/Nodes/NodeDetail.swift b/Meshtastic/Views/Nodes/NodeDetail.swift index e69913a2..38544cde 100644 --- a/Meshtastic/Views/Nodes/NodeDetail.swift +++ b/Meshtastic/Views/Nodes/NodeDetail.swift @@ -59,7 +59,8 @@ struct NodeDetail: View { VStack { if node.positions?.count ?? 0 > 0 { ZStack { - let annotations = node.positions?.array as? [PositionEntity] ?? [] + let positionArray = node.positions?.array as? [PositionEntity] ?? [] + let todaysPositions = positionArray.filter { $0.time! >= Calendar.current.startOfDay(for: Date()) } ZStack { MapViewSwiftUI(onLongPress: { coord in waypointCoordinate = coord @@ -70,7 +71,7 @@ struct NodeDetail: View { editingWaypoint = wpId presentingWaypointForm = true } - }, positions: annotations, waypoints: Array(waypoints), + }, positions: todaysPositions, waypoints: Array(waypoints), mapViewType: mapType, userTrackingMode: MKUserTrackingMode.none, showNodeHistory: meshMapShowNodeHistory, From 0a0f7f01cbe4cefe3e968b061e0f5bf5f8202b97 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 22 Mar 2023 10:16:20 -0700 Subject: [PATCH 9/9] Use whatever channel the node info comes in on --- Meshtastic/Helpers/MeshPackets.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Meshtastic/Helpers/MeshPackets.swift b/Meshtastic/Helpers/MeshPackets.swift index daa6744f..d329dceb 100644 --- a/Meshtastic/Helpers/MeshPackets.swift +++ b/Meshtastic/Helpers/MeshPackets.swift @@ -251,7 +251,7 @@ func nodeInfoPacket (nodeInfo: NodeInfo, channel: UInt32, context: NSManagedObje let newNode = NodeInfoEntity(context: context) newNode.id = Int64(nodeInfo.num) newNode.num = Int64(nodeInfo.num) - newNode.channel = 0//Int32(channel) + newNode.channel = Int32(channel) if nodeInfo.hasDeviceMetrics { let telemetry = TelemetryEntity(context: context)