diff --git a/OpenHaystack/OpenHaystack.xcodeproj/project.pbxproj b/OpenHaystack/OpenHaystack.xcodeproj/project.pbxproj index 121b4b9..e8323fc 100644 --- a/OpenHaystack/OpenHaystack.xcodeproj/project.pbxproj +++ b/OpenHaystack/OpenHaystack.xcodeproj/project.pbxproj @@ -51,6 +51,7 @@ 78EC226C25DBC2E40042B775 /* OpenHaystackMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78EC226B25DBC2E40042B775 /* OpenHaystackMainView.swift */; }; 78EC227225DBC8CE0042B775 /* Accessory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78EC227125DBC8CE0042B775 /* Accessory.swift */; }; 78EC227725DBDB7E0042B775 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78EC227625DBDB7E0042B775 /* KeychainController.swift */; }; + F126102F2600D1D80066A859 /* Slider+LogScale.swift in Sources */ = {isa = PBXBuildFile; fileRef = F126102E2600D1D80066A859 /* Slider+LogScale.swift */; }; F12D5A5A25FA4F3500CBBA09 /* BluetoothAccessoryScanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = F12D5A5925FA4F3500CBBA09 /* BluetoothAccessoryScanner.swift */; }; F12D5A6025FA79FA00CBBA09 /* Advertisement.swift in Sources */ = {isa = PBXBuildFile; fileRef = F12D5A5F25FA79FA00CBBA09 /* Advertisement.swift */; }; F1647C1625FF6C61004144D6 /* BluetoothTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1647C1525FF6C61004144D6 /* BluetoothTests.swift */; }; @@ -154,6 +155,7 @@ 78EC226B25DBC2E40042B775 /* OpenHaystackMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenHaystackMainView.swift; sourceTree = ""; }; 78EC227125DBC8CE0042B775 /* Accessory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Accessory.swift; sourceTree = ""; }; 78EC227625DBDB7E0042B775 /* KeychainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainController.swift; sourceTree = ""; }; + F126102E2600D1D80066A859 /* Slider+LogScale.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Slider+LogScale.swift"; sourceTree = ""; }; F12D5A5925FA4F3500CBBA09 /* BluetoothAccessoryScanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothAccessoryScanner.swift; sourceTree = ""; }; F12D5A5F25FA79FA00CBBA09 /* Advertisement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Advertisement.swift; sourceTree = ""; }; F1647C1525FF6C61004144D6 /* BluetoothTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothTests.swift; sourceTree = ""; }; @@ -367,6 +369,7 @@ 7851F1DC25EE90FA0049480D /* AccessoryMapView.swift */, 7821DAD225F7C39A0054DC33 /* ESP32InstallSheet.swift */, 78D9B80525F7CF60009B9CE8 /* ManageAccessoriesView.swift */, + F126102E2600D1D80066A859 /* Slider+LogScale.swift */, ); path = Views; sourceTree = ""; @@ -595,6 +598,7 @@ 78D9B80625F7CF60009B9CE8 /* ManageAccessoriesView.swift in Sources */, 78486BEF25DD711E0007ED87 /* PopUpAlertView.swift in Sources */, 78014A2925DC08580089F6D9 /* MicrobitController.swift in Sources */, + F126102F2600D1D80066A859 /* Slider+LogScale.swift in Sources */, F1647C1B25FF7954004144D6 /* AccessoryNearbyMonitor.swift in Sources */, 78286D1F25E3D8B800F65511 /* ALTAnisetteData.m in Sources */, 781EB3EC25DAD7EA00FEAA19 /* DecryptReports.swift in Sources */, diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift b/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift index cae1a84..62c73dd 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Views/OpenHaystackMainView.swift @@ -29,7 +29,7 @@ struct OpenHaystackMainView: View { @State var isLoading = false @State var focusedAccessory: Accessory? @State var historyMapView = false - @State var historySeconds: TimeInterval = 2 * TimeInterval.Units.day.rawValue + @State var historySeconds: TimeInterval = TimeInterval.Units.day.rawValue @State var accessoryToDeploy: Accessory? @State var showMailPlugInPopover = false @@ -115,7 +115,7 @@ struct OpenHaystackMainView: View { Group { if self.historyMapView { Text("\(TimeInterval(self.historySeconds).description)") - Slider(value: $historySeconds, in: 30 * TimeInterval.Units.minute.rawValue...TimeInterval.Units.week.rawValue) { + Slider.withLogScale(value: $historySeconds, in: 30 * TimeInterval.Units.minute.rawValue...TimeInterval.Units.week.rawValue) { Text("Past time to show") } .frame(width: 80) @@ -442,8 +442,8 @@ extension TimeInterval { var value = 0 var unit = Units.second Units.allCases.forEach { u in - if self >= u.rawValue { - value = Int(self / u.rawValue) + if self.rounded() >= u.rawValue { + value = Int((self / u.rawValue).rounded()) unit = u } } diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Views/Slider+LogScale.swift b/OpenHaystack/OpenHaystack/HaystackApp/Views/Slider+LogScale.swift new file mode 100644 index 0000000..76d6050 --- /dev/null +++ b/OpenHaystack/OpenHaystack/HaystackApp/Views/Slider+LogScale.swift @@ -0,0 +1,45 @@ +// +// OpenHaystack – Tracking personal Bluetooth devices via Apple's Find My network +// +// Copyright © 2021 Secure Mobile Networking Lab (SEEMOO) +// Copyright © 2021 The Open Wireless Link Project +// +// SPDX-License-Identifier: AGPL-3.0-only +// + +import SwiftUI + +extension Binding where Value == Double { + func logarithmic(base: Double = 10.0) -> Binding { + Binding( + get: { + logC(self.wrappedValue, forBase: base) + }, + set: { (newValue) in + self.wrappedValue = pow(base, newValue) + }) + } +} + +extension Slider { + static func withLogScale( + base: Double = 10.0, + value: Binding, + in inRange: ClosedRange, + minimumValueLabel: ValueLabel = EmptyView() as! ValueLabel, + maximumValueLabel: ValueLabel = EmptyView() as! ValueLabel, + label: () -> Label = { EmptyView() as! Label }, + onEditingChanged: @escaping (Bool) -> Void = { _ in } + ) -> Slider where Label: View, ValueLabel: View { + return self.init( + value: value.logarithmic(base: base), + in: logC(inRange.lowerBound, forBase: base)...logC(inRange.upperBound, forBase: base), + onEditingChanged: onEditingChanged, minimumValueLabel: minimumValueLabel, + maximumValueLabel: maximumValueLabel, + label: label) + } +} + +private func logC(_ value: Double, forBase base: Double) -> Double { + return log(value) / log(base) +}