From 9c4deecc36c19cc2329458df823a999502f21539 Mon Sep 17 00:00:00 2001 From: Garth Vander Houwen Date: Sat, 25 Feb 2023 08:39:10 -0800 Subject: [PATCH] Try and re-connect BLE devices in the background better --- Meshtastic.xcodeproj/project.pbxproj | 22 ++- Meshtastic/Helpers/BLEManager.swift | 36 +--- Meshtastic/Info.plist | 2 + .../MeshActivityAttributes.swift | 23 +++ MeshtasticWidgets/MeshActivityWidget.swift | 172 ++++++++++++++++++ MeshtasticWidgets/MeshtasticWidgets.swift | 68 ------- .../MeshtasticWidgetsBundle.swift | 7 +- .../MeshtasticWidgetsLiveActivity.swift | 77 -------- zh-Hans.lproj/Localizable.strings | 4 + 9 files changed, 228 insertions(+), 183 deletions(-) create mode 100644 MeshtasticWidgets/MeshActivityAttributes.swift create mode 100644 MeshtasticWidgets/MeshActivityWidget.swift delete mode 100644 MeshtasticWidgets/MeshtasticWidgets.swift delete mode 100644 MeshtasticWidgets/MeshtasticWidgetsLiveActivity.swift diff --git a/Meshtastic.xcodeproj/project.pbxproj b/Meshtastic.xcodeproj/project.pbxproj index cf6091d0..604185c6 100644 --- a/Meshtastic.xcodeproj/project.pbxproj +++ b/Meshtastic.xcodeproj/project.pbxproj @@ -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 = ""; }; - DD4C0F0029A8735400D3316C /* MeshtasticWidgetsLiveActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticWidgetsLiveActivity.swift; sourceTree = ""; }; - DD4C0F0229A8735400D3316C /* MeshtasticWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshtasticWidgets.swift; sourceTree = ""; }; DD4C0F0429A8735400D3316C /* MeshtasticWidgets.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = MeshtasticWidgets.intentdefinition; sourceTree = ""; }; DD4C0F0529A8735500D3316C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; DD4C0F0729A8735500D3316C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DD4C0F0D29A8735500D3316C /* MeshtasticWidgetsExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MeshtasticWidgetsExtension.entitlements; sourceTree = ""; }; + DD4C0F1229A9DDF800D3316C /* MeshActivityAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshActivityAttributes.swift; sourceTree = ""; }; + DD4C0F1429A9E21A00D3316C /* MeshActivityWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeshActivityWidget.swift; sourceTree = ""; }; DD4F23CC28779A3C001D37CB /* EnvironmentMetricsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnvironmentMetricsLog.swift; sourceTree = ""; }; DD5394FD276BA0EF00AD86B1 /* PositionEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PositionEntityExtension.swift; sourceTree = ""; }; DD58C5F12919AD3C00D5BEFB /* ChannelEntityExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChannelEntityExtension.swift; sourceTree = ""; }; @@ -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 = ""; @@ -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", diff --git a/Meshtastic/Helpers/BLEManager.swift b/Meshtastic/Helpers/BLEManager.swift index ed28b2a5..ca50f80d 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 @@ -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!") diff --git a/Meshtastic/Info.plist b/Meshtastic/Info.plist index 55f1bdb3..db7a3c90 100644 --- a/Meshtastic/Info.plist +++ b/Meshtastic/Info.plist @@ -55,6 +55,8 @@ We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device. NSLocationWhenInUseUsageDescription We use your location to display it on the mesh map as well as to have GPS coordinatess to send to the connected device. + NSSupportsLiveActivities + Privacy – Bluetooth Always Usage Description We use bluetooth to connect to nearby Meshtastic Devices UIApplicationSceneManifest diff --git a/MeshtasticWidgets/MeshActivityAttributes.swift b/MeshtasticWidgets/MeshActivityAttributes.swift new file mode 100644 index 00000000..cf00b260 --- /dev/null +++ b/MeshtasticWidgets/MeshActivityAttributes.swift @@ -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 + } + + var nodeNum: Int + var nodeName: String + var channelUtilization: Double + var airtime: Double + var activityName: String +} diff --git a/MeshtasticWidgets/MeshActivityWidget.swift b/MeshtasticWidgets/MeshActivityWidget.swift new file mode 100644 index 00000000..2933be20 --- /dev/null +++ b/MeshtasticWidgets/MeshActivityWidget.swift @@ -0,0 +1,172 @@ +//// +//// MeshActivityWidget.swift +//// Meshtastic +//// +//// Created by Garth Vander Houwen on 2/24/23. +//// +// +///* +//See LICENSE folder for this sample’s 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 +// +// 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 +// +// 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 +// +// 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") +// } +//} diff --git a/MeshtasticWidgets/MeshtasticWidgets.swift b/MeshtasticWidgets/MeshtasticWidgets.swift deleted file mode 100644 index 4cc715ba..00000000 --- a/MeshtasticWidgets/MeshtasticWidgets.swift +++ /dev/null @@ -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) -> ()) { - 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)) - } -} diff --git a/MeshtasticWidgets/MeshtasticWidgetsBundle.swift b/MeshtasticWidgets/MeshtasticWidgetsBundle.swift index 4fbfbb9e..88f6af9a 100644 --- a/MeshtasticWidgets/MeshtasticWidgetsBundle.swift +++ b/MeshtasticWidgets/MeshtasticWidgetsBundle.swift @@ -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 } } diff --git a/MeshtasticWidgets/MeshtasticWidgetsLiveActivity.swift b/MeshtasticWidgets/MeshtasticWidgetsLiveActivity.swift deleted file mode 100644 index f871023d..00000000 --- a/MeshtasticWidgets/MeshtasticWidgetsLiveActivity.swift +++ /dev/null @@ -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") - } -} diff --git a/zh-Hans.lproj/Localizable.strings b/zh-Hans.lproj/Localizable.strings index 9a54174b..e2f07a11 100644 --- a/zh-Hans.lproj/Localizable.strings +++ b/zh-Hans.lproj/Localizable.strings @@ -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: %@";