Explorar o código

manage the display of Issue provided by devicePump with UserNotification and store the alert in a alertStore array

avouspierre %!s(int64=3) %!d(string=hai) anos
pai
achega
e5e589251a

+ 20 - 20
FreeAPS.xcodeproj/project.pbxproj

@@ -2589,7 +2589,7 @@
 				BUNDLE_IDENTIFIER = "$(BUNDLE_IDENTIFIER)";
 				CODE_SIGN_ENTITLEMENTS = FreeAPS/Resources/FreeAPS.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 1;
+				CURRENT_PROJECT_VERSION = 0.2.7;
 				DEVELOPMENT_ASSET_PATHS = "";
 				DEVELOPMENT_TEAM = "${DEVELOPER_TEAM}";
 				ENABLE_PREVIEWS = YES;
@@ -2603,7 +2603,7 @@
 					"$(inherited)",
 					"$(SDKROOT)/usr/lib/swift",
 				);
-				MARKETING_VERSION = "$(CURRENT_PROJECT_VERSION)";
+				MARKETING_VERSION = 0.2;
 				OTHER_LDFLAGS = (
 					"-weak_framework",
 					CoreNFC,
@@ -2625,7 +2625,7 @@
 				BUNDLE_IDENTIFIER = "$(BUNDLE_IDENTIFIER)";
 				CODE_SIGN_ENTITLEMENTS = FreeAPS/Resources/FreeAPS.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 1;
+				CURRENT_PROJECT_VERSION = 0.2.7;
 				DEVELOPMENT_ASSET_PATHS = "";
 				DEVELOPMENT_TEAM = "${DEVELOPER_TEAM}";
 				ENABLE_PREVIEWS = YES;
@@ -2639,7 +2639,7 @@
 					"$(inherited)",
 					"$(SDKROOT)/usr/lib/swift",
 				);
-				MARKETING_VERSION = "$(CURRENT_PROJECT_VERSION)";
+				MARKETING_VERSION = 0.2;
 				OTHER_LDFLAGS = (
 					"-weak_framework",
 					CoreNFC,
@@ -2662,14 +2662,14 @@
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
 				CODE_SIGN_ENTITLEMENTS = FreeAPSWatch/FreeAPSWatch.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = "$(BUILD_VERSION)";
+				CURRENT_PROJECT_VERSION = 0.2.7;
 				DEVELOPMENT_TEAM = "${DEVELOPER_TEAM}";
 				GENERATE_INFOPLIST_FILE = YES;
 				IBSC_MODULE = FreeAPSWatch_WatchKit_Extension;
-				INFOPLIST_KEY_CFBundleDisplayName = "$(APP_DISPLAY_NAME)";
+				INFOPLIST_KEY_CFBundleDisplayName = "FreeAPS X";
 				INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
-				INFOPLIST_KEY_WKCompanionAppBundleIdentifier = "$(BUNDLE_IDENTIFIER)";
-				MARKETING_VERSION = 1;
+				INFOPLIST_KEY_WKCompanionAppBundleIdentifier = ru.artpancreas.HJS9L9WUYM.FreeAPS;
+				MARKETING_VERSION = 0.2;
 				PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_IDENTIFIER).watchkitapp";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = watchos;
@@ -2692,14 +2692,14 @@
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
 				CODE_SIGN_ENTITLEMENTS = FreeAPSWatch/FreeAPSWatch.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = "$(BUILD_VERSION)";
+				CURRENT_PROJECT_VERSION = 0.2.7;
 				DEVELOPMENT_TEAM = "${DEVELOPER_TEAM}";
 				GENERATE_INFOPLIST_FILE = YES;
 				IBSC_MODULE = FreeAPSWatch_WatchKit_Extension;
-				INFOPLIST_KEY_CFBundleDisplayName = "$(APP_DISPLAY_NAME)";
+				INFOPLIST_KEY_CFBundleDisplayName = "FreeAPS X";
 				INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
-				INFOPLIST_KEY_WKCompanionAppBundleIdentifier = "$(BUNDLE_IDENTIFIER)";
-				MARKETING_VERSION = 1;
+				INFOPLIST_KEY_WKCompanionAppBundleIdentifier = ru.artpancreas.HJS9L9WUYM.FreeAPS;
+				MARKETING_VERSION = 0.2;
 				PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_IDENTIFIER).watchkitapp";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = watchos;
@@ -2720,14 +2720,14 @@
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
 				CODE_SIGN_ENTITLEMENTS = "FreeAPSWatch WatchKit Extension/FreeAPSWatch WatchKit Extension.entitlements";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = "$(BUILD_VERSION)";
+				CURRENT_PROJECT_VERSION = 0.2.7;
 				DEVELOPMENT_ASSET_PATHS = "\"FreeAPSWatch WatchKit Extension/Preview Content\"";
 				DEVELOPMENT_TEAM = "${DEVELOPER_TEAM}";
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
 				INFOPLIST_FILE = "FreeAPSWatch WatchKit Extension/Info.plist";
-				INFOPLIST_KEY_CFBundleDisplayName = "$(APP_DISPLAY_NAME) WatchKit Extension";
-				INFOPLIST_KEY_CLKComplicationPrincipalClass = "$(PRODUCT_MODULE_NAME).ComplicationController";
+				INFOPLIST_KEY_CFBundleDisplayName = "FreeAPS X WatchKit Extension";
+				INFOPLIST_KEY_CLKComplicationPrincipalClass = FreeAPSWatch_WatchKit_Extension.ComplicationController;
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				INFOPLIST_KEY_WKRunsIndependentlyOfCompanionApp = NO;
 				LD_RUNPATH_SEARCH_PATHS = (
@@ -2735,7 +2735,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 1;
+				MARKETING_VERSION = 0.2;
 				PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_IDENTIFIER).watchkitapp.watchkitextension";
 				PRODUCT_NAME = "${TARGET_NAME}";
 				SDKROOT = watchos;
@@ -2756,14 +2756,14 @@
 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
 				CODE_SIGN_ENTITLEMENTS = "FreeAPSWatch WatchKit Extension/FreeAPSWatch WatchKit Extension.entitlements";
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = "$(BUILD_VERSION)";
+				CURRENT_PROJECT_VERSION = 0.2.7;
 				DEVELOPMENT_ASSET_PATHS = "\"FreeAPSWatch WatchKit Extension/Preview Content\"";
 				DEVELOPMENT_TEAM = "${DEVELOPER_TEAM}";
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
 				INFOPLIST_FILE = "FreeAPSWatch WatchKit Extension/Info.plist";
-				INFOPLIST_KEY_CFBundleDisplayName = "$(APP_DISPLAY_NAME) WatchKit Extension";
-				INFOPLIST_KEY_CLKComplicationPrincipalClass = "$(PRODUCT_MODULE_NAME).ComplicationController";
+				INFOPLIST_KEY_CFBundleDisplayName = "FreeAPS X WatchKit Extension";
+				INFOPLIST_KEY_CLKComplicationPrincipalClass = FreeAPSWatch_WatchKit_Extension.ComplicationController;
 				INFOPLIST_KEY_NSHumanReadableCopyright = "";
 				INFOPLIST_KEY_WKRunsIndependentlyOfCompanionApp = NO;
 				LD_RUNPATH_SEARCH_PATHS = (
@@ -2771,7 +2771,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 1;
+				MARKETING_VERSION = 0.2;
 				PRODUCT_BUNDLE_IDENTIFIER = "$(BUNDLE_IDENTIFIER).watchkitapp.watchkitextension";
 				PRODUCT_NAME = "${TARGET_NAME}";
 				SDKROOT = watchos;

+ 47 - 26
FreeAPS/Sources/APS/DeviceDataManager.swift

@@ -23,6 +23,7 @@ protocol DeviceDataManager: GlucoseSource {
     var pumpExpiresAtDate: CurrentValueSubject<Date?, Never> { get }
     func heartbeat(date: Date)
     func createBolusProgressReporter() -> DoseProgressReporter?
+    var alertStore: [Alert] { get }
 }
 
 private let staticPumpManagers: [PumpManagerUI.Type] = [
@@ -62,6 +63,7 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
     let bolusTrigger = PassthroughSubject<Bool, Never>()
     let errorSubject = PassthroughSubject<Error, Never>()
     let pumpNewStatus = PassthroughSubject<Void, Never>()
+    var alertStore: [Alert]
     @SyncAccess private var pumpUpdateCancellable: AnyCancellable?
     private var pumpUpdatePromise: Future<Bool, Never>.Promise?
     @SyncAccess var loopInProgress: Bool = false
@@ -108,6 +110,7 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
     let pumpName = CurrentValueSubject<String, Never>("Pump")
 
     init(resolver: Resolver) {
+        alertStore = []
         injectServices(resolver)
         setupPumpManager()
         UIDevice.current.isBatteryMonitoringEnabled = true
@@ -422,13 +425,31 @@ extension BaseDeviceDataManager: PumpManagerDelegate {
 // MARK: - DeviceManagerDelegate
 
 extension BaseDeviceDataManager: DeviceManagerDelegate {
-    func issueAlert(_: Alert) {}
+    func issueAlert(_ alert: Alert) {
+        if !alertStore.contains(where: { $0.identifier.alertIdentifier == alert.identifier.alertIdentifier }) {
+            alertStore.append(alert)
+            broadcaster.notify(pumpNotificationObserver.self, on: processQueue) {
+                $0.pumpNotification(alert: alert)
+            }
+        }
+    }
 
-    func retractAlert(identifier _: Alert.Identifier) {}
+    func retractAlert(identifier: Alert.Identifier) {
+        if let idx = alertStore.firstIndex(where: { $0.identifier.alertIdentifier == identifier.alertIdentifier }) {
+            alertStore.remove(at: idx)
+            broadcaster.notify(pumpNotificationObserver.self, on: processQueue) {
+                $0.pumpRemoveNotification()
+            }
+        }
+    }
 
-    func doesIssuedAlertExist(identifier _: Alert.Identifier, completion _: @escaping (Result<Bool, Error>) -> Void) {}
+    func doesIssuedAlertExist(identifier _: Alert.Identifier, completion _: @escaping (Result<Bool, Error>) -> Void) {
+        debug(.deviceManager, "doesIssueAlertExist")
+    }
 
-    func lookupAllUnretracted(managerIdentifier _: String, completion _: @escaping (Result<[PersistedAlert], Error>) -> Void) {}
+    func lookupAllUnretracted(managerIdentifier _: String, completion _: @escaping (Result<[PersistedAlert], Error>) -> Void) {
+        debug(.deviceManager, "lookupAllUnretracted")
+    }
 
     func lookupAllUnacknowledgedUnretracted(
         managerIdentifier _: String,
@@ -437,28 +458,28 @@ extension BaseDeviceDataManager: DeviceManagerDelegate {
 
     func recordRetractedAlert(_: Alert, at _: Date) {}
 
-    func scheduleNotification(
-        for _: DeviceManager,
-        identifier: String,
-        content: UNNotificationContent,
-        trigger: UNNotificationTrigger?
-    ) {
-        let request = UNNotificationRequest(
-            identifier: identifier,
-            content: content,
-            trigger: trigger
-        )
-
-        DispatchQueue.main.async {
-            UNUserNotificationCenter.current().add(request)
-        }
-    }
-
-    func clearNotification(for _: DeviceManager, identifier: String) {
-        DispatchQueue.main.async {
-            UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [identifier])
-        }
-    }
+//    func scheduleNotification(
+//        for _: DeviceManager,
+//        identifier: String,
+//        content: UNNotificationContent,
+//        trigger: UNNotificationTrigger?
+//    ) {
+//        let request = UNNotificationRequest(
+//            identifier: identifier,
+//            content: content,
+//            trigger: trigger
+//        )
+//
+//        DispatchQueue.main.async {
+//            UNUserNotificationCenter.current().add(request)
+//        }
+//    }
+//
+//    func clearNotification(for _: DeviceManager, identifier: String) {
+//        DispatchQueue.main.async {
+//            UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [identifier])
+//        }
+//    }
 
     func removeNotificationRequests(for _: DeviceManager, identifiers: [String]) {
         DispatchQueue.main.async {

+ 41 - 0
FreeAPS/Sources/Services/UserNotifiactions/UserNotificationsManager.swift

@@ -1,5 +1,7 @@
 import AudioToolbox
 import Foundation
+import LoopKit
+import SwiftUI
 import Swinject
 import UIKit
 import UserNotifications
@@ -22,6 +24,11 @@ protocol BolusFailureObserver {
     func bolusDidFail()
 }
 
+protocol pumpNotificationObserver {
+    func pumpNotification(alert: LoopKit.Alert)
+    func pumpRemoveNotification()
+}
+
 final class BaseUserNotificationsManager: NSObject, UserNotificationsManager, Injectable {
     private enum Identifier: String {
         case glucocoseNotification = "FreeAPS.glucoseNotification"
@@ -29,6 +36,7 @@ final class BaseUserNotificationsManager: NSObject, UserNotificationsManager, In
         case noLoopFirstNotification = "FreeAPS.noLoopFirstNotification"
         case noLoopSecondNotification = "FreeAPS.noLoopSecondNotification"
         case bolusFailedNotification = "FreeAPS.bolusFailedNotification"
+        case pumpNotification = "FreeAPS.pumpNotification"
     }
 
     @Injected() private var settingsManager: SettingsManager!
@@ -36,6 +44,7 @@ final class BaseUserNotificationsManager: NSObject, UserNotificationsManager, In
     @Injected() private var glucoseStorage: GlucoseStorage!
     @Injected() private var apsManager: APSManager!
     @Injected() private var router: Router!
+
     @Injected(as: FetchGlucoseManager.self) private var sourceInfoProvider: SourceInfoProvider!
 
     @Persisted(key: "UserNotificationsManager.snoozeUntilDate") private var snoozeUntilDate: Date = .distantPast
@@ -50,6 +59,7 @@ final class BaseUserNotificationsManager: NSObject, UserNotificationsManager, In
         broadcaster.register(GlucoseObserver.self, observer: self)
         broadcaster.register(SuggestionObserver.self, observer: self)
         broadcaster.register(BolusFailureObserver.self, observer: self)
+        broadcaster.register(pumpNotificationObserver.self, observer: self)
 
         requestNotificationPermissionsIfNeeded()
         sendGlucoseNotification()
@@ -394,6 +404,37 @@ extension BaseUserNotificationsManager: GlucoseObserver {
     }
 }
 
+extension BaseUserNotificationsManager: pumpNotificationObserver {
+    func pumpNotification(alert: LoopKit.Alert) {
+        ensureCanSendNotification {
+            let contentAlert = alert.foregroundContent!
+            let content = UNMutableNotificationContent()
+            content.title = contentAlert.title
+            content.body = contentAlert.body
+            content.sound = .default
+            self.addRequest(
+                identifier: .pumpNotification,
+                content: content,
+                deleteOld: true,
+                trigger: nil
+            )
+            self.apsManager.pumpManager?.acknowledgeAlert(alertIdentifier: alert.identifier.alertIdentifier) { error in
+                if let error = error {
+                    debug(.deviceManager, "acknowledge not succeeded with error \(error.localizedDescription)")
+                }
+            }
+        }
+    }
+
+    func pumpRemoveNotification() {
+        let identifier: Identifier = .pumpNotification
+        DispatchQueue.main.async {
+            self.center.removeDeliveredNotifications(withIdentifiers: [identifier.rawValue])
+            self.center.removePendingNotificationRequests(withIdentifiers: [identifier.rawValue])
+        }
+    }
+}
+
 extension BaseUserNotificationsManager: SuggestionObserver {
     func suggestionDidUpdate(_ suggestion: Suggestion) {
         guard let carndRequired = suggestion.carbsReq else { return }