Explorar el Código

Home Screen Quick Action for Speak BG

bjorkert hace 3 años
padre
commit
d3b69207c0

+ 43 - 0
LoopFollow/Application/SceneDelegate.swift

@@ -7,10 +7,12 @@
 //
 
 import UIKit
+import AVFoundation
 
 class SceneDelegate: UIResponder, UIWindowSceneDelegate {
 
     var window: UIWindow?
+    let synthesizer = AVSpeechSynthesizer()
 
     let appStateController = AppStateController()
     
@@ -49,6 +51,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
                vc.appStateController = appStateController
             }
          }
+
+        // Register the SceneDelegate as an observer for the "toggleSpeakBG" notification, which will be triggered when the user toggles the "Speak BG" feature in General Settings. This helps ensure that the Quick Action is updated according to the current setting.
+        NotificationCenter.default.addObserver(self, selector: #selector(handleToggleSpeakBGEvent), name: NSNotification.Name("toggleSpeakBG"), object: nil)
+        updateQuickActions()
     }
 
     func sceneDidDisconnect(_ scene: UIScene) {
@@ -56,6 +62,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
         // This occurs shortly after the scene enters the background, or when its session is discarded.
         // Release any resources associated with this scene that can be re-created the next time the scene connects.
         // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
+
+        NotificationCenter.default.removeObserver(self, name: NSNotification.Name("toggleSpeakBG"), object: nil)
     }
 
     func sceneDidBecomeActive(_ scene: UIScene) {
@@ -82,6 +90,41 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
         (UIApplication.shared.delegate as? AppDelegate)?.saveContext()
     }
 
+    // Update the Home Screen Quick Action for toggling the "Speak BG" feature based on the current setting in UserDefaultsRepository. This function uses UIApplicationShortcutItem to create a 3D touch action for controlling the feature.
+    func updateQuickActions() {
+        let iconName = UserDefaultsRepository.speakBG.value ? "pause.circle.fill" : "play.circle.fill"
+        let iconTemplate = UIApplicationShortcutIcon(systemImageName: iconName)
+
+        let shortcut = UIApplicationShortcutItem(type: Bundle.main.bundleIdentifier! + ".toggleSpeakBG",
+                                                 localizedTitle: "Speak BG",
+                                                 localizedSubtitle: nil,
+                                                 icon: iconTemplate,
+                                                 userInfo: nil)
+        UIApplication.shared.shortcutItems = [shortcut]
+    }
+
 
+    // Handle the UIApplicationShortcutItem when the user taps on the Home Screen Quick Action. This function toggles the "Speak BG" setting in UserDefaultsRepository, speaks the current state (on/off) using AVSpeechSynthesizer, and updates the Quick Action appearance.
+    func handleShortcutItem(_ shortcutItem: UIApplicationShortcutItem) {
+        if let bundleIdentifier = Bundle.main.bundleIdentifier {
+            let expectedType = bundleIdentifier + ".toggleSpeakBG"
+            if shortcutItem.type == expectedType {
+                UserDefaultsRepository.speakBG.value.toggle()
+                let message = UserDefaultsRepository.speakBG.value ? "BG Speak is now on" : "BG Speak is now off"
+                let utterance = AVSpeechUtterance(string: message)
+                synthesizer.speak(utterance)
+                updateQuickActions()
+            }
+        }
+    }
+
+    // The following method is called when the user taps on the Home Screen Quick Action
+    func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
+        handleShortcutItem(shortcutItem)
+    }
+
+    @objc func handleToggleSpeakBGEvent() {
+        updateQuickActions()
+    }
 }
 

+ 17 - 0
LoopFollow/ViewControllers/GeneralSettingsViewController.swift

@@ -22,6 +22,9 @@ class GeneralSettingsViewController: FormViewController {
          overrideUserInterfaceStyle = .dark
       }
       buildGeneralSettings()
+
+      // Register the GeneralSettingsViewController as an observer for the UIApplication.willEnterForegroundNotification, which will be triggered when the app enters the foreground. This helps ensure that the "Speak BG" switch in the General Settings is updated according to the current setting.
+      NotificationCenter.default.addObserver(self, selector: #selector(handleAppWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
    }
    
    private func buildGeneralSettings() {
@@ -143,6 +146,9 @@ class GeneralSettingsViewController: FormViewController {
         }.onChange { [weak self] row in
                     guard let value = row.value else { return }
                     UserDefaultsRepository.speakBG.value = value
+
+                    // Post a "toggleSpeakBG" notification whenever the "Speak BG" switch value changes in the General Settings. This allows the SceneDelegate to update the Home Screen Quick Action appearance and ensures consistency between the switch and the Quick Action.
+                    NotificationCenter.default.post(name: Notification.Name("toggleSpeakBG"), object: nil)
         }
         
         +++ ButtonRow() {
@@ -152,4 +158,15 @@ class GeneralSettingsViewController: FormViewController {
        }
     }
    
+    // Update the "Speak BG" SwitchRow value in the General Settings when the app enters the foreground. This ensures that the switch reflects the current setting in UserDefaultsRepository, even if it was changed using the Home Screen Quick Action while the app was in the background.
+    @objc func handleAppWillEnterForeground() {
+        if let row = self.form.rowBy(tag: "speakBG") as? SwitchRow {
+            row.value = UserDefaultsRepository.speakBG.value
+            row.updateCell()
+        }
+    }
+
+    deinit {
+        NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil)
+    }
 }