initial heartbeat UI implementation for data source

This commit is contained in:
Paul Plant 2024-03-12 14:54:59 +00:00
parent 4f0b9589e6
commit ecb3539645
12 changed files with 83 additions and 9 deletions

View File

@ -1,4 +1,5 @@
{
"originHash" : "20a19467224593c1dcb0789843c73f4580a23d8227b26f5a2c54d6c62c75927e",
"pins" : [
{
"identity" : "actionclosurable",
@ -37,5 +38,5 @@
}
}
],
"version" : 2
"version" : 3
}

View File

@ -74,6 +74,8 @@ class DexcomG7HeartbeatBluetoothTransmitter: BluetoothTransmitter {
self.bluetoothTransmitterDelegate?.heartBeat()
lastHeartBeatTimeStamp = Date()
UserDefaults.standard.lastHeartBeatTimeStamp = lastHeartBeatTimeStamp
}

View File

@ -67,6 +67,8 @@ class Libre3HeartBeatBluetoothTransmitter: BluetoothTransmitter {
lastHeartBeatTimeStamp = Date()
UserDefaults.standard.lastHeartBeatTimeStamp = lastHeartBeatTimeStamp
}
}
@ -85,6 +87,9 @@ class Libre3HeartBeatBluetoothTransmitter: BluetoothTransmitter {
lastHeartBeatTimeStamp = Date()
UserDefaults.standard.lastHeartBeatTimeStamp = lastHeartBeatTimeStamp
print("heatbeat timestamp: \(lastHeartBeatTimeStamp)")
}
}

View File

@ -68,6 +68,8 @@ class OmniPodHeartBeatTransmitter: BluetoothTransmitter {
lastHeartBeatTimeStamp = Date()
UserDefaults.standard.lastHeartBeatTimeStamp = lastHeartBeatTimeStamp
}
}
@ -85,6 +87,8 @@ class OmniPodHeartBeatTransmitter: BluetoothTransmitter {
lastHeartBeatTimeStamp = Date()
UserDefaults.standard.lastHeartBeatTimeStamp = lastHeartBeatTimeStamp
}
}

View File

@ -394,6 +394,10 @@ extension UserDefaults {
/// - stored as data as read from transmitter
case librePatchInfo = "librePatchInfo"
// heartbeat
/// the last heartbeat connection timestamp
case lastHeartBeatTimeStamp = "lastHeartBeatTimeStamp"
}
@ -2191,6 +2195,19 @@ extension UserDefaults {
}
}
// MARK: - Heartbeat
/// timestamp of last successful connection to follower service
@objc dynamic var lastHeartBeatTimeStamp: Date? {
get {
return object(forKey: Key.lastHeartBeatTimeStamp.rawValue) as? Date
}
set {
set(newValue, forKey: Key.lastHeartBeatTimeStamp.rawValue)
}
}
}

View File

@ -719,8 +719,8 @@ public class AlertManager:NSObject {
return true
} else {
if !UserDefaults.standard.isMaster && UserDefaults.standard.followerBackgroundKeepAliveType == .disabled {
trace("in checkAlert, there's no need to raise alert %{public}@ because we're in follower mode and keep-alive is disabled", log: self.log, category: ConstantsLog.categoryAlertManager, type: .info, alertKind.descriptionForLogging())
if !UserDefaults.standard.isMaster && !UserDefaults.standard.followerBackgroundKeepAliveType.shouldKeepAlive {
trace("in checkAlert, there's no need to raise alert %{public}@ because we're in follower mode and keep-alive is: %[public]@", log: self.log, category: ConstantsLog.categoryAlertManager, type: .info, alertKind.descriptionForLogging(), UserDefaults.standard.followerBackgroundKeepAliveType.description)
} else {
trace("in checkAlert, there's no need to raise alert %{public}@", log: self.log, category: ConstantsLog.categoryAlertManager, type: .info, alertKind.descriptionForLogging())
}

View File

@ -20,6 +20,7 @@ public enum FollowerBackgroundKeepAliveType: Int, CaseIterable {
case disabled = 0
case normal = 1
case aggressive = 2
case heartbeat = 3
var description: String {
switch self {
@ -29,6 +30,8 @@ public enum FollowerBackgroundKeepAliveType: Int, CaseIterable {
return Texts_SettingsView.followerKeepAliveTypeNormal
case .aggressive:
return Texts_SettingsView.followerKeepAliveTypeAggressive
case .heartbeat:
return Texts_SettingsView.followerKeepAliveTypeHeartbeat
}
}
@ -40,6 +43,19 @@ public enum FollowerBackgroundKeepAliveType: Int, CaseIterable {
return 1
case .aggressive:
return 2
case .heartbeat:
return 3
}
}
// return true if in follower mode and if the keep-alive type should provoke a background keep-alive action
// basically if not .disabled and if not .heartbeat
var shouldKeepAlive: Bool {
switch self {
case .disabled, .heartbeat:
return false
default:
return true
}
}
@ -52,6 +68,8 @@ public enum FollowerBackgroundKeepAliveType: Int, CaseIterable {
return UIImage(systemName: "n.circle") ?? UIImage()
case .aggressive:
return UIImage(systemName: "a.circle") ?? UIImage()
case .heartbeat:
return UIImage(systemName: "heart.circle") ?? UIImage()
}
}
@ -64,6 +82,8 @@ public enum FollowerBackgroundKeepAliveType: Int, CaseIterable {
return Image(systemName: "n.circle")
case .aggressive:
return Image(systemName: "a.circle")
case .heartbeat:
return Image(systemName: "heart.circle")
}
}

View File

@ -758,9 +758,9 @@ class LibreLinkUpFollowManager: NSObject {
private func enableSuspensionPrevention() {
// if keep-alive is disabled or if using a heartbeat, then just return and do nothing
if UserDefaults.standard.followerBackgroundKeepAliveType == .disabled || UserDefaults.standard.followerBackgroundKeepAliveType == {
if !UserDefaults.standard.followerBackgroundKeepAliveType.shouldKeepAlive {
print("not enabling suspension prevention as keep-alive is disabled")
print("not enabling suspension prevention as keep-alive type is: \(UserDefaults.standard.followerBackgroundKeepAliveType.description)")
return
@ -803,7 +803,8 @@ class LibreLinkUpFollowManager: NSObject {
if !UserDefaults.standard.isMaster && UserDefaults.standard.followerDataSourceType == .libreLinkUp && UserDefaults.standard.libreLinkUpEmail != nil && UserDefaults.standard.libreLinkUpPassword != nil {
// this will enable the suspension prevention sound playing if background keep-alive is enabled
if UserDefaults.standard.followerBackgroundKeepAliveType != .disabled {
// (i.e. not disabled and not using a heartbeat)
if UserDefaults.standard.followerBackgroundKeepAliveType.shouldKeepAlive {
enableSuspensionPrevention()
} else {
disableSuspensionPrevention()

View File

@ -365,8 +365,8 @@ class NightScoutFollowManager: NSObject {
/// launches timer that will regular play sound - this will be played only when app goes to background and only if the user wants to keep the app alive
private func enableSuspensionPrevention() {
// if keep-alive is disabled, then just return and do nothing
if UserDefaults.standard.followerBackgroundKeepAliveType == .disabled {
// if keep-alive is not needed, then just return and do nothing
if !UserDefaults.standard.followerBackgroundKeepAliveType.shouldKeepAlive {
print("not enabling suspension prevention as keep-alive is disabled")

View File

@ -128,6 +128,10 @@ class Texts_SettingsView {
return NSLocalizedString("settingsviews_followerKeepAliveTypeAggressive", tableName: filename, bundle: Bundle.main, value: "Aggressive", comment: "data source settings, keep-alive mode is set to aggressive")
}()
static let followerKeepAliveTypeHeartbeat: String = {
return NSLocalizedString("settingsviews_followerKeepAliveTypeHeartbeat", tableName: filename, bundle: Bundle.main, value: "Heartbeat ♥", comment: "data source settings, keep-alive mode is set to use an external heartbeat")
}()
static let followerKeepAliveTypeDisabledMessage: String = {
return NSLocalizedString("settingsviews_followerKeepAliveTypeDisabledMessage", tableName: filename, bundle: Bundle.main, value: "Background keep-alive is disabled.\n\nWhen the app is not on screen, no alarms, app badges, notifications or BG updates will take place.\n\nThe app will remain sleeping until you open it again.\n\nThis mode has very little impact on the battery of your device.", comment: "data source settings, keep-alive mode is set to disabled")
}()
@ -140,6 +144,10 @@ class Texts_SettingsView {
return NSLocalizedString("settingsviews_followerKeepAliveTypeAggressiveMessage", tableName: filename, bundle: Bundle.main, value: "Background keep-alive is set to aggressive.\n\nWhen the app is not on screen, we will aggressively attempt to keep it running for you in the background so that BG updates are received and alarms can be triggered.\n\nThis mode has a very noticeable impact on the battery of your device and should only be used if absolutely necessary.", comment: "data source settings, keep-alive mode is set to aggressive")
}()
static let followerKeepAliveTypeHeartbeatMessage: String = {
return NSLocalizedString("settingsviews_followerKeepAliveTypeHeartbeatMessage", tableName: filename, bundle: Bundle.main, value: "Background keep-alive is set to use an external heartbeat. ❤️\n\nWhen the app is not on screen, the external heartbeat will wake it up in the background so that BG updates are received and alarms can be triggered.\n\nMake sure you add a heartbeat device in the Bluetooth screen.\n\nThis mode has very little impact on the battery of your device but will only work if a valid heartbeat is running.", comment: "data source settings, keep-alive mode is set to use an external heartbeat")
}()
static let followerPatientName: String = {
return NSLocalizedString("settingsviews_followerPatientName", tableName: filename, bundle: Bundle.main, value: "Patient Name", comment: "data source settings, the name of the person we are following")
}()

View File

@ -919,6 +919,9 @@ final class RootViewController: UIViewController, ObservableObject {
// add observer for showAppleWatchDebug, to reload the watch app view if the value changes
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.showAppleWatchDebug.rawValue, options: .new, context: nil)
// add observer for the last heartbeat timestamp in order to update the UI
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.lastHeartBeatTimeStamp.rawValue, options: .new, context: nil)
// setup delegate for UNUserNotificationCenter
UNUserNotificationCenter.current().delegate = self
@ -1692,8 +1695,10 @@ final class RootViewController: UIViewController, ObservableObject {
}
case UserDefaults.Key.showAppleWatchDebug:
watchManager?.updateWatchApp()
case UserDefaults.Key.lastHeartBeatTimeStamp:
updateDataSourceInfo(animate: false)
default:
break
@ -3237,6 +3242,15 @@ final class RootViewController: UIViewController, ObservableObject {
// let's go through the specific cases for follower modes
if !isMaster {
// first set the heartbeat icon color depending on when the last heartbeat was received
if UserDefaults.standard.followerBackgroundKeepAliveType == .heartbeat {
if let lastHeartBeatTimeStamp = UserDefaults.standard.lastHeartBeatTimeStamp, lastHeartBeatTimeStamp < Date().addingTimeInterval(-30) {
dataSourceKeepAliveImageOutlet.tintColor = .systemRed
} else {
dataSourceKeepAliveImageOutlet.tintColor = .systemGreen
}
}
dataSourceLabelOutlet.text = UserDefaults.standard.followerDataSourceType.fullDescription
switch UserDefaults.standard.followerDataSourceType {

View File

@ -249,6 +249,8 @@ class SettingsViewDataSourceSettingsViewModel: NSObject, SettingsViewModelProtoc
message += Texts_SettingsView.followerKeepAliveTypeNormalMessage
case .aggressive:
message += Texts_SettingsView.followerKeepAliveTypeAggressiveMessage
case .heartbeat:
message += Texts_SettingsView.followerKeepAliveTypeHeartbeatMessage
}
self.callMessageHandlerInMainThread(title: Texts_SettingsView.labelfollowerKeepAliveType, message: message)