Try and re-connect BLE devices in the background better

This commit is contained in:
Garth Vander Houwen 2023-02-25 08:39:10 -08:00
parent b214b34d64
commit 9c4deecc36
9 changed files with 228 additions and 183 deletions

View file

@ -38,12 +38,13 @@
DD4C0EFA29A8735400D3316C /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD4C0EF929A8735400D3316C /* WidgetKit.framework */; };
DD4C0EFC29A8735400D3316C /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD4C0EFB29A8735400D3316C /* SwiftUI.framework */; };
DD4C0EFF29A8735400D3316C /* MeshtasticWidgetsBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4C0EFE29A8735400D3316C /* MeshtasticWidgetsBundle.swift */; };
DD4C0F0129A8735400D3316C /* MeshtasticWidgetsLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4C0F0029A8735400D3316C /* MeshtasticWidgetsLiveActivity.swift */; };
DD4C0F0329A8735400D3316C /* MeshtasticWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4C0F0229A8735400D3316C /* MeshtasticWidgets.swift */; };
DD4C0F0629A8735500D3316C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DD4C0F0529A8735500D3316C /* Assets.xcassets */; };
DD4C0F0829A8735500D3316C /* MeshtasticWidgets.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = DD4C0F0429A8735400D3316C /* MeshtasticWidgets.intentdefinition */; };
DD4C0F0929A8735500D3316C /* MeshtasticWidgets.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = DD4C0F0429A8735400D3316C /* MeshtasticWidgets.intentdefinition */; };
DD4C0F0C29A8735500D3316C /* MeshtasticWidgetsExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = DD4C0EF829A8735400D3316C /* MeshtasticWidgetsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
DD4C0F1329A9DDF800D3316C /* MeshActivityAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4C0F1229A9DDF800D3316C /* MeshActivityAttributes.swift */; };
DD4C0F1629A9EB5000D3316C /* MeshActivityWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4C0F1429A9E21A00D3316C /* MeshActivityWidget.swift */; };
DD4C0F1729A9EB6300D3316C /* MeshActivityWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4C0F1429A9E21A00D3316C /* MeshActivityWidget.swift */; };
DD4F23CD28779A3C001D37CB /* EnvironmentMetricsLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD4F23CC28779A3C001D37CB /* EnvironmentMetricsLog.swift */; };
DD5394FC276993AD00AD86B1 /* SwiftProtobuf in Frameworks */ = {isa = PBXBuildFile; productRef = DD5394FB276993AD00AD86B1 /* SwiftProtobuf */; };
DD5394FE276BA0EF00AD86B1 /* PositionEntityExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */; };
@ -199,12 +200,12 @@
DD4C0EF929A8735400D3316C /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
DD4C0EFB29A8735400D3316C /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
DD4C0EFE29A8735400D3316C /* MeshtasticWidgetsBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticWidgetsBundle.swift; sourceTree = "<group>"; };
DD4C0F0029A8735400D3316C /* MeshtasticWidgetsLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticWidgetsLiveActivity.swift; sourceTree = "<group>"; };
DD4C0F0229A8735400D3316C /* MeshtasticWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticWidgets.swift; sourceTree = "<group>"; };
DD4C0F0429A8735400D3316C /* MeshtasticWidgets.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = MeshtasticWidgets.intentdefinition; sourceTree = "<group>"; };
DD4C0F0529A8735500D3316C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
DD4C0F0729A8735500D3316C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DD4C0F0D29A8735500D3316C /* MeshtasticWidgetsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MeshtasticWidgetsExtension.entitlements; sourceTree = "<group>"; };
DD4C0F1229A9DDF800D3316C /* MeshActivityAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshActivityAttributes.swift; sourceTree = "<group>"; };
DD4C0F1429A9E21A00D3316C /* MeshActivityWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshActivityWidget.swift; sourceTree = "<group>"; };
DD4F23CC28779A3C001D37CB /* EnvironmentMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentMetricsLog.swift; sourceTree = "<group>"; };
DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionEntityExtension.swift; sourceTree = "<group>"; };
DD58C5F12919AD3C00D5BEFB /* ChannelEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelEntityExtension.swift; sourceTree = "<group>"; };
@ -401,11 +402,11 @@
isa = PBXGroup;
children = (
DD4C0EFE29A8735400D3316C /* MeshtasticWidgetsBundle.swift */,
DD4C0F0029A8735400D3316C /* MeshtasticWidgetsLiveActivity.swift */,
DD4C0F0229A8735400D3316C /* MeshtasticWidgets.swift */,
DD4C0F0429A8735400D3316C /* MeshtasticWidgets.intentdefinition */,
DD4C0F0529A8735500D3316C /* Assets.xcassets */,
DD4C0F0729A8735500D3316C /* Info.plist */,
DD4C0F1229A9DDF800D3316C /* MeshActivityAttributes.swift */,
DD4C0F1429A9E21A00D3316C /* MeshActivityWidget.swift */,
);
path = MeshtasticWidgets;
sourceTree = "<group>";
@ -870,9 +871,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DD4C0F1629A9EB5000D3316C /* MeshActivityWidget.swift in Sources */,
DD4C0EFF29A8735400D3316C /* MeshtasticWidgetsBundle.swift in Sources */,
DD4C0F0129A8735400D3316C /* MeshtasticWidgetsLiveActivity.swift in Sources */,
DD4C0F0329A8735400D3316C /* MeshtasticWidgets.swift in Sources */,
DD4C0F0829A8735500D3316C /* MeshtasticWidgets.intentdefinition in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -946,6 +946,7 @@
DDB6ABE028B13AC700384BA1 /* DeviceRoles.swift in Sources */,
DD86D40C287F401000BAEB7A /* SaveChannelQRCode.swift in Sources */,
DDA1C48E28DB49D3009933EC /* ChannelRoles.swift in Sources */,
DD4C0F1729A9EB6300D3316C /* MeshActivityWidget.swift in Sources */,
DD8ED9C8289CE4B900B3B0AB /* RoutingError.swift in Sources */,
DD5E5202298EE33B00D21B61 /* admin.pb.swift in Sources */,
DD964FC62975DBFD007C176F /* QueryCoreData.swift in Sources */,
@ -953,6 +954,7 @@
DD8ED9C52898D51F00B3B0AB /* NetworkConfig.swift in Sources */,
DDC3B274283F411B00AC321C /* LastHeardText.swift in Sources */,
DD2E65262767A01F00E45FC5 /* NodeDetail.swift in Sources */,
DD4C0F1329A9DDF800D3316C /* MeshActivityAttributes.swift in Sources */,
DD1925B928CDA93900720036 /* SerialConfigEnums.swift in Sources */,
DD86D4112881D16900BAEB7A /* WriteCsvFile.swift in Sources */,
DD86D40F2881BE4C00BAEB7A /* CsvDocument.swift in Sources */,
@ -1237,7 +1239,7 @@
INFOPLIST_FILE = Meshtastic/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Meshtastic;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
IPHONEOS_DEPLOYMENT_TARGET = 16.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@ -1271,7 +1273,7 @@
INFOPLIST_FILE = Meshtastic/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = Meshtastic;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
IPHONEOS_DEPLOYMENT_TARGET = 16.2;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",

View file

@ -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
@ -2018,35 +2018,19 @@ extension BLEManager: CBCentralManagerDelegate {
}
if peripherals.count > 0 {
//connectedPeripheral.peripheral = peripherals[0]
// 5
//connectedPeripheral.peripheral.delegate = self
for peripheral in peripherals {
print(peripheral)
switch peripheral.state {
case .connecting: // I've only seen this happen when
// re-launching attached to Xcode.
print("Xcode Restore")
case .connecting: // I've only seen this happen when
// re-launching attached to Xcode.
print("Xcode Restore")
case .connected: // Store for connection / requesting
// notifications when BT starts.
disconnectPeripheral(reconnect: true)
if connectedPeripheral?.peripheral != nil {
self.sendWantConfig()
} else {
disconnectPeripheral(reconnect: true)
case .connected:
connectTo(peripheral: peripheral)
print("Restore BLE State")
default: break
}
print("Actual restore")
//centralManager.connect(peripheral)
default: break
}
// connectedPeripheral.peripheral
//connectedPeripheral.peripheral = peripheral
//connectedPeripheral.peripheral.delegate = self
}
}
print("willRestoreState Hit!")

View file

@ -55,6 +55,8 @@
<string>We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device.</string>
<key>NSSupportsLiveActivities</key>
<true/>
<key>Privacy Bluetooth Always Usage Description</key>
<string>We use bluetooth to connect to nearby Meshtastic Devices</string>
<key>UIApplicationSceneManifest</key>

View file

@ -0,0 +1,23 @@
//
// MeshActivityAttributes.swift
// Meshtastic
//
// Copyright(c) Garth Vander Houwen 2/24/23.
//
import Foundation
import ActivityKit
struct MeshActivityAttributes: ActivityAttributes {
public typealias MyActivityStatus = ContentState
public struct ContentState: Codable, Hashable {
var timerRange: ClosedRange<Date>
}
var nodeNum: Int
var nodeName: String
var channelUtilization: Double
var airtime: Double
var activityName: String
}

View file

@ -0,0 +1,172 @@
////
//// MeshActivityWidget.swift
//// Meshtastic
////
//// Created by Garth Vander Houwen on 2/24/23.
////
//
///*
//See LICENSE folder for this samples licensing information.
//
//Abstract:
//Defines the live activity and dynamic island.
//*/
//
//import SwiftUI
//import WidgetKit
//
//
//struct MeshActivityWidget: Widget {
//
// var body: some WidgetConfiguration {
//
// ActivityConfiguration(for: MeshActivityAttributes.self) { context in
// LiveActivityView(orderNumber: String(context.attributes.nodeNum), timerRange: context.state.timerRange)
// .widgetURL(URL(string: "meshtastic://node/\(context.attributes.nodeNum)"))
// } dynamicIsland: { context in
// DynamicIsland {
// DynamicIslandExpandedRegion(.leading) {
// ExpandedLeadingView()
// }
//
// DynamicIslandExpandedRegion(.trailing, priority: 1) {
// ExpandedTrailingView(orderNumber: String(context.attributes.nodeNum), timerInterval: context.state.timerRange)
// .dynamicIsland(verticalPlacement: .belowIfTooWide)
// }
// } compactLeading: {
// Image("IslandCompactIcon")
// .padding(4)
// .background(.indigo.gradient, in: ContainerRelativeShape())
//
// } compactTrailing: {
// Text(timerInterval: context.state.timerRange, countsDown: true)
// .monospacedDigit()
// .foregroundColor(Color("LightIndigo"))
// .frame(width: 40)
// } minimal: {
// Image("IslandCompactIcon")
// .padding(4)
// .background(.indigo.gradient, in: ContainerRelativeShape())
// }
// .contentMargins(.trailing, 32, for: .expanded)
// .contentMargins([.leading, .top, .bottom], 6, for: .compactLeading)
// .contentMargins(.all, 6, for: .minimal)
// .widgetURL(URL(string: "foodtruck://order/\(context.attributes.nodeNum)"))
// }
// }
//}
//
//struct LiveActivityView: View {
// @Environment(\.colorScheme) private var colorScheme
// @Environment(\.isLuminanceReduced) var isLuminanceReduced
//
// var orderNumber: String
// var timerRange: ClosedRange<Date>
//
// var body: some View {
// HStack {
// Image("IslandExpandedIcon")
// .clipShape(ContainerRelativeShape())
// .opacity(isLuminanceReduced ? 0.5 : 1.0)
// OrderInfoView(orderNumber: orderNumber)
// Spacer()
// OrderTimerView(timerRange: timerRange)
// }
// .tint(.primary)
// .padding([.leading, .top, .bottom])
// .padding(.trailing, 32)
// .activityBackgroundTint(colorScheme == .light ? Color("LiveActivityBackground") : Color("AccentColorDimmed"))
// .activitySystemActionForegroundColor(.primary)
// }
//}
//
//struct ExpandedLeadingView: View {
// var body: some View {
// Image("IslandExpandedIcon")
// .clipShape(ContainerRelativeShape())
// }
//}
//
//struct OrderInfoView: View {
// @Environment(\.isLuminanceReduced) var isLuminanceReduced
//
// var orderNumber: String
//
// var body: some View {
// VStack(alignment: .leading, spacing: 0) {
// Text("Order \(orderNumber)")
// .font(.title3)
// .fontWeight(.semibold)
// .foregroundStyle(.tint)
//
// Text("6 donuts")
// .font(.subheadline)
// .fontWeight(.medium)
// .foregroundStyle(.secondary)
// .opacity(isLuminanceReduced ? 0.5 : 1.0)
// }
// }
//}
//
//struct OrderTimerView: View {
// @Environment(\.isLuminanceReduced) var isLuminanceReduced
//
// var timerRange: ClosedRange<Date>
//
// var body: some View {
// VStack(alignment: .trailing) {
// Text(timerInterval: timerRange, countsDown: true)
// .monospacedDigit()
// .multilineTextAlignment(.trailing)
// .frame(width: 80)
// .font(.title3)
// .fontWeight(.semibold)
// .foregroundStyle(.tint)
// Text("Remaining")
// .font(.subheadline)
// .fontWeight(.medium)
// .foregroundStyle(.secondary)
// .opacity(isLuminanceReduced ? 0.5 : 1.0)
// }
// }
//}
//
//struct ExpandedTrailingView: View {
// var orderNumber: String
// var timerInterval: ClosedRange<Date>
//
// var body: some View {
// HStack(alignment: .lastTextBaseline) {
// OrderInfoView(orderNumber: orderNumber)
// Spacer()
// OrderTimerView(timerRange: timerInterval)
// }
// .tint(Color("LightIndigo"))
// }
//}
//struct MeshActivityPreviewProvider: PreviewProvider {
// static let activityAttributes = MeshActivityAttributes(
// nodeNum: 666, nodeName: "Garth Hydra", channelUtilization: 45.0, airtime: 10.0, activityName: "activity name"
// )
//
// static let state = MeshActivityAttributes.ContentState(
// timerRange: Date.now...Date(timeIntervalSinceNow: 30))
//
// static var previews: some View {
// activityAttributes
// .previewContext(state, viewKind: .dynamicIsland(.compact))
// .previewDisplayName("Compact")
//
// activityAttributes
// .previewContext(state, viewKind: .dynamicIsland(.expanded))
// .previewDisplayName("Expanded")
//
// activityAttributes
// .previewContext(state, viewKind: .content)
// .previewDisplayName("Notification")
//
// activityAttributes
// .previewContext(state, viewKind: .dynamicIsland(.minimal))
// .previewDisplayName("Minimal")
// }
//}

View file

@ -1,68 +0,0 @@
//
// MeshtasticWidgets.swift
// MeshtasticWidgets
//
// Created by Garth Vander Houwen on 2/23/23.
//
import WidgetKit
import SwiftUI
import Intents
struct Provider: IntentTimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), configuration: ConfigurationIntent())
}
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), configuration: configuration)
completion(entry)
}
func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
var entries: [SimpleEntry] = []
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
let entry = SimpleEntry(date: entryDate, configuration: configuration)
entries.append(entry)
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let configuration: ConfigurationIntent
}
struct MeshtasticWidgetsEntryView : View {
var entry: Provider.Entry
var body: some View {
Text(entry.date, style: .time)
}
}
struct MeshtasticWidgets: Widget {
let kind: String = "MeshtasticWidgets"
var body: some WidgetConfiguration {
IntentConfiguration(kind: kind, intent: ConfigurationIntent.self, provider: Provider()) { entry in
MeshtasticWidgetsEntryView(entry: entry)
}
.configurationDisplayName("My Widget")
.description("This is an example widget.")
}
}
struct MeshtasticWidgets_Previews: PreviewProvider {
static var previews: some View {
MeshtasticWidgetsEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent()))
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}

View file

@ -11,7 +11,10 @@ import SwiftUI
@main
struct MeshtasticWidgetsBundle: WidgetBundle {
var body: some Widget {
MeshtasticWidgets()
MeshtasticWidgetsLiveActivity()
// MARK: - Live Activity Widgets
#if canImport(ActivityKit)
//MeshActivityWidget()
#endif
}
}

View file

@ -1,77 +0,0 @@
//
// MeshtasticWidgetsLiveActivity.swift
// MeshtasticWidgets
//
// Created by Garth Vander Houwen on 2/23/23.
//
import ActivityKit
import WidgetKit
import SwiftUI
struct MeshtasticWidgetsAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
// Dynamic stateful properties about your activity go here!
var value: Int
}
// Fixed non-changing properties about your activity go here!
var name: String
}
struct MeshtasticWidgetsLiveActivity: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: MeshtasticWidgetsAttributes.self) { context in
// Lock screen/banner UI goes here
VStack {
Text("Hello")
}
.activityBackgroundTint(Color.cyan)
.activitySystemActionForegroundColor(Color.black)
} dynamicIsland: { context in
DynamicIsland {
// Expanded UI goes here. Compose the expanded UI through
// various regions, like leading/trailing/center/bottom
DynamicIslandExpandedRegion(.leading) {
Text("Leading")
}
DynamicIslandExpandedRegion(.trailing) {
Text("Trailing")
}
DynamicIslandExpandedRegion(.bottom) {
Text("Bottom")
// more content
}
} compactLeading: {
Text("L")
} compactTrailing: {
Text("T")
} minimal: {
Text("Min")
}
.widgetURL(URL(string: "http://www.apple.com"))
.keylineTint(Color.red)
}
}
}
struct MeshtasticWidgetsLiveActivity_Previews: PreviewProvider {
static let attributes = MeshtasticWidgetsAttributes(name: "Me")
static let contentState = MeshtasticWidgetsAttributes.ContentState(value: 3)
static var previews: some View {
attributes
.previewContext(contentState, viewKind: .dynamicIsland(.compact))
.previewDisplayName("Island Compact")
attributes
.previewContext(contentState, viewKind: .dynamicIsland(.expanded))
.previewDisplayName("Island Expanded")
attributes
.previewContext(contentState, viewKind: .dynamicIsland(.minimal))
.previewDisplayName("Minimal")
attributes
.previewContext(contentState, viewKind: .content)
.previewDisplayName("Notification")
}
}

View file

@ -140,6 +140,10 @@
"map.centering"="Centering";
"map.recentering"="Automatic Re-centering";
"map.type"="地图类型";
"map.usertrackingmode"="User tracking mode";
"map.usertrackingmode.none"="None";
"map.usertrackingmode.follow"="Follow";
"map.usertrackingmode.followwithheading"="Follow with heading";
"mesh.log"="Mesh 日志";
"mesh.log.bluetooth.config %@"="Bluetooth config received: %@";
"mesh.log.cannedmessage.config %@"="Canned Message module config received: %@";