Przeglądaj źródła

Merge branch 'dexcom' of https://github.com/josep1972/LoopFollow into dexcom

Jose Paredes 6 lat temu
rodzic
commit
54b46000f0

+ 24 - 0
LoopFollow.xcodeproj/project.pbxproj

@@ -8,7 +8,13 @@
 
 /* Begin PBXBuildFile section */
 		3F1335F351590E573D8E6962 /* Pods_LoopFollow.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7D55B42A22051DAD69E89D0 /* Pods_LoopFollow.framework */; };
+		DD152D3B24C01B2300361FA2 /* InfoDisplaySettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD152D3A24C01B2300361FA2 /* InfoDisplaySettingsViewController.swift */; };
 		DD98F54424BCEFEE0007425A /* ShareClientExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD98F54324BCEFEE0007425A /* ShareClientExtension.swift */; };
+		DDCF979424C0D380002C9752 /* UIViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979324C0D380002C9752 /* UIViewExtension.swift */; };
+		DDCF979624C1443C002C9752 /* GeneralSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979524C1443C002C9752 /* GeneralSettingsViewController.swift */; };
+		DDCF979824C1489C002C9752 /* GraphSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979724C1489C002C9752 /* GraphSettingsViewController.swift */; };
+		DDCF979A24C14DB4002C9752 /* WatchSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979924C14DB4002C9752 /* WatchSettingsViewController.swift */; };
+		DDCF979C24C14EFB002C9752 /* DebugSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDCF979B24C14EFB002C9752 /* DebugSettingsViewController.swift */; };
 		FC16A97A24996673003D6245 /* NightScout.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC16A97924996673003D6245 /* NightScout.swift */; };
 		FC16A97B249966A3003D6245 /* AlarmSound.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC7CE589248ABEA3001F83B8 /* AlarmSound.swift */; };
 		FC16A97D24996747003D6245 /* Alarms.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC16A97C24996747003D6245 /* Alarms.swift */; };
@@ -163,7 +169,13 @@
 /* Begin PBXFileReference section */
 		059B0FA59AABFE72FE13DDDA /* Pods-LoopFollow.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LoopFollow.release.xcconfig"; path = "Target Support Files/Pods-LoopFollow/Pods-LoopFollow.release.xcconfig"; sourceTree = "<group>"; };
 		A7D55B42A22051DAD69E89D0 /* Pods_LoopFollow.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LoopFollow.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		DD152D3A24C01B2300361FA2 /* InfoDisplaySettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoDisplaySettingsViewController.swift; sourceTree = "<group>"; };
 		DD98F54324BCEFEE0007425A /* ShareClientExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareClientExtension.swift; sourceTree = "<group>"; };
+		DDCF979324C0D380002C9752 /* UIViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewExtension.swift; sourceTree = "<group>"; };
+		DDCF979524C1443C002C9752 /* GeneralSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsViewController.swift; sourceTree = "<group>"; };
+		DDCF979724C1489C002C9752 /* GraphSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphSettingsViewController.swift; sourceTree = "<group>"; };
+		DDCF979924C14DB4002C9752 /* WatchSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchSettingsViewController.swift; sourceTree = "<group>"; };
+		DDCF979B24C14EFB002C9752 /* DebugSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugSettingsViewController.swift; sourceTree = "<group>"; };
 		ECA3EFB4037410B4973BB632 /* Pods-LoopFollow.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-LoopFollow.debug.xcconfig"; path = "Target Support Files/Pods-LoopFollow/Pods-LoopFollow.debug.xcconfig"; sourceTree = "<group>"; };
 		FC16A97924996673003D6245 /* NightScout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightScout.swift; sourceTree = "<group>"; };
 		FC16A97C24996747003D6245 /* Alarms.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alarms.swift; sourceTree = "<group>"; };
@@ -356,6 +368,7 @@
 			isa = PBXGroup;
 			children = (
 				DD98F54324BCEFEE0007425A /* ShareClientExtension.swift */,
+				DDCF979324C0D380002C9752 /* UIViewExtension.swift */,
 			);
 			path = Extensions;
 			sourceTree = "<group>";
@@ -589,6 +602,11 @@
 				FCFEECA1248857A600402A7F /* SettingsViewController.swift */,
 				FCC6884F248935D800A0279D /* AlarmViewController.swift */,
 				FC7CE59E248D8D23001F83B8 /* SnoozeViewController.swift */,
+				DD152D3A24C01B2300361FA2 /* InfoDisplaySettingsViewController.swift */,
+				DDCF979524C1443C002C9752 /* GeneralSettingsViewController.swift */,
+				DDCF979724C1489C002C9752 /* GraphSettingsViewController.swift */,
+				DDCF979924C14DB4002C9752 /* WatchSettingsViewController.swift */,
+				DDCF979B24C14EFB002C9752 /* DebugSettingsViewController.swift */,
 			);
 			path = ViewControllers;
 			sourceTree = "<group>";
@@ -836,6 +854,7 @@
 				FC7CE59F248D8D23001F83B8 /* SnoozeViewController.swift in Sources */,
 				FCC6886724898F8000A0279D /* UserDefaultsValue.swift in Sources */,
 				FC97881E2485969B00A7906C /* NightScoutViewController.swift in Sources */,
+				DDCF979824C1489C002C9752 /* GraphSettingsViewController.swift in Sources */,
 				FC3AE7B5249E8E0E00AAE1E0 /* LoopFollow.xcdatamodeld in Sources */,
 				FCC6886F2489A53800A0279D /* AppConstants.swift in Sources */,
 				FC16A97A24996673003D6245 /* NightScout.swift in Sources */,
@@ -844,11 +863,15 @@
 				FC16A97B249966A3003D6245 /* AlarmSound.swift in Sources */,
 				FCFEECA2248857A600402A7F /* SettingsViewController.swift in Sources */,
 				FCFEECA02488157B00402A7F /* Chart.swift in Sources */,
+				DDCF979424C0D380002C9752 /* UIViewExtension.swift in Sources */,
 				FCEF87AC24A141A700AE6FA0 /* bgUnits.swift in Sources */,
 				FC1BDD3224A2585C001B652C /* DataStructs.swift in Sources */,
 				FCC6886524898EEE00A0279D /* UserDefaults.swift in Sources */,
 				FC16A97F249969E2003D6245 /* Graphs.swift in Sources */,
+				DD152D3B24C01B2300361FA2 /* InfoDisplaySettingsViewController.swift in Sources */,
+				DDCF979C24C14EFB002C9752 /* DebugSettingsViewController.swift in Sources */,
 				FC97881C2485969B00A7906C /* MainViewController.swift in Sources */,
+				DDCF979624C1443C002C9752 /* GeneralSettingsViewController.swift in Sources */,
 				FCC0FAC224922A22003E610E /* DictionaryKeyPath.swift in Sources */,
 				FC9788182485969B00A7906C /* AppDelegate.swift in Sources */,
 				FCC6886B24898FD800A0279D /* ObservationToken.swift in Sources */,
@@ -859,6 +882,7 @@
 				FC1BDD2B24A22650001B652C /* Stats.swift in Sources */,
 				FC1BDD2D24A23204001B652C /* StatsView.swift in Sources */,
 				FCE537BC249A4D7D00F80BF8 /* carbBolusArrays.swift in Sources */,
+				DDCF979A24C14DB4002C9752 /* WatchSettingsViewController.swift in Sources */,
 				FC16A98124996C07003D6245 /* DateTime.swift in Sources */,
 				FC3CAB022493B6220068A152 /* BackgroundTaskAudio.swift in Sources */,
 			);

+ 23 - 8
LoopFollow/Controllers/Graphs.swift

@@ -10,10 +10,9 @@ import Foundation
 import Charts
 import UIKit
 
-
+let ScaleXMax:Float = 150.0
 extension MainViewController {
     
-
     func chartValueSelected(_ chartView: ChartViewBase, entry: ChartDataEntry, highlight: Highlight) {
         if chartView == BGChartFull {
             BGChart.moveViewToX(entry.x)
@@ -27,9 +26,22 @@ extension MainViewController {
             //BGChartFull.highlightValue(x: Double(currentMatrix.tx), y: Double(currentMatrix.ty), dataSetIndex: 0)
         }
     }
+    
+    func chartScaled(_ chartView: ChartViewBase, scaleX: CGFloat, scaleY: CGFloat) {
+        print("Chart Scaled: \(BGChart.scaleX), \(BGChart.scaleY)")
+      
+        // dont store huge values
+        var scale: Float = Float(BGChart.scaleX)
+        if(scale > ScaleXMax ) {
+            scale = ScaleXMax
+        }
+        UserDefaultsRepository.chartScaleX.value = Float(scale)
+    }
+    
 
     func createGraph(){
         self.BGChart.clear()
+        
         // Create the BG Graph Data
         let entries = bgData
         var bgChartEntry = [ChartDataEntry]()
@@ -211,12 +223,11 @@ extension MainViewController {
         BGChart.scaleYEnabled = false
         BGChart.drawGridBackgroundEnabled = false
         //BGChart.gridBackgroundColor = NSUIColor.secondarySystemBackground
+      
         
         BGChart.data = data
         BGChart.setExtraOffsets(left: 10, top: 10, right: 10, bottom: 10)
         
-       
-        
     }
     
     func updateBGGraphSettings() {
@@ -255,7 +266,7 @@ extension MainViewController {
         ul.limit = Double(UserDefaultsRepository.highLine.value)
         ul.lineColor = NSUIColor.systemYellow.withAlphaComponent(0.5)
         BGChart.rightAxis.addLimitLine(ul)
-        
+    
         BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
         BGChart.data?.notifyDataChanged()
         BGChart.notifyDataSetChanged()
@@ -293,7 +304,6 @@ extension MainViewController {
         }
         
         
-        
         // Set Colors
         let lineBG = BGChart.lineData!.dataSets[dataIndex] as! LineChartDataSet
 
@@ -315,7 +325,6 @@ extension MainViewController {
         
         BGChart.rightAxis.axisMaximum = Double(maxBG)
         BGChart.setVisibleXRangeMinimum(600)
-        
         BGChart.data?.dataSets[dataIndex].notifyDataSetChanged()
         BGChart.data?.notifyDataChanged()
         BGChart.notifyDataSetChanged()
@@ -324,7 +333,13 @@ extension MainViewController {
         BGChartFull.notifyDataSetChanged()
         
         if firstGraphLoad {
-            BGChart.zoom(scaleX: 18, scaleY: 1, x: 1, y: 1)
+            var scaleX = CGFloat(UserDefaultsRepository.chartScaleX.value)
+            print("Scale: \(scaleX)")
+            if( scaleX > 18 ) {
+                scaleX = 18
+                UserDefaultsRepository.chartScaleX.value = 18
+            }
+            BGChart.zoom(scaleX: scaleX, scaleY: 1, x: 1, y: 1)
             firstGraphLoad = false
         }
         if BGChart.chartXMax > dateTimeUtils.getNowTimeIntervalUTC() {

+ 6 - 0
LoopFollow/Controllers/NightScout.swift

@@ -400,6 +400,7 @@ extension MainViewController {
         
         // Loop
         if let lastLoopRecord = lastDeviceStatus?["loop"] as! [String : AnyObject]? {
+            print("Loop: \(lastLoopRecord)")
             if let lastLoopTime = formatter.date(from: (lastLoopRecord["timestamp"] as! String))?.timeIntervalSince1970  {
                 UserDefaultsRepository.alertLastLoopTime.value = lastLoopTime
                 if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "lastLoopTime: " + String(lastLoopTime)) }
@@ -413,6 +414,7 @@ extension MainViewController {
                         if UserDefaultsRepository.debugLog.value { self.writeDebugLog(value: "Loop: Was Enacted") }
                         wasEnacted = true
                         if let lastTempBasal = enacted["rate"] as? Double {
+
                         }
                     }
                     if let iobdata = lastLoopRecord["iob"] as? [String:AnyObject] {
@@ -445,6 +447,9 @@ extension MainViewController {
                             updatePredictionGraph()
                         }
                     }
+                    if let recBolus = lastLoopRecord["recommendedBolus"] as? Double {
+                        tableData[8].value = String(format:"%.2fU", recBolus)
+                    }
                     if let loopStatus = lastLoopRecord["recommendedTempBasal"] as? [String:AnyObject] {
                         if let tempBasalTime = formatter.date(from: (loopStatus["timestamp"] as! String))?.timeIntervalSince1970 {
                             var lastBGTime = lastLoopTime
@@ -1019,6 +1024,7 @@ extension MainViewController {
         if UserDefaultsRepository.graphBasal.value {
             updateBasalGraph()
         }
+        infoTable.reloadData()
     }
     
     // NS Bolus Web Call

+ 29 - 0
LoopFollow/Extensions/UIViewExtension.swift

@@ -0,0 +1,29 @@
+//
+//  UIViewExtension.swift
+//  LoopFollow
+//
+//  Created by Jose Paredes on 7/16/20.
+//  Copyright © 2020 Jon Fawcett. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+extension UIView {
+    enum ViewSide {
+        case Left, Right, Top, Bottom
+    }
+    
+    func addBorder(toSide side: ViewSide, withColor color: CGColor, andThickness thickness: CGFloat) {
+       let border = CALayer()
+       border.backgroundColor = color
+       
+       switch side {
+          case .Left: border.frame = CGRect(x:0, y: 0, width: thickness, height: frame.height); break
+          case .Right: border.frame = CGRect(x: frame.width-thickness, y: 0, width: thickness, height: frame.height); break
+          case .Top: border.frame = CGRect(x: 0, y: 0, width: frame.width, height: thickness); break
+          case .Bottom: border.frame = CGRect(x: 0, y: frame.height-thickness, width: frame.width, height: thickness); break
+       }
+       layer.addSublayer(border)
+    }
+}

+ 110 - 0
LoopFollow/ViewControllers/DebugSettingsViewController.swift

@@ -0,0 +1,110 @@
+//
+//  DebugSettingsViewController.swift
+//  LoopFollow
+//
+//  Created by Jose Paredes on 7/16/20.
+//  Copyright © 2020 Jon Fawcett. All rights reserved.
+//
+
+import Foundation
+import Eureka
+import EventKit
+import EventKitUI
+
+class DebugSettingsViewController: FormViewController {
+   override func viewDidLoad()  {
+      super.viewDidLoad()
+      if UserDefaultsRepository.forceDarkMode.value {
+         overrideUserInterfaceStyle = .dark
+      }
+      buildDebugSettings()
+   }
+   private func buildDebugSettings() {
+        form
+            +++ Section("Debug Settings")
+
+        <<< SwitchRow("downloadBasal"){ row in
+            row.title = "Download Basal"
+            row.value = UserDefaultsRepository.downloadBasal.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.downloadBasal.value = value
+            }
+            <<< SwitchRow("graphBasal"){ row in
+            row.title = "Graph Basal"
+            row.value = UserDefaultsRepository.graphBasal.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.graphBasal.value = value
+            }
+            <<< SwitchRow("downloadBolus"){ row in
+                row.title = "Download Bolus"
+                row.value = UserDefaultsRepository.downloadBolus.value
+            }.onChange { [weak self] row in
+                        guard let value = row.value else { return }
+                        UserDefaultsRepository.downloadBolus.value = value
+                }
+           <<< SwitchRow("graphBolus"){ row in
+               row.title = "Graph Bolus"
+               row.value = UserDefaultsRepository.graphBolus.value
+           }.onChange { [weak self] row in
+                       guard let value = row.value else { return }
+                       UserDefaultsRepository.graphBolus.value = value
+               }
+            <<< SwitchRow("downloadCarbs"){ row in
+                row.title = "Download Carbs"
+                row.value = UserDefaultsRepository.downloadCarbs.value
+            }.onChange { [weak self] row in
+                        guard let value = row.value else { return }
+                        UserDefaultsRepository.downloadCarbs.value = value
+                }
+              <<< SwitchRow("graphCarbs"){ row in
+                  row.title = "Graph Carbs"
+                  row.value = UserDefaultsRepository.graphCarbs.value
+              }.onChange { [weak self] row in
+                          guard let value = row.value else { return }
+                          UserDefaultsRepository.graphCarbs.value = value
+                  }
+            
+        <<< SwitchRow("downloadPrediction"){ row in
+                 row.title = "Download Prediction"
+                 row.value = UserDefaultsRepository.downloadPrediction.value
+             }.onChange { [weak self] row in
+                         guard let value = row.value else { return }
+                         UserDefaultsRepository.downloadPrediction.value = value
+                 }
+        <<< SwitchRow("graphPrediction"){ row in
+            row.title = "Graph Prediction"
+            row.value = UserDefaultsRepository.graphPrediction.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.graphPrediction.value = value
+            }
+            <<< SwitchRow("debugLog"){ row in
+                row.title = "Show Debug Log"
+                row.value = UserDefaultsRepository.debugLog.value
+            }.onChange { [weak self] row in
+                        guard let value = row.value else { return }
+                        UserDefaultsRepository.debugLog.value = value
+                }
+        <<< StepperRow("viewRefreshDelay") { row in
+            row.title = "View Refresh Delay"
+            row.cell.stepper.stepValue = 1
+            row.cell.stepper.minimumValue = 5
+            row.cell.stepper.maximumValue = 30
+            row.value = Double(UserDefaultsRepository.viewRefreshDelay.value)
+            row.displayValueFor = { value in
+                guard let value = value else { return nil }
+                return "\(Int(value))"
+            }
+        }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.viewRefreshDelay.value = Double(value)
+        }
+        +++ ButtonRow() {
+          $0.title = "DONE"
+        }.onCellSelection { (row, arg)  in
+          self.dismiss(animated:true, completion: nil)
+        }
+    }
+}

+ 110 - 0
LoopFollow/ViewControllers/GeneralSettingsViewController.swift

@@ -0,0 +1,110 @@
+//
+//  GeneralSetingsViewController.swift
+//  LoopFollow
+//
+//  Created by Jose Paredes on 7/16/20.
+//  Copyright © 2020 Jon Fawcett. All rights reserved.
+//
+
+import Foundation
+import Eureka
+import EventKit
+import EventKitUI
+
+class GeneralSettingsViewController: FormViewController {
+   
+   override func viewDidLoad()  {
+      super.viewDidLoad()
+      
+      if UserDefaultsRepository.forceDarkMode.value {
+         overrideUserInterfaceStyle = .dark
+      }
+      buildGeneralSettings()
+   }
+   
+   private func buildGeneralSettings() {
+      form
+        +++ Section("General Settings")
+        <<< SwitchRow("colorBGText") { row in
+        row.title = "Color Main BG Text"
+        row.value = UserDefaultsRepository.colorBGText.value
+        }.onChange { [weak self] row in
+            guard let value = row.value else { return }
+                UserDefaultsRepository.colorBGText.value = value
+            // Force main screen update
+            guard let mainScreen = self?.tabBarController!.viewControllers?[0] as? MainViewController else { return }
+            mainScreen.setBGTextColor()
+        }
+        <<< SwitchRow("forceDarkMode") { row in
+        row.title = "Force Dark Mode (Restart App)"
+        row.value = UserDefaultsRepository.forceDarkMode.value
+        }.onChange { [weak self] row in
+            guard let value = row.value else { return }
+                UserDefaultsRepository.forceDarkMode.value = value
+        }
+        <<< SwitchRow("persistentNotification") { row in
+        row.title = "Persistent Notification"
+        row.value = UserDefaultsRepository.persistentNotification.value
+        }.onChange { [weak self] row in
+            guard let value = row.value else { return }
+                UserDefaultsRepository.persistentNotification.value = value
+        }
+        <<< SwitchRow("screenlockSwitchState") { row in
+            row.title = "Keep Screen Active"
+            row.value = UserDefaultsRepository.screenlockSwitchState.value
+            }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                
+                if value {
+                    UserDefaultsRepository.screenlockSwitchState.value = value
+                }
+            }
+        
+        <<< SwitchRow("backgroundRefresh"){ row in
+            row.title = "Background Refresh"
+            row.tag = "backgroundRefresh"
+            row.value = UserDefaultsRepository.backgroundRefresh.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.backgroundRefresh.value = value
+            }
+        <<< StepperRow("backgroundRefreshFrequency") { row in
+            row.title = "Refresh Minutes"
+            row.tag = "backgroundRefreshFrequency"
+            row.cell.stepper.stepValue = 0.25
+            row.cell.stepper.minimumValue = 0.25
+            row.cell.stepper.maximumValue = 10
+            row.value = Double(UserDefaultsRepository.backgroundRefreshFrequency.value)
+            row.hidden = "$backgroundRefresh == false"
+        }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.backgroundRefreshFrequency.value = value
+        }
+            
+        <<< SwitchRow("appBadge"){ row in
+            row.title = "Display App Badge"
+            row.tag = "appBadge"
+            row.value = UserDefaultsRepository.appBadge.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.appBadge.value = value
+                    // Force main screen update
+                    guard let mainScreen = self?.tabBarController!.viewControllers?[0] as? MainViewController else { return }
+                    mainScreen.nightscoutLoader(forceLoad: true)
+        }
+        <<< SwitchRow("speakBG"){ row in
+            row.title = "Speak BG"
+            row.value = UserDefaultsRepository.speakBG.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.speakBG.value = value
+        }
+        
+        +++ ButtonRow() {
+          $0.title = "DONE"
+       }.onCellSelection { (row, arg)  in
+          self.dismiss(animated:true, completion: nil)
+       }
+    }
+   
+}

+ 161 - 0
LoopFollow/ViewControllers/GraphSettingsViewController.swift

@@ -0,0 +1,161 @@
+//
+//  GraphSettingsViewController.swift
+//  LoopFollow
+//
+//  Created by Jose Paredes on 7/16/20.
+//  Copyright © 2020 Jon Fawcett. All rights reserved.
+//
+
+import Foundation
+import Eureka
+import EventKit
+import EventKitUI
+
+class GraphSettingsViewController: FormViewController {
+   override func viewDidLoad()  {
+      super.viewDidLoad()
+      if UserDefaultsRepository.forceDarkMode.value {
+         overrideUserInterfaceStyle = .dark
+      }
+      buildGraphSettings()
+   }
+    private func buildGraphSettings() {
+        form
+            +++ Section("Graph Settings")
+            
+        <<< SwitchRow("switchRowDots"){ row in
+            row.title = "Display Dots"
+            row.value = UserDefaultsRepository.showDots.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.showDots.value = value
+                // Force main screen update
+                guard let mainScreen = self?.tabBarController!.viewControllers?[0] as? MainViewController else { return }
+                mainScreen.updateBGGraphSettings()
+            }
+        <<< SwitchRow("switchRowLines"){ row in
+            row.title = "Display Lines"
+            row.value = UserDefaultsRepository.showLines.value
+        }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.showLines.value = value
+            // Force main screen update
+            guard let mainScreen = self?.tabBarController!.viewControllers?[0] as? MainViewController else { return }
+            mainScreen.updateBGGraphSettings()
+                    
+        }
+            <<< SwitchRow("offsetCarbsBolus"){ row in
+                row.title = "Offset Carb/Bolus Dots"
+                row.value = UserDefaultsRepository.offsetCarbsBolus.value
+            }.onChange { [weak self] row in
+                        guard let value = row.value else { return }
+                        UserDefaultsRepository.offsetCarbsBolus.value = value
+                        
+            }
+            <<< StepperRow("predictionToLoad") { row in
+                row.title = "Hours of Prediction"
+                row.cell.stepper.stepValue = 0.25
+                row.cell.stepper.minimumValue = 0.0
+                row.cell.stepper.maximumValue = 6.0
+                row.value = Double(UserDefaultsRepository.predictionToLoad.value)
+            }.onChange { [weak self] row in
+                    guard let value = row.value else { return }
+                    UserDefaultsRepository.predictionToLoad.value = value
+            }
+        <<< StepperRow("minBGScale") { row in
+            row.title = "Min BG Scale"
+            row.cell.stepper.stepValue = 1
+            row.cell.stepper.minimumValue = Double(UserDefaultsRepository.highLine.value)
+            row.cell.stepper.maximumValue = 400
+            row.value = Double(UserDefaultsRepository.minBGScale.value)
+            row.displayValueFor = { value in
+                guard let value = value else { return nil }
+                return bgUnits.toDisplayUnits(String(value))
+            }
+        }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.minBGScale.value = Float(value)
+            }
+            <<< StepperRow("minBGValue") { row in
+                row.title = "Min BG Display"
+                row.cell.stepper.stepValue = 1
+                row.cell.stepper.minimumValue = -40
+                row.cell.stepper.maximumValue = 40
+                row.value = Double(UserDefaultsRepository.minBGValue.value)
+                row.displayValueFor = { value in
+                    guard let value = value else { return nil }
+                    return bgUnits.toDisplayUnits(String(value))
+                }
+            }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.minBGValue.value = Float(value)
+                // Force main screen update
+                guard let mainScreen = self?.tabBarController!.viewControllers?[0] as? MainViewController else { return }
+                mainScreen.updateBGGraphSettings()
+            }
+        <<< StepperRow("minBasalScale") { row in
+            row.title = "Min Basal Scale"
+            row.cell.stepper.stepValue = 0.5
+            row.cell.stepper.minimumValue = 0.5
+            row.cell.stepper.maximumValue = 20
+            row.value = Double(UserDefaultsRepository.minBasalScale.value)
+        }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.minBasalScale.value = value
+        }
+        <<< StepperRow("lowLine") { row in
+            row.title = "Low BG Display Value"
+            row.cell.stepper.stepValue = 1
+            row.cell.stepper.minimumValue = 40
+            row.cell.stepper.maximumValue = 120
+            row.value = Double(UserDefaultsRepository.lowLine.value)
+            row.displayValueFor = { value in
+                guard let value = value else { return nil }
+                return bgUnits.toDisplayUnits(String(value))
+            }
+        }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.lowLine.value = Float(value)
+            // Force main screen update
+            guard let mainScreen = self?.tabBarController!.viewControllers?[0] as? MainViewController else { return }
+            mainScreen.updateBGGraphSettings()
+        }
+        <<< StepperRow("highLine") { row in
+            row.title = "High BG Display Value"
+            row.cell.stepper.stepValue = 1
+            row.cell.stepper.minimumValue = 120
+            row.cell.stepper.maximumValue = 400
+            row.value = Double(UserDefaultsRepository.highLine.value)
+            row.displayValueFor = { value in
+                guard let value = value else { return nil }
+                return bgUnits.toDisplayUnits(String(value))
+            }
+        }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.highLine.value = Float(value)
+            // Force main screen update
+            guard let mainScreen = self?.tabBarController!.viewControllers?[0] as? MainViewController else { return }
+            mainScreen.updateBGGraphSettings()
+        }
+        <<< StepperRow("overrideDisplayLocation") { row in
+            row.title = "Override BG Location"
+            row.cell.stepper.stepValue = 1
+            row.cell.stepper.minimumValue = Double(UserDefaultsRepository.minBGValue.value)
+            row.cell.stepper.maximumValue = Double(UserDefaultsRepository.minBGScale.value)
+            row.value = Double(UserDefaultsRepository.overrideDisplayLocation.value)
+            row.displayValueFor = { value in
+                guard let value = value else { return nil }
+                return bgUnits.toDisplayUnits(String(value))
+            }
+        }.onChange { [weak self] row in
+                guard let value = row.value else { return }
+                UserDefaultsRepository.overrideDisplayLocation.value = Float(value)
+            }
+            
+       +++ ButtonRow() {
+          $0.title = "DONE"
+       }.onCellSelection { (row, arg)  in
+          self.dismiss(animated:true, completion: nil)
+       }
+    }
+}

+ 118 - 0
LoopFollow/ViewControllers/InfoDisplaySettingsViewController.swift

@@ -0,0 +1,118 @@
+//
+//  InfoDisplaySettingsViewController.swift
+//  LoopFollow
+//
+//  Created by Jose Paredes on 7/16/20.
+//  Copyright © 2020 Jon Fawcett. All rights reserved.
+//
+
+import UIKit
+import Eureka
+import EventKit
+import EventKitUI
+
+class InfoDisplaySettingsViewController: FormViewController {
+
+    var infoNames:   [String] = []
+    var infoSort:    [Int]    = []
+    var infoVisible: [Bool]   = []
+    
+    override func viewDidLoad() {
+        print("Display Load")
+        super.viewDidLoad()
+        if UserDefaultsRepository.forceDarkMode.value {
+            overrideUserInterfaceStyle = .dark
+        }
+         
+        // get the info table
+        let userDefaults = UserDefaults.standard
+
+        // names
+        self.infoNames = userDefaults.stringArray(forKey:InfoNames) ?? [String]()
+        if(self.infoNames.count == 0) {
+            self.infoNames = DefaultInfoNames
+            userDefaults.set(self.infoNames, forKey:InfoNames)
+        }
+        
+        // sort
+        self.infoSort = userDefaults.array(forKey:InfoSort) as? [Int] ?? [Int]()
+        if(self.infoSort.count != self.infoNames.count) {
+            self.infoSort = []
+            for i in 0..<self.infoNames.count {
+                self.infoSort.append(i)
+            }
+            userDefaults.set(self.infoSort, forKey:InfoSort)
+        }
+        // visible
+        self.infoVisible = userDefaults.array(forKey:InfoVisible) as? [Bool] ?? [Bool]()
+        if(self.infoVisible.count != self.infoNames.count) {
+            self.infoVisible = []
+            for _ in 0..<self.infoNames.count {
+                self.infoVisible.append(true)
+            }
+            userDefaults.set(self.infoVisible, forKey:InfoVisible)
+        }
+        createForm()
+    }
+    
+    private func createForm() {
+        form
+        +++ MultivaluedSection(multivaluedOptions: .Reorder, header: "Information Display Settings", footer: "Arrage/Enable Information Desired") {
+        
+           // TODO: add the other display values
+           $0.tag = "InfoDisplay"
+           
+            for i in 0..<self.infoNames.count {
+              $0 <<< TextRow() { row in
+                if(self.infoVisible[self.infoSort[i]]) {
+                    row.title = "\u{2713}\t\(self.infoNames[self.infoSort[i]])"
+                 } else {
+                    row.title = "\u{2001}\t\(self.infoNames[self.infoSort[i]])"
+                 }
+              }.onCellSelection{(cell, row) in
+                let i = row.indexPath!.row
+                self.infoVisible[self.infoSort[i]] = !self.infoVisible[self.infoSort[i]]
+                
+                // save info visible
+                UserDefaults.standard.set(self.infoVisible, forKey:InfoVisible)
+                
+                self.tableView.reloadData()
+                
+                //print("\(row.title)")
+                //print("\(row.indexPath?.row)")
+              }.cellSetup { (cell, row) in
+                 cell.textField.isUserInteractionEnabled = false
+              }.cellUpdate{ (cell, row) in
+                 if(self.infoVisible[self.infoSort[i]]) {
+                    row.title = "\u{2713}\t\(self.infoNames[self.infoSort[i]])"
+                 } else {
+                    row.title = "\u{2001}\t\(self.infoNames[self.infoSort[i]])"
+                 }
+              }
+           }
+       }
+    
+       +++ ButtonRow() {
+          $0.title = "DONE"
+       }.onCellSelection { (row, arg)  in
+          self.dismiss(animated:true, completion: nil)
+       }
+    }
+    
+    override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
+        //let view = tableView
+        let sourceIndex = sourceIndexPath.row
+        let destIndex = destinationIndexPath.row
+        
+        // new sort
+        let tmpVal = self.infoSort[sourceIndex]
+        self.infoSort.remove(at:sourceIndex)
+        self.infoSort.insert(tmpVal, at:destIndex)
+       
+        // save to defaults
+        UserDefaults.standard.set(self.infoSort, forKey:InfoSort)
+        
+        print("Source Row: \(sourceIndexPath.row); Source Section: \(sourceIndexPath.section)")
+        print("Destination Row: \(destinationIndexPath.row); Destination Section: \(destinationIndexPath.section)")
+    }
+ }

+ 92 - 23
LoopFollow/ViewControllers/MainViewController.swift

@@ -11,6 +11,20 @@ import Charts
 import EventKit
 import ShareClient
 
+let DefaultInfoNames = [
+   "IOB",
+   "COB",
+   "Basal",
+   "Override",
+   "Battery",
+   "Pump",
+   "SAGE",
+   "CAGE",
+   "Rec. Bolus"
+]
+let InfoNames = "infoNames"
+let InfoSort = "infoSort"
+let InfoVisible = "infoVisible"
 
 class MainViewController: UIViewController, UITableViewDataSource, ChartViewDelegate, UNUserNotificationCenterDelegate {
     
@@ -32,13 +46,19 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
     @IBOutlet weak var statsAvgBG: UILabel!
     @IBOutlet weak var statsEstA1C: UILabel!
     @IBOutlet weak var statsStdDev: UILabel!
+
+        
+    // Data Table class
+    class infoData {
+        public var name: String
+        public var value: String
+        init(name: String, value: String) {
+            self.name = name
+            self.value = value
+        }
+
     @IBOutlet weak var serverText: UILabel!
-    
-    
-    // Data Table Struct
-    struct infoData {
-        var name: String
-        var value: String
+
     }
 
     // Variables for BG Charts
@@ -78,17 +98,11 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
     var calTimer = Timer()
     
     // Info Table Setup
-    // TODO: make this selectable
-    var tableData = [
-        infoData(name: "IOB", value: ""), //0
-        infoData(name: "COB", value: ""), //1
-        infoData(name: "Basal", value: ""), //2
-        infoData(name: "Override", value: ""), //3
-        infoData(name: "Battery", value: ""), //4
-        infoData(name: "Pump", value: ""), //5
-        infoData(name: "SAGE", value: ""), //6
-        infoData(name: "CAGE", value: "") //7
-    ]
+    var infoNames : [String] = []
+    var infoSort : [Int] = []
+    var infoVisible: [Bool] = []
+    var tableData : [infoData] = []
+    var derivedTableData: [infoData] = []
     
     var bgData: [ShareGlucoseData] = []
     var basalProfile: [basalProfileStruct] = []
@@ -123,7 +137,34 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
     
     override func viewDidLoad() {
         super.viewDidLoad()
+
+        
+        // table view
+        //infoTable.layer.borderColor = UIColor.darkGray.cgColor
+        //infoTable.layer.borderWidth = 1.0
+        //infoTable.layer.cornerRadius = 6
+        infoTable.rowHeight = 24
+        infoTable.dataSource = self
+        infoTable.tableFooterView = UIView(frame: .zero) // get rid of extra rows
+        infoTable.bounces = false
+        infoTable.addBorder(toSide: .Left, withColor: UIColor.darkGray.cgColor, andThickness: 2)
+      
+        // get the info table
+        let userDefaults = UserDefaults.standard
+
+        // names
+        self.infoNames = userDefaults.stringArray(forKey:InfoNames) ?? [String]()
+        if(self.infoNames.count == 0) {
+            self.infoNames = DefaultInfoNames
+            userDefaults.set(self.infoNames, forKey:InfoNames)
+        }
         
+        // initialize the tableData
+        self.tableData = []
+        for i in 0..<self.infoNames.count {
+            self.tableData.append(infoData(name:self.infoNames[i], value:""))
+        }
+      
         // TODO: look for username and password in the UserDefaultsRepo ?
         // TODO: need non-us server ?
         let shareUserName = UserDefaultsRepository.shareUserName.value
@@ -163,10 +204,7 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
         let notificationCenter = NotificationCenter.default
             notificationCenter.addObserver(self, selector: #selector(appMovedToBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
             notificationCenter.addObserver(self, selector: #selector(appCameToForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
-        
-        //Bind info data
-        infoTable.rowHeight = 25
-        infoTable.dataSource = self
+    
         
         // Setup the Graph
         if firstGraphLoad {
@@ -187,16 +225,47 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
         // set screen lock
         UIApplication.shared.isIdleTimerDisabled = UserDefaultsRepository.screenlockSwitchState.value;
         
+        // get the info table
+        let userDefaults = UserDefaults.standard
+
+        // sort
+        self.infoSort = userDefaults.array(forKey:InfoSort) as? [Int] ?? [Int]()
+        if(self.infoSort.count != self.infoNames.count) {
+            self.infoSort = []
+            for i in 0..<self.infoNames.count {
+                self.infoSort.append(i)
+            }
+            userDefaults.set(self.infoSort, forKey:InfoSort)
+        }
+        // visible
+        infoVisible = userDefaults.array(forKey:InfoVisible) as? [Bool] ?? [Bool]()
+        if(self.infoVisible.count != self.infoNames.count) {
+            self.infoVisible = []
+            for _ in 0..<self.infoNames.count {
+                self.infoVisible.append(true)
+            }
+            userDefaults.set(self.infoVisible, forKey:InfoVisible)
+        }
+        
+        self.derivedTableData = []
+        for i in 0..<self.tableData.count {
+            if(self.infoVisible[self.infoSort[i]]) {
+                self.derivedTableData.append(self.tableData[self.infoSort[i]])
+            }
+        }
+        infoTable.reloadData()
     }
     
     // Info Table Functions
     func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
-        return tableData.count
+        //return tableData.count
+        return derivedTableData.count
+        
     }
     
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let cell = tableView.dequeueReusableCell(withIdentifier: "LabelCell", for: indexPath)
-        let values = tableData[indexPath.row]
+        let values = derivedTableData[indexPath.row]
         cell.textLabel?.text = values.name
         cell.detailTextLabel?.text = values.value
         return cell

Plik diff jest za duży
+ 52 - 366
LoopFollow/ViewControllers/SettingsViewController.swift


Plik diff jest za duży
+ 93 - 0
LoopFollow/ViewControllers/WatchSettingsViewController.swift


+ 4 - 2
LoopFollow/repository/UserDefaults.swift

@@ -10,7 +10,9 @@ import Foundation
 import UIKit
 
 class UserDefaultsRepository {
-    
+
+    // DisplayValues total
+    static let infoDataTotal = UserDefaultsValue<Int>(key: "infoDataTotal", default: 0)
     
     //Nightscout Settings
     static let url = UserDefaultsValue<String>(key: "url", default: "")
@@ -23,6 +25,7 @@ class UserDefaultsRepository {
     static let shareServer = UserDefaultsValue<String>(key: "shareServer", default: "US")
     
     // Graph Settings
+    static let chartScaleX = UserDefaultsValue<Float>(key: "chartScaleX", default: 18.0)
     static let showDots = UserDefaultsValue<Bool>(key: "showDots", default: true)
     static let showLines = UserDefaultsValue<Bool>(key: "showLines", default: true)
     static let offsetCarbsBolus = UserDefaultsValue<Bool>(key: "offsetCarbsBolus", default: true)
@@ -92,7 +95,6 @@ class UserDefaultsRepository {
     static let alertCageInsertTime = UserDefaultsValue<TimeInterval>(key: "alertCageInsertTime", default: 0)
     static let alertSageInsertTime = UserDefaultsValue<TimeInterval>(key: "alertSageInsertTime", default: 0)
     
-    
     static let alertSnoozeAllTime = UserDefaultsValue<Date?>(key: "alertSnoozeAllTime", default: nil)
     static let alertSnoozeAllIsSnoozed = UserDefaultsValue<Bool>(key: "alertSnoozeAllIsSnoozed", default: false)