From b214b34d64fb48500cdb8466a568f5dceedc2223 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Fri, 24 Feb 2023 21:14:08 -0800 Subject: [PATCH] Add setting for user tracking mode --- Meshtastic/Enums/AppSettingsEnums.swift | 39 ++++++++- Meshtastic/Enums/DeviceRoles.swift | 7 ++ Meshtastic/Helpers/BLEManager.swift | 4 +- Meshtastic/Model/UserSettings.swift | 9 +- .../Views/Map/Custom/MapViewSwiftUI.swift | 86 ++++++++++--------- Meshtastic/Views/Settings/AppSettings.swift | 9 ++ de.lproj/Localizable.strings | 4 + en.lproj/Localizable.strings | 6 +- 8 files changed, 117 insertions(+), 47 deletions(-) diff --git a/Meshtastic/Enums/AppSettingsEnums.swift b/Meshtastic/Enums/AppSettingsEnums.swift index 7d17601d..d7001a62 100644 --- a/Meshtastic/Enums/AppSettingsEnums.swift +++ b/Meshtastic/Enums/AppSettingsEnums.swift @@ -39,7 +39,7 @@ enum CenteringMode: Int, CaseIterable, Identifiable { case allAnnotations = 0 case allPositions = 1 - case clientGps = 2 + case phoneGps = 2 var id: Int { self.rawValue } var description: String { @@ -49,8 +49,8 @@ enum CenteringMode: Int, CaseIterable, Identifiable { return "All Annotations"// NSLocalizedString("default", comment: "Default Keyboard") case .allPositions: return "All Node Postions"// NSLocalizedString("ascii.capable", comment: "ASCII Capable Keyboard") - case .clientGps: - return "Client GPS"//NSLocalizedString("email.address", comment: "Email Address Keyboard") + case .phoneGps: + return "Phone GPS"//NSLocalizedString("email.address", comment: "Email Address Keyboard") } } } @@ -105,6 +105,39 @@ enum MeshMapType: String, CaseIterable, Identifiable { } } +enum UserTrackingModes: Int, CaseIterable, Identifiable { + + case none = 0 + case follow = 1 + case followWithHeading = 2 + + var id: Int { self.rawValue } + + var description: String { + get { + switch self { + case .none: + return NSLocalizedString("map.usertrackingmode.none", comment: "None") + case .follow: + return NSLocalizedString("map.usertrackingmode.follow", comment: "Follow") + case .followWithHeading: + return NSLocalizedString("map.usertrackingmode.followwithheading", comment: "Follow with Heading") + } + } + } + func MKUserTrackingModeValue() -> MKUserTrackingMode { + + switch self { + case .none: + return MKUserTrackingMode.none + case .follow: + return MKUserTrackingMode.follow + case .followWithHeading: + return MKUserTrackingMode.followWithHeading + } + } +} + enum LocationUpdateInterval: Int, CaseIterable, Identifiable { case fiveSeconds = 5 diff --git a/Meshtastic/Enums/DeviceRoles.swift b/Meshtastic/Enums/DeviceRoles.swift index f5652f0d..9691a52d 100644 --- a/Meshtastic/Enums/DeviceRoles.swift +++ b/Meshtastic/Enums/DeviceRoles.swift @@ -16,6 +16,7 @@ enum DeviceRoles: Int, CaseIterable, Identifiable { case routerClient = 3 case repeater = 4 case tracker = 5 + case sensor = 6 var id: Int { self.rawValue } var name: String { @@ -34,6 +35,8 @@ enum DeviceRoles: Int, CaseIterable, Identifiable { return "Repeater" case .tracker: return "Tracker" + case .sensor: + return "Sensor" } } } @@ -53,6 +56,8 @@ enum DeviceRoles: Int, CaseIterable, Identifiable { return NSLocalizedString("device.role.repeater", comment: "Repeater - Mesh packets will prefer to be routed over this node. This role eliminates unnecessary overhead such as NodeInfo, DeviceTelemetry, and any other mesh packet, resulting in the device not appearing as part of the network. Please see Rebroadcast Mode for additional settings specific to this role.") case .tracker: return NSLocalizedString("device.role.tracker", comment: "Tracker - For use with devices intended as a GPS tracker. Position packets sent from this device will be higher priority, with position broadcasting every two minutes. Smart Position Broadcast will default to off.") + case .sensor: + return NSLocalizedString("device.role.sensor", comment: "Sensor - For use with remote telemetry sensors. Setting this role will turn on environment telemetry. Telemetry packets sent from this device will be higher priority, with telemetry broadcasting every 7 minutes") } } } @@ -72,6 +77,8 @@ enum DeviceRoles: Int, CaseIterable, Identifiable { return Config.DeviceConfig.Role.repeater case .tracker: return Config.DeviceConfig.Role.tracker + case .sensor: + return Config.DeviceConfig.Role.sensor } } } diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 3e03a89f..ed28b2a5 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -62,8 +62,8 @@ class BLEManager: NSObject, CBPeripheralDelegate, ObservableObject { self.lastConnectionError = "" self.connectedVersion = "0.0.0" super.init() - //centralManager = CBCentralManager(delegate: self, queue: nil) - centralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionRestoreIdentifierKey: restoreKey]) + centralManager = CBCentralManager(delegate: self, queue: nil) + //centralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionRestoreIdentifierKey: restoreKey]) } // MARK: Scanning for BLE Devices diff --git a/Meshtastic/Model/UserSettings.swift b/Meshtastic/Model/UserSettings.swift index 675d238c..a8f56efe 100644 --- a/Meshtastic/Model/UserSettings.swift +++ b/Meshtastic/Model/UserSettings.swift @@ -62,7 +62,13 @@ class UserSettings: ObservableObject { UserDefaults.standard.set(meshMapCustomTileServer, forKey: "meshMapCustomTileServer") } } - + @Published var meshMapUserTrackingMode: Int { + didSet { + UserDefaults.standard.set(meshMapUserTrackingMode, forKey: "meshMapUserTrackingMode") + UserDefaults.standard.synchronize() + } + } + init() { self.meshtasticUsername = UserDefaults.standard.object(forKey: "meshtasticusername") as? String ?? "" @@ -75,5 +81,6 @@ class UserSettings: ObservableObject { self.meshMapCenteringMode = UserDefaults.standard.object(forKey: "meshMapCenteringMode") as? Int ?? 0 self.meshMapRecentering = UserDefaults.standard.object(forKey: "meshMapRecentering") as? Bool ?? true self.meshMapCustomTileServer = UserDefaults.standard.string(forKey: "meshMapCustomTileServer") ?? "" + self.meshMapUserTrackingMode = UserDefaults.standard.object(forKey: "meshMapUserTrackingMode") as? Int ?? 0 } } diff --git a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift index 6c1060e5..e2931d04 100644 --- a/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift +++ b/Meshtastic/Views/Map/Custom/MapViewSwiftUI.swift @@ -23,6 +23,7 @@ struct MapViewSwiftUI: UIViewRepresentable { let centerOnPositionsOnly: Bool @AppStorage("meshMapRecentering") private var recenter = false + @AppStorage("meshMapUserTrackingMode") private var userTrackingModeId = 0 // Offline Maps //make this view dependent on the UserDefault that is updated when importing a new map file @@ -39,18 +40,18 @@ struct MapViewSwiftUI: UIViewRepresentable { mapView.addAnnotations(waypoints) // Logic to manage the map centering options switch centeringMode { - case .allAnnotations: - mapView.addAnnotations(positions) - mapView.fitAllAnnotations() - case .allPositions: - mapView.fit(annotations: positions, andShow: true) - case .clientGps: - - let span = MKCoordinateSpan(latitudeDelta: 0.003, longitudeDelta: 0.003) - let center = CLLocationCoordinate2D(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude) - let region = MKCoordinateRegion(center: center, span: span) - mapView.setRegion(region, animated: true) - mapView.addAnnotations(positions) + case .allAnnotations: + mapView.addAnnotations(positions) + mapView.fitAllAnnotations() + case .allPositions: + mapView.fit(annotations: positions, andShow: true) + case .phoneGps: + + let span = MKCoordinateSpan(latitudeDelta: 0.003, longitudeDelta: 0.003) + let center = CLLocationCoordinate2D(latitude: LocationHelper.currentLocation.latitude, longitude: LocationHelper.currentLocation.longitude) + let region = MKCoordinateRegion(center: center, span: span) + mapView.setRegion(region, animated: true) + mapView.addAnnotations(positions) } // Other MKMapView Settings @@ -116,34 +117,36 @@ struct MapViewSwiftUI: UIViewRepresentable { DispatchQueue.main.async { mapView.removeAnnotations(mapView.annotations) mapView.addAnnotations(waypoints) - switch centeringMode { - case .allAnnotations: - mapView.addAnnotations(positions) - if recenter { - mapView.fitAllAnnotations() - } - case .allPositions: - if recenter { - mapView.fit(annotations: positions, andShow: true) - } else { - mapView.addAnnotations(positions) - } - case .clientGps: - mapView.addAnnotations(positions) - mapView.showsUserLocation = true - mapView.setUserTrackingMode(.followWithHeading, animated: true) - if recenter { - // create a 3D Camera - let mapCamera = MKMapCamera() - mapCamera.centerCoordinate = LocationHelper.currentLocation - mapCamera.pitch = 45 - mapCamera.altitude = 500 // example altitude - mapCamera.heading = 45 + mapView.setUserTrackingMode(UserTrackingModes(rawValue: userTrackingModeId )?.MKUserTrackingModeValue() ?? MKUserTrackingMode.none, animated: true) - // set the camera property - mapView.camera = mapCamera - mapView.centerCoordinate = LocationHelper.currentLocation - } + switch centeringMode { + case .allAnnotations: + mapView.addAnnotations(positions) + if recenter { + mapView.fitAllAnnotations() + } + case .allPositions: + if recenter { + mapView.fit(annotations: positions, andShow: true) + } else { + mapView.addAnnotations(positions) + } + case .phoneGps: + mapView.addAnnotations(positions) + mapView.showsUserLocation = true + + if recenter { + // create a 3D Camera +// let mapCamera = MKMapCamera() +// mapCamera.centerCoordinate = LocationHelper.currentLocation +// mapCamera.pitch = 45 +// mapCamera.altitude = 500 // example altitude +// mapCamera.heading = 45 +// // set the camera property +// mapView.camera = mapCamera + + mapView.centerCoordinate = LocationHelper.currentLocation + } } } } @@ -176,7 +179,7 @@ struct MapViewSwiftUI: UIViewRepresentable { case _ as MKClusterAnnotation: let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "nodeGroup") as? MKMarkerAnnotationView ?? MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "WaypointGroup") - annotationView.markerTintColor = .brown//.systemRed + annotationView.markerTintColor = .brown annotationView.displayPriority = .defaultLow annotationView.tag = -1 return annotationView @@ -220,6 +223,9 @@ struct MapViewSwiftUI: UIViewRepresentable { annotationView.glyphImage = UIImage(systemName: "wifi.router.fill") } else if DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.tracker { annotationView.glyphImage = UIImage(systemName: "location.viewfinder") + } else if DeviceRoles(rawValue: Int(positionAnnotation.nodePosition!.metadata?.role ?? 0)) == DeviceRoles.sensor { + annotationView.glyphImage = UIImage(systemName: "sensor") + } let pf = PositionFlags(rawValue: Int(positionAnnotation.nodePosition?.metadata?.positionFlags ?? 3)) diff --git a/Meshtastic/Views/Settings/AppSettings.swift b/Meshtastic/Views/Settings/AppSettings.swift index 4605eda1..92df911f 100644 --- a/Meshtastic/Views/Settings/AppSettings.swift +++ b/Meshtastic/Views/Settings/AppSettings.swift @@ -58,6 +58,15 @@ struct AppSettings: View { .font(.caption) .listRowSeparator(.visible) } + Picker("map.usertrackingmode", selection: $userSettings.meshMapUserTrackingMode) { + ForEach(UserTrackingModes.allCases) { utm in + Text(utm.description) + } + } + .pickerStyle(DefaultPickerStyle()) + Text("map.usertrackingmode.description") + .font(.caption) + .listRowSeparator(.visible) } Section(header: Text("map options")) { diff --git a/de.lproj/Localizable.strings b/de.lproj/Localizable.strings index f6785b90..c535db76 100644 --- a/de.lproj/Localizable.strings +++ b/de.lproj/Localizable.strings @@ -140,6 +140,10 @@ "map.centering"="Centering"; "map.recentering"="Automatic Re-centering"; "map.type"="kartentyp"; +"map.usertrackingmode"="User tracking mode"; +"map.usertrackingmode.none"="None"; +"map.usertrackingmode.follow"="Follow"; +"map.usertrackingmode.followwithheading"="Follow with heading"; "mesh.log"="Mesh Log"; "mesh.log.bluetooth.config %@"="Bluetooth Konfiguration empfangen: %@"; "mesh.log.cannedmessage.config %@"="Canned Message module config received: %@"; diff --git a/en.lproj/Localizable.strings b/en.lproj/Localizable.strings index 36951b44..1da45fb6 100644 --- a/en.lproj/Localizable.strings +++ b/en.lproj/Localizable.strings @@ -130,7 +130,7 @@ "interval.eighteen.hours"="Eighteen Hours"; "interval.twentyfour.hours"="Twenty Four Hours"; "interval.thirtysix.hours"="Thirty Six Hours"; -"interval.fortyeight.hours"="Forty Eight Hours Hours"; +"interval.fortyeight.hours"="Forty Eight Hours"; "interval.seventytwo.hours"="Seventy Two Hours"; "keyboard.type"="Keyboard Type"; "logging"="Logging"; @@ -140,6 +140,10 @@ "map.type"="Default Type"; "map.centering"="Centering Mode"; "map.recentering"="Automatic Re-centering"; +"map.usertrackingmode"="User tracking mode"; +"map.usertrackingmode.none"="None"; +"map.usertrackingmode.follow"="Follow"; +"map.usertrackingmode.followwithheading"="Follow with heading"; "mesh.log"="Mesh Log"; "mesh.log.bluetooth.config %@"="Bluetooth config received: %@"; "mesh.log.cannedmessage.config %@"="Canned Message module config received: %@";