Merge pull request #339 from meshtastic/2.1.3_Working_Changes

2.1.3 working changes
This commit is contained in:
Garth Vander Houwen 2023-03-23 12:30:32 -07:00 committed by GitHub
commit bba25666b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 130 additions and 129 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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<D: SwiftProtobuf.Decoder>(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
}

View file

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

View file

@ -20,21 +20,19 @@ 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
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
@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
func makeUIView(context: Context) -> MKMapView {
// Map View Parameters
mapView.mapType = mapViewType
@ -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
@ -123,46 +107,59 @@ 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()) }
}
}
DispatchQueue.main.async {
let latest = positions
.filter { $0.latest == true }
.sorted { $0.nodePosition?.num ?? 0 > $1.nodePosition?.num ?? -1 }
if showRouteLines {
// Remove all existing PolyLine Overlays
for overlay in mapView.overlays {
if overlay is MKPolyline {
mapView.removeOverlay(overlay)
}
}
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 10
if lineIndex > 9 {
lineIndex = 0
}
}
}
}
DispatchQueue.main.async {
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)
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
}
}
@ -177,7 +174,6 @@ struct MapViewSwiftUI: UIViewRepresentable {
var parent: MapViewSwiftUI
var longPressRecognizer = UILongPressGestureRecognizer()
var overlays: [Overlay] = []
init(_ parent: MapViewSwiftUI) {
self.parent = parent
@ -187,7 +183,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? {
@ -356,8 +351,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
}
@ -464,30 +461,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
// }
// }
}

View file

@ -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
@ -24,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",
@ -47,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"
@ -57,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
@ -68,15 +71,12 @@ struct NodeDetail: View {
editingWaypoint = wpId
presentingWaypointForm = true
}
}, positions: annotations, waypoints: Array(waypoints),
}, positions: todaysPositions, waypoints: Array(waypoints),
mapViewType: mapType,
userTrackingMode: MKUserTrackingMode.none,
centeringMode: .allPositions,
showRouteLines: false,
showNodeHistory: true,
centerOnPositionsOnly: true,
customMapOverlay: self.customMapOverlay,
overlays: self.overlays
showNodeHistory: meshMapShowNodeHistory,
showRouteLines: meshMapShowRouteLines,
customMapOverlay: self.customMapOverlay
)
VStack(alignment: .leading) {
Spacer()
@ -177,14 +177,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 +288,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))

View file

@ -31,9 +31,9 @@ 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
@AppStorage("meshMapShowRouteLines") private var meshMapShowRouteLines = 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<PositionEntity>
@ -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
@ -55,11 +54,6 @@ struct NodeMap: View {
tileType: "png",
canReplaceMapContent: true
)
@State private var overlays: [MapViewSwiftUI.Overlay] = []
// init() {
// _positions = FetchRequest<PositionEntity>(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 {
@ -83,12 +77,9 @@ struct NodeMap: View {
waypoints: Array(waypoints),
mapViewType: mapType,
userTrackingMode: userTrackingMode,
centeringMode: mapCenteringMode,
showRouteLines: false,
showNodeHistory: true,
centerOnPositionsOnly: false,
customMapOverlay: self.customMapOverlay,
overlays: self.overlays
showNodeHistory: meshMapShowNodeHistory,
showRouteLines: meshMapShowRouteLines,
customMapOverlay: self.customMapOverlay
)
VStack {
Spacer()
@ -124,7 +115,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":

View file

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

View file

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