forked from LoopKit/Loop
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCGMStatusHUDViewModel.swift
More file actions
191 lines (156 loc) · 6.91 KB
/
CGMStatusHUDViewModel.swift
File metadata and controls
191 lines (156 loc) · 6.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//
// CGMStatusHUDViewModel.swift
// LoopUI
//
// Created by Nathaniel Hamming on 2020-06-24.
// Copyright © 2020 LoopKit Authors. All rights reserved.
//
import HealthKit
import LoopKit
public class CGMStatusHUDViewModel {
static let staleGlucoseRepresentation: String = NSLocalizedString("– – –", comment: "No glucose value representation (3 dashes for mg/dL)")
var trend: GlucoseTrend?
var unitsString: String = "–"
var glucoseValueString: String = CGMStatusHUDViewModel.staleGlucoseRepresentation
var accessibilityString: String = ""
var glucoseValueTintColor: UIColor = .label
var glucoseTrendTintColor: UIColor = .glucoseTintColor
var glucoseTrendIcon: UIImage? {
guard let manualGlucoseTrendIconOverride = manualGlucoseTrendIconOverride else {
return trend?.image
}
return manualGlucoseTrendIconOverride
}
private var glucoseValueCurrent: Bool {
guard let isStaleAt = isStaleAt else { return true }
return Date() < isStaleAt
}
private var isManualGlucose: Bool = false
private var isManualGlucoseCurrent: Bool {
return isManualGlucose && glucoseValueCurrent
}
var manualGlucoseTrendIconOverride: UIImage?
private var storedStatusHighlight: DeviceStatusHighlight?
var statusHighlight: DeviceStatusHighlight? {
get {
guard !isManualGlucoseCurrent else {
// if there is a current manual glucose, don't provide the stored status highlight
return nil
}
return storedStatusHighlight
}
set {
storedStatusHighlight = newValue
if isManualGlucoseCurrent {
// If there is a current manual glucose, it displays the current status highlight icon
setManualGlucoseTrendIconOverride()
}
if let localizedMessage = storedStatusHighlight?.localizedMessage.replacingOccurrences(of: "\n", with: " "),
let statusStateMessage = storedStatusHighlight?.state.localizedDescription
{
accessibilityString = localizedMessage + ", " + statusStateMessage
}
}
}
var isVisible: Bool = true {
didSet {
if oldValue != isVisible {
if !isVisible {
stalenessTimer?.invalidate()
stalenessTimer = nil
} else {
startStalenessTimerIfNeeded()
}
}
}
}
private var stalenessTimer: Timer?
private var isStaleAt: Date? {
didSet {
if oldValue != isStaleAt {
stalenessTimer?.invalidate()
stalenessTimer = nil
}
}
}
private func startStalenessTimerIfNeeded() {
if let fireDate = isStaleAt,
isVisible,
stalenessTimer == nil
{
stalenessTimer = Timer(fire: fireDate, interval: 0, repeats: false) { (_) in
self.displayStaleGlucoseValue()
self.staleGlucoseValueHandler()
}
RunLoop.main.add(stalenessTimer!, forMode: .default)
}
}
private lazy var timeFormatter = DateFormatter(timeStyle: .short)
var staleGlucoseValueHandler: () -> Void
init(staleGlucoseValueHandler: @escaping () -> Void) {
self.staleGlucoseValueHandler = staleGlucoseValueHandler
}
func setGlucoseQuantity(_ glucoseQuantity: Double,
at glucoseStartDate: Date,
unit: HKUnit,
staleGlucoseAge: TimeInterval,
glucoseDisplay: GlucoseDisplayable?,
wasUserEntered: Bool,
isDisplayOnly: Bool)
{
var accessibilityStrings = [String]()
// reset state
manualGlucoseTrendIconOverride = nil
trend = nil
let time = timeFormatter.string(from: glucoseStartDate)
isStaleAt = glucoseStartDate.addingTimeInterval(staleGlucoseAge)
glucoseValueTintColor = glucoseDisplay?.glucoseRangeCategory?.glucoseColor ?? .label
let numberFormatter = NumberFormatter.glucoseFormatter(for: unit)
if let valueString = numberFormatter.string(from: glucoseQuantity) {
if glucoseValueCurrent {
startStalenessTimerIfNeeded()
switch glucoseDisplay?.glucoseRangeCategory {
case .some(.belowRange):
glucoseValueString = LocalizedString("LOW", comment: "String displayed instead of a glucose value below the CGM range")
case .some(.aboveRange):
glucoseValueString = LocalizedString("HIGH", comment: "String displayed instead of a glucose value above the CGM range")
default:
glucoseValueString = valueString
}
} else {
displayStaleGlucoseValue()
}
accessibilityStrings.append(String(format: LocalizedString("%1$@ at %2$@", comment: "Accessbility format value describing glucose: (1: glucose number)(2: glucose time)"), valueString, time))
}
// Only a user-entered glucose value that is *not* display-only (i.e. a calibration) is considered a manual glucose entry.
isManualGlucose = wasUserEntered && !isDisplayOnly
if isManualGlucoseCurrent {
// a manual glucose value presents any status highlight icon instead of a trend icon
setManualGlucoseTrendIconOverride()
} else if let trend = glucoseDisplay?.trendType, glucoseValueCurrent {
self.trend = trend
glucoseTrendTintColor = glucoseDisplay?.glucoseRangeCategory?.trendColor ?? .glucoseTintColor
accessibilityStrings.append(trend.localizedDescription)
} else {
glucoseTrendTintColor = .glucoseTintColor
}
if let statusStateMessage = storedStatusHighlight?.state.localizedDescription,
let localizedMessage = storedStatusHighlight?.localizedMessage.replacingOccurrences(of: "\n", with: " ")
{
accessibilityStrings.append(localizedMessage + ", " + statusStateMessage)
}
unitsString = unit.localizedShortUnitString
accessibilityString = accessibilityStrings.joined(separator: ", ")
}
func displayStaleGlucoseValue() {
glucoseValueString = CGMStatusHUDViewModel.staleGlucoseRepresentation
glucoseValueTintColor = .label
trend = nil
glucoseTrendTintColor = .glucoseTintColor
manualGlucoseTrendIconOverride = nil
}
func setManualGlucoseTrendIconOverride() {
manualGlucoseTrendIconOverride = storedStatusHighlight?.image
glucoseTrendTintColor = storedStatusHighlight?.state.color ?? .glucoseTintColor
}
}