Quellcode durchsuchen

Add Lock Screen Widget setting to Notifications module

Mike Plante vor 2 Jahren
Ursprung
Commit
c4f7652fe0

+ 48 - 39
FreeAPS/Sources/Modules/NotificationsConfig/View/NotificationsConfigRootView.swift

@@ -1,20 +1,20 @@
-import SwiftUI
-import Swinject
 import ActivityKit
 import Combine
+import SwiftUI
+import Swinject
 
 extension NotificationsConfig {
     struct RootView: BaseView {
         let resolver: Resolver
         @StateObject var state = StateModel()
-        
+
         @State private var systemLiveActivitySetting: Bool = {
-               if #available(iOS 16.1, *) {
-                   ActivityAuthorizationInfo().areActivitiesEnabled
-               } else {
-                   false
-               }
-           }()
+            if #available(iOS 16.1, *) {
+                ActivityAuthorizationInfo().areActivitiesEnabled
+            } else {
+                false
+            }
+        }()
 
         private var glucoseFormatter: NumberFormatter {
             let formatter = NumberFormatter()
@@ -54,41 +54,50 @@ extension NotificationsConfig {
                     endPoint: .bottom
                 )
         }
-        
+
         @ViewBuilder private func liveActivitySection() -> some View {
             if #available(iOS 16.2, *) {
-                              Section(
-                                  header: Text("Live Activity"),
-                                  footer: Text(
-                                      liveActivityFooterText()
-                                  ),
-                                  content: {
-                                      if !systemLiveActivitySetting {
-                                          Button("Open Settings App") {
-                                              UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
-                                          }
-                                      } else {
-                                          Toggle("Show Live Activity", isOn: $state.useLiveActivity) }
-                                  }
-                              )
-                              .onReceive(resolver.resolve(LiveActivityBridge.self)!.$systemEnabled, perform: {
-                                  self.systemLiveActivitySetting = $0
-                              })
-                          }
+                Section(
+                    header: Text("Live Activity"),
+                    footer: Text(
+                        liveActivityFooterText()
+                    ),
+                    content: {
+                        if !systemLiveActivitySetting {
+                            Button("Open Settings App") {
+                                UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
+                            }
+                        } else {
+                            Toggle("Show Live Activity", isOn: $state.useLiveActivity)
+                        }
+                        Picker(
+                            selection: $state.lockScreenView,
+                            label: Text("Lock screen widget")
+                        ) {
+                            ForEach(LockScreenView.allCases) { selection in
+                                Text(selection.displayName).tag(selection)
+                            }
+                        }
+                    }
+                )
+                .onReceive(resolver.resolve(LiveActivityBridge.self)!.$systemEnabled, perform: {
+                    self.systemLiveActivitySetting = $0
+                })
+            }
         }
-        
+
         private func liveActivityFooterText() -> String {
-                 var footer =
-                     "Live activity displays blood glucose live on the lock screen and on the dynamic island (if available)"
+            var footer =
+                "Live activity displays blood glucose live on the lock screen and on the dynamic island (if available)"
 
-                 if !systemLiveActivitySetting {
-                     footer =
-                         "Live activities are turned OFF in system settings. To enable live activities, go to Settings app -> iAPS -> Turn live Activities ON.\n\n" +
-                         footer
-                 }
+            if !systemLiveActivitySetting {
+                footer =
+                    "Live activities are turned OFF in system settings. To enable live activities, go to Settings app -> iAPS -> Turn live Activities ON.\n\n" +
+                    footer
+            }
 
-                 return footer
-             }
+            return footer
+        }
 
         var body: some View {
             Form {
@@ -121,7 +130,7 @@ extension NotificationsConfig {
                         Text("g").foregroundColor(.secondary)
                     }
                 }
-                
+
                 liveActivitySection()
             }.scrollContentBackground(.hidden).background(color)
                 .onAppear(perform: configureView)

+ 22 - 22
FreeAPS/Sources/Services/LiveActivity/LiveActivityBridge.swift

@@ -34,9 +34,9 @@ extension LiveActivityAttributes.ContentState {
         }
 
         let formattedBG = Self.formatGlucose(glucose, mmol: mmol, forceSign: false)
-        
+
         var rotationDegrees: Double = 0.0
-        
+
         switch bg.direction {
         case .doubleUp,
              .singleUp,
@@ -58,7 +58,7 @@ extension LiveActivityAttributes.ContentState {
              .some(.none):
             rotationDegrees = 0
         }
-        
+
         let trendString = bg.direction?.symbol
 
         let change = prev?.glucose.map({
@@ -80,7 +80,7 @@ extension LiveActivityAttributes.ContentState {
         let iob = suggestion.iob ?? 0
 
         let lockScreenView = settings.lockScreenView.displayName
-        
+
         self.init(
             bg: formattedBG,
             direction: trendString,
@@ -123,9 +123,9 @@ extension LiveActivityAttributes.ContentState {
     @Injected() private var glucoseStorage: GlucoseStorage!
     @Injected() private var broadcaster: Broadcaster!
     @Injected() private var storage: FileStorage!
-    
+
     private let activityAuthorizationInfo = ActivityAuthorizationInfo()
-       @Published private(set) var systemEnabled: Bool
+    @Published private(set) var systemEnabled: Bool
 
     private var settings: FreeAPSSettings {
         settingsManager.settings
@@ -142,7 +142,7 @@ extension LiveActivityAttributes.ContentState {
         systemEnabled = activityAuthorizationInfo.areActivitiesEnabled
         injectServices(resolver)
         broadcaster.register(GlucoseObserver.self, observer: self)
-        
+
         Foundation.NotificationCenter.default.addObserver(
             forName: UIApplication.didEnterBackgroundNotification,
             object: nil,
@@ -158,7 +158,7 @@ extension LiveActivityAttributes.ContentState {
         ) { _ in
             self.forceActivityUpdate()
         }
-       
+
         monitorForLiveActivityAuthorizationChanges()
     }
 
@@ -207,9 +207,9 @@ extension LiveActivityAttributes.ContentState {
                 await pushUpdate(state)
             } else {
                 let content = ActivityContent(
-                                   state: state,
-                                   staleDate: min(state.date, Date.now).addingTimeInterval(TimeInterval(6 * 60))
-                               )
+                    state: state,
+                    staleDate: min(state.date, Date.now).addingTimeInterval(TimeInterval(6 * 60))
+                )
                 await currentActivity.activity.update(content)
             }
         } else {
@@ -241,9 +241,9 @@ extension LiveActivityAttributes.ContentState {
                     pushType: nil
                 )
                 currentActivity = ActiveActivity(activity: activity, startDate: Date.now)
-                
+
                 // then show the actual content
-               await pushUpdate(state)
+                await pushUpdate(state)
             } catch {
                 print("activity creation error: \(error)")
             }
@@ -268,14 +268,14 @@ extension LiveActivityAttributes.ContentState {
 extension LiveActivityBridge: GlucoseObserver {
     func glucoseDidUpdate(_ glucose: [BloodGlucose]) {
         guard settings.useLiveActivity else {
-                   if currentActivity != nil {
-                       Task {
-                           await self.endActivity()
-                       }
-                   }
-                   return
-               }
-        
+            if currentActivity != nil {
+                Task {
+                    await self.endActivity()
+                }
+            }
+            return
+        }
+
         // backfill latest glucose if contained in this update
         if glucose.count > 1 {
             latestGlucose = glucose[glucose.count - 2]
@@ -283,7 +283,7 @@ extension LiveActivityBridge: GlucoseObserver {
         defer {
             self.latestGlucose = glucose.last
         }
-        
+
         // fetch glucose for chart from Core Data
         let coreDataStorage = CoreDataStorage()
         let sixHoursAgo = Calendar.current.date(byAdding: .hour, value: -6, to: Date()) ?? Date()

+ 83 - 83
LiveActivity/LiveActivity.swift

@@ -73,20 +73,20 @@ struct LiveActivity: Widget {
             }
         }
     }
-    
+
     private func updatedLabel(context: ActivityViewContext<LiveActivityAttributes>) -> Text {
-            let text = Text("Updated: \(dateFormatter.string(from: context.state.date))")
-            if context.isStale {
-                if #available(iOSApplicationExtension 17.0, *) {
-                    return text.bold().foregroundStyle(.red)
-                } else {
-                    return text.bold().foregroundColor(.red)
-                }
+        let text = Text("Updated: \(dateFormatter.string(from: context.state.date))")
+        if context.isStale {
+            if #available(iOSApplicationExtension 17.0, *) {
+                return text.bold().foregroundStyle(.red)
             } else {
-                return text
+                return text.bold().foregroundColor(.red)
             }
+        } else {
+            return text
         }
-    
+    }
+
     private func bgLabel(context: ActivityViewContext<LiveActivityAttributes>) -> Text {
         Text(context.state.bg)
             .fontWeight(.bold)
@@ -94,63 +94,63 @@ struct LiveActivity: Widget {
     }
 
     private func bgAndTrend(context: ActivityViewContext<LiveActivityAttributes>, size: Size) -> (some View, Int) {
-          var characters = 0
+        var characters = 0
 
-          let bgText = context.state.bg
-          characters += bgText.count
+        let bgText = context.state.bg
+        characters += bgText.count
 
-          // narrow mode is for the minimal dynamic island view
-          // there is not enough space to show all three arrow there
-          // and everything has to be squeezed together to some degree
-          // only display the first arrow character and make it red in case there were more characters
-          var directionText: String?
-          var warnColor: Color?
-          if let direction = context.state.direction {
-              if size == .compact {
-                  directionText = String(direction[direction.startIndex ... direction.startIndex])
+        // narrow mode is for the minimal dynamic island view
+        // there is not enough space to show all three arrow there
+        // and everything has to be squeezed together to some degree
+        // only display the first arrow character and make it red in case there were more characters
+        var directionText: String?
+        var warnColor: Color?
+        if let direction = context.state.direction {
+            if size == .compact {
+                directionText = String(direction[direction.startIndex ... direction.startIndex])
 
-                  if direction.count > 1 {
-                      warnColor = Color.red
-                  }
-              } else {
-                  directionText = direction
-              }
+                if direction.count > 1 {
+                    warnColor = Color.red
+                }
+            } else {
+                directionText = direction
+            }
 
-              characters += directionText!.count
-          }
+            characters += directionText!.count
+        }
 
-          let spacing: CGFloat
-          switch size {
-          case .minimal: spacing = -1
-          case .compact: spacing = 0
-          case .expanded: spacing = 3
-          }
+        let spacing: CGFloat
+        switch size {
+        case .minimal: spacing = -1
+        case .compact: spacing = 0
+        case .expanded: spacing = 3
+        }
 
-          let stack = HStack(spacing: spacing) {
-              Text(bgText)
-                  .strikethrough(context.isStale, pattern: .solid, color: .red.opacity(0.6))
-              if let direction = directionText {
-                  let text = Text(direction)
-                  switch size {
-                  case .minimal:
-                      let scaledText = text.scaleEffect(x: 0.7, y: 0.7, anchor: .leading)
-                      if let warnColor {
-                          scaledText.foregroundStyle(warnColor)
-                      } else {
-                          scaledText
-                      }
-                  case .compact:
-                      text.scaleEffect(x: 0.8, y: 0.8, anchor: .leading).padding(.trailing, -3)
+        let stack = HStack(spacing: spacing) {
+            Text(bgText)
+                .strikethrough(context.isStale, pattern: .solid, color: .red.opacity(0.6))
+            if let direction = directionText {
+                let text = Text(direction)
+                switch size {
+                case .minimal:
+                    let scaledText = text.scaleEffect(x: 0.7, y: 0.7, anchor: .leading)
+                    if let warnColor {
+                        scaledText.foregroundStyle(warnColor)
+                    } else {
+                        scaledText
+                    }
+                case .compact:
+                    text.scaleEffect(x: 0.8, y: 0.8, anchor: .leading).padding(.trailing, -3)
 
-                  case .expanded:
-                      text.scaleEffect(x: 0.7, y: 0.7, anchor: .leading).padding(.trailing, -5)
-                  }
-              }
-          }
-          .foregroundStyle(context.isStale ? Color.primary.opacity(0.5) : Color.primary)
+                case .expanded:
+                    text.scaleEffect(x: 0.7, y: 0.7, anchor: .leading).padding(.trailing, -5)
+                }
+            }
+        }
+        .foregroundStyle(context.isStale ? Color.primary.opacity(0.5) : Color.primary)
 
-          return (stack, characters)
-      }
+        return (stack, characters)
+    }
 
     @ViewBuilder func bobble(context: ActivityViewContext<LiveActivityAttributes>) -> some View {
         @State var angularGradient = AngularGradient(colors: [
@@ -223,13 +223,13 @@ struct LiveActivity: Widget {
                     }
                 }
                 .privacySensitive()
-               .padding(.all, 15)
-               // Semantic BackgroundStyle and Color values work here. They adapt to the given interface style (light mode, dark mode)
-               // Semantic UIColors do NOT (as of iOS 17.1.1). Like UIColor.systemBackgroundColor (it does not adapt to changes of the interface style)
-               // The colorScheme environment varaible that is usually used to detect dark mode does NOT work here (it reports false values)
-               .foregroundStyle(Color.primary)
-               .background(BackgroundStyle.background.opacity(0.4))
-               .activityBackgroundTint(Color.clear)
+                .padding(.all, 15)
+                // Semantic BackgroundStyle and Color values work here. They adapt to the given interface style (light mode, dark mode)
+                // Semantic UIColors do NOT (as of iOS 17.1.1). Like UIColor.systemBackgroundColor (it does not adapt to changes of the interface style)
+                // The colorScheme environment varaible that is usually used to detect dark mode does NOT work here (it reports false values)
+                .foregroundStyle(Color.primary)
+                .background(BackgroundStyle.background.opacity(0.4))
+                .activityBackgroundTint(Color.clear)
             } else {
                 HStack(spacing: 2) {
                     VStack {
@@ -271,12 +271,12 @@ struct LiveActivity: Widget {
                 DynamicIslandExpandedRegion(.bottom) {
                     if context.state.lockScreenView == "Simple" {
                         Group {
-                             updatedLabel(context: context).font(.caption).foregroundStyle(Color.secondary)
-                         }
-                         .frame(
-                             maxHeight: .infinity,
-                             alignment: .bottom
-                         )
+                            updatedLabel(context: context).font(.caption).foregroundStyle(Color.secondary)
+                        }
+                        .frame(
+                            maxHeight: .infinity,
+                            alignment: .bottom
+                        )
                     } else {
                         chart(context: context)
                     }
@@ -293,21 +293,21 @@ struct LiveActivity: Widget {
             } minimal: {
                 let (_label, characterCount) = bgAndTrend(context: context, size: .minimal)
 
-              let label = _label.padding(.leading, 7).padding(.trailing, 3)
+                let label = _label.padding(.leading, 7).padding(.trailing, 3)
 
-              if characterCount < 4 {
-                  label
-              } else if characterCount < 5 {
-                  label.fontWidth(.condensed)
-              } else {
-                  label.fontWidth(.compressed)
-              }
+                if characterCount < 4 {
+                    label
+                } else if characterCount < 5 {
+                    label.fontWidth(.condensed)
+                } else {
+                    label.fontWidth(.compressed)
+                }
             }
             .widgetURL(URL(string: "freeaps-x://"))
             .keylineTint(Color.purple)
-           .contentMargins(.horizontal, 0, for: .minimal)
-           .contentMargins(.trailing, 0, for: .compactLeading)
-           .contentMargins(.leading, 0, for: .compactTrailing)
+            .contentMargins(.horizontal, 0, for: .minimal)
+            .contentMargins(.trailing, 0, for: .compactLeading)
+            .contentMargins(.leading, 0, for: .compactTrailing)
         }
     }
 }