From 5790839022f601f211bc020011525f3d56cc1790 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Wed, 12 Oct 2022 15:26:25 -0700 Subject: [PATCH] Start of connection timer UI --- Meshtastic/Helpers/BLEManager.swift | 32 ++++++----- Meshtastic/MeshtasticApp.swift | 13 ++--- Meshtastic/Views/Bluetooth/Connect.swift | 54 +++++++++++++------ Meshtastic/Views/Messages/Contacts.swift | 11 ++-- Meshtastic/Views/Settings/ShareChannels.swift | 30 ++++++----- 5 files changed, 85 insertions(+), 55 deletions(-) diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index 76b43972..23d65fb5 100644 --- a/Meshtastic/Helpers/BLEManager.swift +++ b/Meshtastic/Helpers/BLEManager.swift @@ -36,6 +36,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph @Published var isSwitchedOn: Bool = false @Published var isScanning: Bool = false + @Published var isConnecting: Bool = false @Published var isConnected: Bool = false /// Used to make sure we never get foold by old BLE packets @@ -44,7 +45,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph var timeoutTimer: Timer? var timeoutTimerCount = 0 var positionTimer: Timer? - let broadcastNodeNum: UInt32 = 4294967295 /* Meshtastic Service Details */ @@ -133,6 +133,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph let name: String = timerContext["name", default: "Unknown"] self.timeoutTimerCount += 1 + self.isConnecting = true + self.lastConnectionError = "" if timeoutTimerCount == 10 { @@ -141,17 +143,20 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph self.centralManager?.cancelPeripheralConnection(connectedPeripheral.peripheral) } connectedPeripheral = nil - self.isConnected = false - - self.lastConnectionError = "🚨 BLE Connection Timeout after making \(timeoutTimerCount) attempts to connect to \(name)." - - if meshLoggingEnabled { MeshLogger.log(self.lastConnectionError + " This can occur when a device has been taken out of BLE range, or if a device is already connected to another phone, tablet or computer.") } - - self.timeoutTimerCount = 0 if self.timeoutTimer != nil { self.timeoutTimer!.invalidate() } + self.isConnected = false + self.isConnecting = false + + self.lastConnectionError = "🚨 BLE Connection Timeout after making \(timeoutTimerCount) attempts to connect to \(name). If you continue to get this message you may need to forget your device under settings > bluetooth." + + if meshLoggingEnabled { MeshLogger.log("🚨 BLE Connection Timeout after making \(timeoutTimerCount) attempts to connect to \(name). If you continue to get this message you may need to forget your device under settings > bluetooth.") } + + self.timeoutTimerCount = 0 + self.startScanning() + } else { @@ -165,6 +170,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph if meshLoggingEnabled { MeshLogger.log("✅ BLE Connecting: \(peripheral.name ?? "Unknown")") } stopScanning() + self.isConnecting = true + self.lastConnectionError = "" if self.connectedPeripheral != nil { @@ -184,7 +191,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Use a timer to keep track of connecting peripherals, context to pass the radio name with the timer and the RunLoop to prevent // the timer from running on the main UI thread let context = ["name": "@\(peripheral.name ?? "Unknown")"] - self.timeoutTimer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(timeoutTimerFired), userInfo: context, repeats: true) + self.timeoutTimer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(timeoutTimerFired), userInfo: context, repeats: true) RunLoop.current.add(self.timeoutTimer!, forMode: .common) } @@ -235,6 +242,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Called when a peripheral is connected func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { + self.isConnecting = false self.isConnected = true if userSettings?.preferredPeripheralId.count ?? 0 < 1 { @@ -287,6 +295,7 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph // Start a scan so the disconnected peripheral is moved to the peripherals[] if it is awake self.startScanning() self.connectedPeripheral = nil + self.isConnecting = false if let e = error { @@ -378,9 +387,6 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph for characteristic in characteristics { switch characteristic.uuid { - case EOL_FROMRADIO_UUID: - if meshLoggingEnabled { MeshLogger.log("🚨 BLE did discover EOL_TORADIO characteristic for Meshtastic by \(peripheral.name ?? "Unknown")") } - invalidVersion = true case TORADIO_UUID: @@ -474,6 +480,8 @@ class BLEManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeriph guard (connectedPeripheral!.peripheral.state == CBPeripheralState.connected) else { return } if FROMRADIO_characteristic == nil { + + if meshLoggingEnabled { MeshLogger.log("🚨 Unsupported Firmware Version Detected, unable to connect to device.") } invalidVersion = true return diff --git a/Meshtastic/MeshtasticApp.swift b/Meshtastic/MeshtasticApp.swift index 7f7f787d..96d1f899 100644 --- a/Meshtastic/MeshtasticApp.swift +++ b/Meshtastic/MeshtasticApp.swift @@ -23,6 +23,12 @@ struct MeshtasticAppleApp: App { .environment(\.managedObjectContext, persistenceController.container.viewContext) .environmentObject(bleManager) .environmentObject(userSettings) + .sheet(isPresented: $saveChannels) { + + SaveChannelQRCode(channelHash: channelSettings ?? "Empty Channel URL", validUrl: true) + .presentationDetents([.medium, .large]) + .presentationDragIndicator(.visible) + } .onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in @@ -41,12 +47,7 @@ struct MeshtasticAppleApp: App { print("User wants to open Channel Settings URL: \(String(describing: incomingUrl!.relativeString))") } } - .sheet(isPresented: $saveChannels) { - - SaveChannelQRCode(channelHash: channelSettings ?? "Empty Channel URL", validUrl: true) - .presentationDetents([.medium, .large]) - .presentationDragIndicator(.visible) - } + .onOpenURL(perform: { (url) in print("Some sort of URL was received \(url)") diff --git a/Meshtastic/Views/Bluetooth/Connect.swift b/Meshtastic/Views/Bluetooth/Connect.swift index 673415e0..575fad17 100644 --- a/Meshtastic/Views/Bluetooth/Connect.swift +++ b/Meshtastic/Views/Bluetooth/Connect.swift @@ -35,13 +35,13 @@ struct Connect: View { Section(header: Text("Connection Error").font(.title)) { - Text(bleManager.lastConnectionError).font(.title3).foregroundColor(.red) + Text(bleManager.lastConnectionError).font(.callout).foregroundColor(.red) } .textCase(nil) } - + Section(header: Text("Connected Radio").font(.title)) { - + if bleManager.connectedPeripheral != nil && bleManager.connectedPeripheral.peripheral.state == .connected { HStack { @@ -65,10 +65,10 @@ struct Connect: View { .font(.caption).foregroundColor(Color.gray) } if bleManager.connectedPeripheral.subscribed { - Text("Properly Subscribed").font(.caption) + Text("Subscribed to mesh").font(.caption) .foregroundColor(.green) } else { - Text("Attempting to connect. . . ").font(.caption) + Text("Communicating with device. . . ").font(.caption) .foregroundColor(.orange) } @@ -135,20 +135,44 @@ struct Connect: View { } } else { - HStack { - Image(systemName: "antenna.radiowaves.left.and.right.slash") - .symbolRenderingMode(.hierarchical) - .imageScale(.large).foregroundColor(.red) - .padding(.trailing) - Text("No device connected").font(.title3) - } - .padding() - } + + if bleManager.isConnecting { + HStack { + Image(systemName: "antenna.radiowaves.left.and.right") + .symbolRenderingMode(.hierarchical) + .imageScale(.large).foregroundColor(.orange) + .padding(.trailing) + if bleManager.timeoutTimerCount == 0 { + Text("Connecting . . .") + .font(.title3) + .foregroundColor(.orange) + } else { + VStack { + Text("Connection Attempt \(bleManager.timeoutTimerCount) of 10") + .font(.callout) + .foregroundColor(.orange) + } + } + } + .padding() + + } else { + + HStack { + Image(systemName: "antenna.radiowaves.left.and.right.slash") + .symbolRenderingMode(.hierarchical) + .imageScale(.large).foregroundColor(.red) + .padding(.trailing) + Text("No device connected").font(.title3) + } + .padding() + } + } } .textCase(nil) - if bleManager.peripherals.count > 0 { + if self.bleManager.isScanning { Section(header: Text("Available Radios").font(.title)) { ForEach(bleManager.peripherals.filter({ $0.peripheral.state == CBPeripheralState.disconnected }).sorted(by: { $0.rssi > $1.rssi })) { peripheral in HStack { diff --git a/Meshtastic/Views/Messages/Contacts.swift b/Meshtastic/Views/Messages/Contacts.swift index 325ec948..c65a5254 100644 --- a/Meshtastic/Views/Messages/Contacts.swift +++ b/Meshtastic/Views/Messages/Contacts.swift @@ -28,8 +28,8 @@ struct Contacts: View { // Display Contacts for DM's on the Primary Channel // Display Contacts for the rest of the non admin channels - - List(users) { (user: UserEntity) in + List { + ForEach(users) { (user: UserEntity) in let connectedNodeNum = bleManager.connectedPeripheral != nil ? bleManager.connectedPeripheral.num : 0 @@ -131,6 +131,7 @@ struct Contacts: View { } } } + } .navigationTitle("Contacts") .navigationBarTitleDisplayMode(.inline) .navigationBarItems(leading: @@ -140,9 +141,3 @@ struct Contacts: View { .listStyle(PlainListStyle()) } } - -struct Contacts_Previews: PreviewProvider { - static var previews: some View { - Contacts() - } -} diff --git a/Meshtastic/Views/Settings/ShareChannels.swift b/Meshtastic/Views/Settings/ShareChannels.swift index cbc3a052..52c16ccc 100644 --- a/Meshtastic/Views/Settings/ShareChannels.swift +++ b/Meshtastic/Views/Settings/ShareChannels.swift @@ -77,7 +77,7 @@ struct ShareChannels: View { .font(.caption) .fontWeight(.bold) .padding(.trailing) - Text("Channel Name") + Text("Channel") .font(.caption) .fontWeight(.bold) .padding(.trailing) @@ -96,57 +96,59 @@ struct ShareChannels: View { .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 1) - Text((channel.name!.isEmpty ? "primary" : channel.name) ?? "primary") + Text((channel.name!.isEmpty ? "Primary" : channel.name) ?? "Primary") } else if channel.index == 1 { Toggle("Channel 1 Included", isOn: $includeChannel1) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") } else if channel.index == 2 { Toggle("Channel 2 Included", isOn: $includeChannel2) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") } else if channel.index == 3 { Toggle("Channel 3 Included", isOn: $includeChannel3) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") } else if channel.index == 4 { Toggle("Channel 4 Included", isOn: $includeChannel4) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") } else if channel.index == 5 { Toggle("Channel 5 Included", isOn: $includeChannel5) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") } else if channel.index == 6 { Toggle("Channel 6 Included", isOn: $includeChannel6) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") } else if channel.index == 7 { Toggle("Channel 7 Included", isOn: $includeChannel7) .toggleStyle(.switch) .labelsHidden() .disabled(channel.role == 0) - Text((channel.name!.isEmpty ? "channel \(channel.index)" : channel.name) ?? "Channel \(channel.index)") + Text((channel.name!.isEmpty ? "Channel\(channel.index)" : channel.name) ?? "Channel\(channel.index)") } - if channel.role > 0 { - Image(systemName: "lock.fill") - .foregroundColor(.green) - } else { + + if channel.role == 0 { Image(systemName: "lock.slash") .foregroundColor(.gray) } + else if channel.role > 0 { + Image(systemName: "lock.fill") + .foregroundColor(.green) + } Spacer() } } @@ -285,7 +287,7 @@ struct ShareChannels: View { var channelSettings = ChannelSettings() channelSettings.name = ch.name! - channelSettings.psk = ch.psk ?? Data() + channelSettings.psk = ch.psk! channelSettings.id = UInt32(ch.id) channelSettings.uplinkEnabled = ch.uplinkEnabled channelSettings.downlinkEnabled = ch.downlinkEnabled