Explorar el Código

Detection of inactive state

Jonas Björkert hace 2 años
padre
commit
47e7493f78

+ 4 - 0
LoopFollow.xcodeproj/project.pbxproj

@@ -22,6 +22,7 @@
 		DD493AE72ACF23CF009A6922 /* DeviceStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD493AE62ACF23CF009A6922 /* DeviceStatus.swift */; };
 		DD493AE92ACF2445009A6922 /* BGData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD493AE82ACF2445009A6922 /* BGData.swift */; };
 		DD608A082C1F584900F91132 /* DeviceStatusLoop.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD608A072C1F584900F91132 /* DeviceStatusLoop.swift */; };
+		DD608A0C2C27415C00F91132 /* BackgroundAlertManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD608A0B2C27415C00F91132 /* BackgroundAlertManager.swift */; };
 		DD6A935E2BFA6FA2003FFB8E /* DeviceStatusOpenAPS.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD6A935D2BFA6FA2003FFB8E /* DeviceStatusOpenAPS.swift */; };
 		DD7E19842ACDA50C00DBD158 /* Overrides.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7E19832ACDA50C00DBD158 /* Overrides.swift */; };
 		DD7E19862ACDA59700DBD158 /* BGCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7E19852ACDA59700DBD158 /* BGCheck.swift */; };
@@ -214,6 +215,7 @@
 		DD493AE62ACF23CF009A6922 /* DeviceStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceStatus.swift; sourceTree = "<group>"; };
 		DD493AE82ACF2445009A6922 /* BGData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGData.swift; sourceTree = "<group>"; };
 		DD608A072C1F584900F91132 /* DeviceStatusLoop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceStatusLoop.swift; sourceTree = "<group>"; };
+		DD608A0B2C27415C00F91132 /* BackgroundAlertManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundAlertManager.swift; sourceTree = "<group>"; };
 		DD6A935D2BFA6FA2003FFB8E /* DeviceStatusOpenAPS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceStatusOpenAPS.swift; sourceTree = "<group>"; };
 		DD7E19832ACDA50C00DBD158 /* Overrides.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Overrides.swift; sourceTree = "<group>"; };
 		DD7E19852ACDA59700DBD158 /* BGCheck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGCheck.swift; sourceTree = "<group>"; };
@@ -501,6 +503,7 @@
 				FC1BDD2A24A22650001B652C /* Stats.swift */,
 				FC1BDD2C24A23204001B652C /* StatsView.swift */,
 				FCA2DDE52501095000254A8C /* Timers.swift */,
+				DD608A0B2C27415C00F91132 /* BackgroundAlertManager.swift */,
 			);
 			path = Controllers;
 			sourceTree = "<group>";
@@ -1025,6 +1028,7 @@
 				FCC0FAC224922A22003E610E /* DictionaryKeyPath.swift in Sources */,
 				DD493AD72ACF2139009A6922 /* SuspendPump.swift in Sources */,
 				FC9788182485969B00A7906C /* AppDelegate.swift in Sources */,
+				DD608A0C2C27415C00F91132 /* BackgroundAlertManager.swift in Sources */,
 				DD493AD92ACF2171009A6922 /* Carbs.swift in Sources */,
 				DD493AE92ACF2445009A6922 /* BGData.swift in Sources */,
 				FCC6886B24898FD800A0279D /* ObservationToken.swift in Sources */,

+ 22 - 12
LoopFollow/Application/AppDelegate.swift

@@ -16,11 +16,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
 
     var window: UIWindow?
     let notificationCenter = UNUserNotificationCenter.current()
-   
+
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
         // Override point for customization after application launch.
         
-        
         let options: UNAuthorizationOptions = [.alert, .sound, .badge]
         notificationCenter.requestAuthorization(options: options) {
             (didAllow, error) in
@@ -37,11 +36,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
             }
         }
         
+        let action = UNNotificationAction(identifier: "OPEN_APP_ACTION", title: "Open App", options: .foreground)
+        let category = UNNotificationCategory(identifier: "loopfollow.background.alert", actions: [action], intentIdentifiers: [], options: [])
+        UNUserNotificationCenter.current().setNotificationCategories([category])
+
         UNUserNotificationCenter.current().delegate = self
-        
-         return true
-      }
-        
+
+        return true
+    }
+
     func applicationWillTerminate(_ application: UIApplication) {
         if UserDefaultsRepository.alertAppInactive.value {
             AlarmSound.setSoundFile(str: "Alarm_Buzzer")
@@ -53,7 +56,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
     
     func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
         
-
         // This application should be called in background every X Minutes
         UIApplication.shared.setMinimumBackgroundFetchInterval(
             TimeInterval(UserDefaultsRepository.backgroundRefreshFrequency.value * 60)
@@ -91,13 +93,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
          creates and returns a container, having loaded the store for the
          application to it. This property is optional since there are legitimate
          error conditions that could cause the creation of the store to fail.
-        */
+         */
         let container = NSPersistentCloudKitContainer(name: "LoopFollow")
         container.loadPersistentStores(completionHandler: { (storeDescription, error) in
             if let error = error as NSError? {
                 // Replace this implementation with code to handle the error appropriately.
                 // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
-                 
+
                 /*
                  Typical reasons for an error here include:
                  * The parent directory does not exist, cannot be created, or disallows writing.
@@ -128,13 +130,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
         }
     }
 
+    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
+        if response.actionIdentifier == "OPEN_APP_ACTION" {
+            if let window = window {
+                window.rootViewController?.dismiss(animated: true, completion: nil)
+                window.rootViewController?.present(MainViewController(), animated: true, completion: nil)
+            }
+        }
+        completionHandler()
+    }
 }
 
 extension AppDelegate: UNUserNotificationCenterDelegate {
-
     func userNotificationCenter(_ center: UNUserNotificationCenter,
-           willPresent notification: UNNotification,
-           withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
+                                willPresent notification: UNNotification,
+                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
     {
         completionHandler(.alert)
     }

+ 52 - 0
LoopFollow/Controllers/BackgroundAlertManager.swift

@@ -0,0 +1,52 @@
+//
+//  BackgroundAlertManager.swift
+//  LoopFollow
+//
+//  Created by Jonas Björkert on 2024-06-22.
+//  Copyright © 2024 Jon Fawcett. All rights reserved.
+//
+
+import Foundation
+import UserNotifications
+
+class BackgroundAlertManager {
+    static let shared = BackgroundAlertManager()
+    
+    private init() {}
+    
+    private var isAlertScheduled: Bool = false
+    
+    func startBackgroundAlert() {
+        isAlertScheduled = true
+        scheduleBackgroundAlert()
+    }
+    
+    func stopBackgroundAlert() {
+        isAlertScheduled = false
+        cancelBackgroundAlert()
+    }
+    
+    func scheduleBackgroundAlert() {
+        guard isAlertScheduled, UserDefaultsRepository.backgroundRefresh.value else { return }
+        
+        let content = UNMutableNotificationContent()
+        content.title = "LoopFollow Background Refresh"
+        content.body = "The app is not active, open the app to resume."
+        content.sound = .defaultCritical
+        content.categoryIdentifier = "loopfollow.background.alert"
+        
+        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 360, repeats: false)
+        
+        let request = UNNotificationRequest(identifier: "loopfollow.background.alert", content: content, trigger: trigger)
+        
+        UNUserNotificationCenter.current().add(request) { error in
+            if let error = error {
+                print("Error scheduling background alert: \(error)")
+            }
+        }
+    }
+    
+    private func cancelBackgroundAlert() {
+        UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["loopfollow.background.alert"])
+    }
+}

+ 1 - 2
LoopFollow/Controllers/Timers.swift

@@ -143,7 +143,6 @@ extension MainViewController {
     }
     
     @objc func bgTimerDidEnd(_ timer:Timer) {
-        
         // reset timer to 1 minute if settings aren't entered
         if UserDefaultsRepository.shareUserName.value == "" && UserDefaultsRepository.sharePassword.value == "" && UserDefaultsRepository.url.value == "" {
             startBGTimer(time: 60)
@@ -155,7 +154,7 @@ extension MainViewController {
         } else {
             webLoadNSBGData()
         }
-        
+        BackgroundAlertManager.shared.scheduleBackgroundAlert()
     }
     
     // Device Status Timer

+ 2 - 0
LoopFollow/ViewControllers/MainViewController.swift

@@ -432,6 +432,7 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
         // Cancel the current timer and start a fresh background timer using the settings value only if background task is enabled
         
         if UserDefaultsRepository.backgroundRefresh.value {
+            BackgroundAlertManager.shared.startBackgroundAlert()
             backgroundTask.startBackgroundTask()
         }
         
@@ -444,6 +445,7 @@ class MainViewController: UIViewController, UITableViewDataSource, ChartViewDele
         // Cancel the background tasks, start a fresh timer
         if UserDefaultsRepository.backgroundRefresh.value {
             backgroundTask.stopBackgroundTask()
+            BackgroundAlertManager.shared.stopBackgroundAlert()
         }
         
         restartAllTimers()