From bb162c07116d1f06f6192b2bbdc98424b0c38788 Mon Sep 17 00:00:00 2001 From: Programistich Date: Wed, 4 Sep 2024 12:25:03 +0300 Subject: [PATCH] [Widget] Lock Screen Battery --- .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 35 +++ .../Charging.imageset/Charging.svg | 3 + .../Charging.imageset/Contents.json | 21 ++ .../Assets.xcassets/Contents.json | 6 + .../Flipper.imageset/Contents.json | 21 ++ .../Flipper.imageset/Flipper.svg | 3 + .../FlipperCharging.imageset/Contents.json | 21 ++ .../FlipperCharging.svg | 12 + .../Contents.json | 21 ++ .../FlipperDisconnected.svg | 21 ++ .../FlipperUnknown.imageset/Contents.json | 21 ++ .../FlipperUnknown.svg | 13 ++ .../WidgetBackground.colorset/Contents.json | 11 + Flipper/BatteryWidget/BatteryWidget.swift | 19 ++ .../BatteryWidgetExtension.entitlements | 10 + .../Components/BatteryViewCircular.swift | 51 +++++ .../Components/BatteryViewInline.swift | 64 ++++++ .../Components/BatteryViewRectangular.swift | 74 +++++++ .../BatteryWidget/Components/Entry+UI.swift | 32 +++ Flipper/BatteryWidget/Entry.swift | 12 + Flipper/BatteryWidget/EntryView.swift | 16 ++ Flipper/BatteryWidget/Info.plist | 11 + Flipper/BatteryWidget/Main.swift | 9 + Flipper/BatteryWidget/Provider.swift | 55 +++++ Flipper/Flipper.xcodeproj/project.pbxproj | 206 ++++++++++++++++++ .../xcshareddata/swiftpm/Package.resolved | 140 ------------ .../Packages/UI/Sources/Main/MainView.swift | 13 ++ 28 files changed, 792 insertions(+), 140 deletions(-) create mode 100644 Flipper/BatteryWidget/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Flipper/BatteryWidget/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Flipper/BatteryWidget/Assets.xcassets/Charging.imageset/Charging.svg create mode 100644 Flipper/BatteryWidget/Assets.xcassets/Charging.imageset/Contents.json create mode 100644 Flipper/BatteryWidget/Assets.xcassets/Contents.json create mode 100644 Flipper/BatteryWidget/Assets.xcassets/Flipper.imageset/Contents.json create mode 100644 Flipper/BatteryWidget/Assets.xcassets/Flipper.imageset/Flipper.svg create mode 100644 Flipper/BatteryWidget/Assets.xcassets/FlipperCharging.imageset/Contents.json create mode 100644 Flipper/BatteryWidget/Assets.xcassets/FlipperCharging.imageset/FlipperCharging.svg create mode 100644 Flipper/BatteryWidget/Assets.xcassets/FlipperDisconnected.imageset/Contents.json create mode 100644 Flipper/BatteryWidget/Assets.xcassets/FlipperDisconnected.imageset/FlipperDisconnected.svg create mode 100644 Flipper/BatteryWidget/Assets.xcassets/FlipperUnknown.imageset/Contents.json create mode 100644 Flipper/BatteryWidget/Assets.xcassets/FlipperUnknown.imageset/FlipperUnknown.svg create mode 100644 Flipper/BatteryWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json create mode 100644 Flipper/BatteryWidget/BatteryWidget.swift create mode 100644 Flipper/BatteryWidget/BatteryWidgetExtension.entitlements create mode 100644 Flipper/BatteryWidget/Components/BatteryViewCircular.swift create mode 100644 Flipper/BatteryWidget/Components/BatteryViewInline.swift create mode 100644 Flipper/BatteryWidget/Components/BatteryViewRectangular.swift create mode 100644 Flipper/BatteryWidget/Components/Entry+UI.swift create mode 100644 Flipper/BatteryWidget/Entry.swift create mode 100644 Flipper/BatteryWidget/EntryView.swift create mode 100644 Flipper/BatteryWidget/Info.plist create mode 100644 Flipper/BatteryWidget/Main.swift create mode 100644 Flipper/BatteryWidget/Provider.swift delete mode 100644 Flipper/Flipper.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/Flipper/BatteryWidget/Assets.xcassets/AccentColor.colorset/Contents.json b/Flipper/BatteryWidget/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/Flipper/BatteryWidget/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Flipper/BatteryWidget/Assets.xcassets/AppIcon.appiconset/Contents.json b/Flipper/BatteryWidget/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..230588010 --- /dev/null +++ b/Flipper/BatteryWidget/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Flipper/BatteryWidget/Assets.xcassets/Charging.imageset/Charging.svg b/Flipper/BatteryWidget/Assets.xcassets/Charging.imageset/Charging.svg new file mode 100644 index 000000000..53936103b --- /dev/null +++ b/Flipper/BatteryWidget/Assets.xcassets/Charging.imageset/Charging.svg @@ -0,0 +1,3 @@ + + + diff --git a/Flipper/BatteryWidget/Assets.xcassets/Charging.imageset/Contents.json b/Flipper/BatteryWidget/Assets.xcassets/Charging.imageset/Contents.json new file mode 100644 index 000000000..ff96eac89 --- /dev/null +++ b/Flipper/BatteryWidget/Assets.xcassets/Charging.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "Charging.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Flipper/BatteryWidget/Assets.xcassets/Contents.json b/Flipper/BatteryWidget/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Flipper/BatteryWidget/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Flipper/BatteryWidget/Assets.xcassets/Flipper.imageset/Contents.json b/Flipper/BatteryWidget/Assets.xcassets/Flipper.imageset/Contents.json new file mode 100644 index 000000000..ba3e914d6 --- /dev/null +++ b/Flipper/BatteryWidget/Assets.xcassets/Flipper.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images": [ + { + "filename": "Flipper.svg", + "idiom": "universal", + "scale": "1x" + }, + { + "idiom": "universal", + "scale": "2x" + }, + { + "idiom": "universal", + "scale": "3x" + } + ], + "info": { + "author": "xcode", + "version": 1 + } +} diff --git a/Flipper/BatteryWidget/Assets.xcassets/Flipper.imageset/Flipper.svg b/Flipper/BatteryWidget/Assets.xcassets/Flipper.imageset/Flipper.svg new file mode 100644 index 000000000..dd4e342d6 --- /dev/null +++ b/Flipper/BatteryWidget/Assets.xcassets/Flipper.imageset/Flipper.svg @@ -0,0 +1,3 @@ + + + diff --git a/Flipper/BatteryWidget/Assets.xcassets/FlipperCharging.imageset/Contents.json b/Flipper/BatteryWidget/Assets.xcassets/FlipperCharging.imageset/Contents.json new file mode 100644 index 000000000..376e23e47 --- /dev/null +++ b/Flipper/BatteryWidget/Assets.xcassets/FlipperCharging.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "FlipperCharging.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Flipper/BatteryWidget/Assets.xcassets/FlipperCharging.imageset/FlipperCharging.svg b/Flipper/BatteryWidget/Assets.xcassets/FlipperCharging.imageset/FlipperCharging.svg new file mode 100644 index 000000000..f954d9a5a --- /dev/null +++ b/Flipper/BatteryWidget/Assets.xcassets/FlipperCharging.imageset/FlipperCharging.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Flipper/BatteryWidget/Assets.xcassets/FlipperDisconnected.imageset/Contents.json b/Flipper/BatteryWidget/Assets.xcassets/FlipperDisconnected.imageset/Contents.json new file mode 100644 index 000000000..5afe21406 --- /dev/null +++ b/Flipper/BatteryWidget/Assets.xcassets/FlipperDisconnected.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "FlipperDisconnected.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Flipper/BatteryWidget/Assets.xcassets/FlipperDisconnected.imageset/FlipperDisconnected.svg b/Flipper/BatteryWidget/Assets.xcassets/FlipperDisconnected.imageset/FlipperDisconnected.svg new file mode 100644 index 000000000..2fa85d925 --- /dev/null +++ b/Flipper/BatteryWidget/Assets.xcassets/FlipperDisconnected.imageset/FlipperDisconnected.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Flipper/BatteryWidget/Assets.xcassets/FlipperUnknown.imageset/Contents.json b/Flipper/BatteryWidget/Assets.xcassets/FlipperUnknown.imageset/Contents.json new file mode 100644 index 000000000..bed0b711c --- /dev/null +++ b/Flipper/BatteryWidget/Assets.xcassets/FlipperUnknown.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "FlipperUnknown.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Flipper/BatteryWidget/Assets.xcassets/FlipperUnknown.imageset/FlipperUnknown.svg b/Flipper/BatteryWidget/Assets.xcassets/FlipperUnknown.imageset/FlipperUnknown.svg new file mode 100644 index 000000000..8947b8257 --- /dev/null +++ b/Flipper/BatteryWidget/Assets.xcassets/FlipperUnknown.imageset/FlipperUnknown.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Flipper/BatteryWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/Flipper/BatteryWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/Flipper/BatteryWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Flipper/BatteryWidget/BatteryWidget.swift b/Flipper/BatteryWidget/BatteryWidget.swift new file mode 100644 index 000000000..2b0634273 --- /dev/null +++ b/Flipper/BatteryWidget/BatteryWidget.swift @@ -0,0 +1,19 @@ +import SwiftUI +import WidgetKit + +struct BatteryWidget: Widget { + var body: some WidgetConfiguration { + StaticConfiguration( + kind: "BatteryWidget", + provider: Provider() + ) { entry in + EntryView(entry: entry) + .containerBackground(.fill.tertiary, for: .widget) + } + .configurationDisplayName("Flipper Battery") + .description("Displays battery status of your Flipper device") + .supportedFamilies( + [.accessoryInline, .accessoryCircular, .accessoryRectangular] + ) + } +} diff --git a/Flipper/BatteryWidget/BatteryWidgetExtension.entitlements b/Flipper/BatteryWidget/BatteryWidgetExtension.entitlements new file mode 100644 index 000000000..98c53f08e --- /dev/null +++ b/Flipper/BatteryWidget/BatteryWidgetExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.flipperdevices.main + + + diff --git a/Flipper/BatteryWidget/Components/BatteryViewCircular.swift b/Flipper/BatteryWidget/Components/BatteryViewCircular.swift new file mode 100644 index 000000000..18d4dd0c1 --- /dev/null +++ b/Flipper/BatteryWidget/Components/BatteryViewCircular.swift @@ -0,0 +1,51 @@ +import SwiftUI +import WidgetKit + +struct BatteryViewCircular: View { + let entry: Provider.Entry + + var body: some View { + Gauge(value: Double(entry.value), in: 0...100) { + Image(entry.image) + .resizable() + .frame(width: 40, height: 40) + } + .gaugeStyle(.accessoryCircularCapacity) + } +} + +#Preview("Circular Loading", as: .accessoryCircular) { + BatteryWidget() +} timeline: { + Entry( + date: .now, + state: .loading + ) +} + +#Preview("Circular Disconnected", as: .accessoryCircular) { + BatteryWidget() +} timeline: { + Entry( + date: .now, + state: .disconnected + ) +} + +#Preview("Circular Default", as: .accessoryCircular) { + BatteryWidget() +} timeline: { + Entry( + date: .now, + state: .connected(65, false) + ) +} + +#Preview("Circular Charging", as: .accessoryCircular) { + BatteryWidget() +} timeline: { + Entry( + date: .now, + state: .connected(65, true) + ) +} diff --git a/Flipper/BatteryWidget/Components/BatteryViewInline.swift b/Flipper/BatteryWidget/Components/BatteryViewInline.swift new file mode 100644 index 000000000..9fb31688d --- /dev/null +++ b/Flipper/BatteryWidget/Components/BatteryViewInline.swift @@ -0,0 +1,64 @@ +import WidgetKit +import SwiftUI + +struct BatteryViewInline: View { + let entry: Provider.Entry + + var body: some View { + switch entry.state { + case .loading: + Text("Loading Device") + .redacted(reason: .placeholder) + case .disconnected: + Text("Flipper Disconnected") + .font(.system(size: 12, weight: .medium)) + case .connected(let battery, let isCharging): + HStack { + if isCharging { + Image("Charging") + .resizable() + .frame(width: 24, height: 24) + } + + Text("Flipper \(battery)%") + .font(.system(size: 12, weight: .medium)) + } + } + } +} + +#Preview("Inline Loading", as: .accessoryInline) { + BatteryWidget() +} timeline: { + Entry( + date: .now, + state: .loading + ) +} + +#Preview("Inline Disconnected", as: .accessoryInline) { + BatteryWidget() +} timeline: { + Entry( + date: .now, + state: .disconnected + ) +} + +#Preview("Inline Default", as: .accessoryInline) { + BatteryWidget() +} timeline: { + Entry( + date: .now, + state: .connected(65, false) + ) +} + +#Preview("Inline Charging", as: .accessoryInline) { + BatteryWidget() +} timeline: { + Entry( + date: .now, + state: .connected(65, true) + ) +} diff --git a/Flipper/BatteryWidget/Components/BatteryViewRectangular.swift b/Flipper/BatteryWidget/Components/BatteryViewRectangular.swift new file mode 100644 index 000000000..16906bae0 --- /dev/null +++ b/Flipper/BatteryWidget/Components/BatteryViewRectangular.swift @@ -0,0 +1,74 @@ +import WidgetKit +import SwiftUI + +struct BatteryViewRectangular: View { + let entry: Provider.Entry + + private var text: String { + return switch entry.state { + case .loading: "Loading Device" + case .disconnected: "Disconnected" + case .connected(let battery, _): + "Flipper \(battery)%" + } + } + + var body: some View { + Gauge(value: Double(entry.value), in: 0...100) { + HStack { + Spacer() + + Image(entry.image) + .resizable() + .frame(width: 40, height: 40) + + if entry.state == .loading { + Text(text) + .redacted(reason: .placeholder) + } else { + Text(text) + .font(.system(size: 12, weight: .medium)) + } + + Spacer() + } + } + .gaugeStyle(.accessoryLinearCapacity) + } +} + +#Preview("Rectangular Loading", as: .accessoryRectangular) { + BatteryWidget() +} timeline: { + Entry( + date: .now, + state: .loading + ) +} + +#Preview("Rectangular Disconnected", as: .accessoryRectangular) { + BatteryWidget() +} timeline: { + Entry( + date: .now, + state: .disconnected + ) +} + +#Preview("Rectangular Default", as: .accessoryRectangular) { + BatteryWidget() +} timeline: { + Entry( + date: .now, + state: .connected(65, false) + ) +} + +#Preview("Rectangular Charging", as: .accessoryRectangular) { + BatteryWidget() +} timeline: { + Entry( + date: .now, + state: .connected(65, true) + ) +} diff --git a/Flipper/BatteryWidget/Components/Entry+UI.swift b/Flipper/BatteryWidget/Components/Entry+UI.swift new file mode 100644 index 000000000..09a43199f --- /dev/null +++ b/Flipper/BatteryWidget/Components/Entry+UI.swift @@ -0,0 +1,32 @@ +import SwiftUI +import WidgetKit + +struct BatteryGaugeView: View { + let entry: Provider.Entry + + var body: some View { + Gauge(value: Double(entry.value), in: 0...100) { + Image(entry.image) + .resizable() + .frame(width: 40, height: 40) + } + } +} + +extension Entry { + var image: String { + return switch self.state { + case .loading: "FlipperUnknown" + case .disconnected: "FlipperDisconnected" + case .connected(_, let isCharging): + isCharging ? "FlipperCharging" : "Flipper" + } + } + + var value: Int { + return switch self.state { + case .connected(let battery, _): battery + default: 0 + } + } +} diff --git a/Flipper/BatteryWidget/Entry.swift b/Flipper/BatteryWidget/Entry.swift new file mode 100644 index 000000000..12103859b --- /dev/null +++ b/Flipper/BatteryWidget/Entry.swift @@ -0,0 +1,12 @@ +import WidgetKit + +struct Entry: TimelineEntry { + let date: Date + let state: State + + enum State: Equatable { + case loading + case disconnected + case connected(Int, Bool) + } +} diff --git a/Flipper/BatteryWidget/EntryView.swift b/Flipper/BatteryWidget/EntryView.swift new file mode 100644 index 000000000..16ab6f3e6 --- /dev/null +++ b/Flipper/BatteryWidget/EntryView.swift @@ -0,0 +1,16 @@ +import SwiftUI + +struct EntryView: View { + @Environment(\.widgetFamily) private var widgetFamily + + let entry: Provider.Entry + + var body: some View { + switch widgetFamily { + case .accessoryCircular: BatteryViewCircular(entry: entry) + case .accessoryInline: BatteryViewInline(entry: entry) + case .accessoryRectangular: BatteryViewRectangular(entry: entry) + default: EmptyView() + } + } +} diff --git a/Flipper/BatteryWidget/Info.plist b/Flipper/BatteryWidget/Info.plist new file mode 100644 index 000000000..0f118fb75 --- /dev/null +++ b/Flipper/BatteryWidget/Info.plist @@ -0,0 +1,11 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/Flipper/BatteryWidget/Main.swift b/Flipper/BatteryWidget/Main.swift new file mode 100644 index 000000000..0355a5a2e --- /dev/null +++ b/Flipper/BatteryWidget/Main.swift @@ -0,0 +1,9 @@ +import WidgetKit +import SwiftUI + +@main +struct BatteryWidgetBundle: WidgetBundle { + var body: some Widget { + BatteryWidget() + } +} diff --git a/Flipper/BatteryWidget/Provider.swift b/Flipper/BatteryWidget/Provider.swift new file mode 100644 index 000000000..9c10af1ec --- /dev/null +++ b/Flipper/BatteryWidget/Provider.swift @@ -0,0 +1,55 @@ +import SwiftUI +import WidgetKit + +struct Provider: TimelineProvider { + + func placeholder(in context: Context) -> Entry { + .init( + date: .now, + state: .loading) + } + + func getSnapshot( + in context: Context, + completion: @escaping (Entry) -> Void + ) { + completion( + .init( + date: .now, + state: getState()) + ) + } + + func getTimeline( + in context: Context, + completion: @escaping (Timeline) -> Void + ) { + completion( + .init( + entries: [ + .init( + date: Date().addingTimeInterval(0), + state: getState()) + ], + policy: .never + ) + ) + } + + private func getState() -> Entry.State { + let battery = UserDefaults.group.integer(forKey: "battery_level") + + if battery == -1 { + return .disconnected + } + + let isCharging = UserDefaults.group.bool(forKey: "battery_charging") + return .connected(battery, isCharging) + } +} + +extension UserDefaults { + static var group: UserDefaults { + .init(suiteName: "group.com.flipperdevices.main")! + } +} diff --git a/Flipper/Flipper.xcodeproj/project.pbxproj b/Flipper/Flipper.xcodeproj/project.pbxproj index 673e5907f..13cb4aa4b 100644 --- a/Flipper/Flipper.xcodeproj/project.pbxproj +++ b/Flipper/Flipper.xcodeproj/project.pbxproj @@ -11,6 +11,15 @@ 0AD5C05D29D9A04E00E0F97E /* ArchivedItemEntity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AD5C05B29D9A04E00E0F97E /* ArchivedItemEntity.swift */; }; 0AD5C05E29D9A04E00E0F97E /* SendArchivedItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AD5C05C29D9A04E00E0F97E /* SendArchivedItem.swift */; }; 49CE8F9E25262E2300B9CBE4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 49CE8F9D25262E2300B9CBE4 /* LaunchScreen.storyboard */; }; + 654B00422C86FC6000B72D5B /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B1A481829C113A5000169E9 /* WidgetKit.framework */; }; + 654B00432C86FC6000B72D5B /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B3E56602C7613A70090F9B8 /* SwiftUI.framework */; }; + 654B004A2C86FC6100B72D5B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 654B00492C86FC6100B72D5B /* Assets.xcassets */; }; + 654B004E2C86FC6100B72D5B /* BatteryWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 654B00412C86FC6000B72D5B /* BatteryWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 6578F92C2C8707EC00A320CD /* Main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6578F92B2C8707EC00A320CD /* Main.swift */; }; + 6578F92E2C87094000A320CD /* Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6578F92D2C87094000A320CD /* Entry.swift */; }; + 6578F9302C8709F600A320CD /* EntryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6578F92F2C8709F600A320CD /* EntryView.swift */; }; + 6578F9322C870A0800A320CD /* Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6578F9312C870A0800A320CD /* Provider.swift */; }; + 6578F9342C87139C00A320CD /* BatteryWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6578F9332C87139C00A320CD /* BatteryWidget.swift */; }; 65CAD4C22BD6946000628789 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 65CAD4C12BD6946000628789 /* PrivacyInfo.xcprivacy */; }; 65CAD4C42BD6946000628789 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 65CAD4C12BD6946000628789 /* PrivacyInfo.xcprivacy */; }; 8B064D5D2BD6C8F6008C00B3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8B064D5C2BD6C8F6008C00B3 /* Assets.xcassets */; }; @@ -71,6 +80,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 654B004C2C86FC6100B72D5B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F0DBFA1524EF2F9600EB2880 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 654B00402C86FC6000B72D5B; + remoteInfo = BatteryWidgetExtension; + }; 8B1A482929C113A6000169E9 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F0DBFA1524EF2F9600EB2880 /* Project object */; @@ -121,6 +137,7 @@ 8BF9CED428E21A4400CD5D31 /* TodayWidget.appex in Embed Foundation Extensions */, 8B1A482B29C113A6000169E9 /* ActivityWidget.appex in Embed Foundation Extensions */, 8B2F29922C1CD73500FEB48E /* LiveWidgetExtension.appex in Embed Foundation Extensions */, + 654B004E2C86FC6100B72D5B /* BatteryWidgetExtension.appex in Embed Foundation Extensions */, 8BCFDDEB277A2405002DA4CD /* KeyPreview.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; @@ -134,6 +151,15 @@ 0AD5C05C29D9A04E00E0F97E /* SendArchivedItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendArchivedItem.swift; sourceTree = ""; }; 44A5B59224F05647009EE7FB /* iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = iOS.entitlements; sourceTree = ""; }; 49CE8F9D25262E2300B9CBE4 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + 654B00412C86FC6000B72D5B /* BatteryWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = BatteryWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 654B00492C86FC6100B72D5B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 654B004B2C86FC6100B72D5B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 6578F92A2C87044200A320CD /* BatteryWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = BatteryWidgetExtension.entitlements; sourceTree = ""; }; + 6578F92B2C8707EC00A320CD /* Main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Main.swift; sourceTree = ""; }; + 6578F92D2C87094000A320CD /* Entry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Entry.swift; sourceTree = ""; }; + 6578F92F2C8709F600A320CD /* EntryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryView.swift; sourceTree = ""; }; + 6578F9312C870A0800A320CD /* Provider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Provider.swift; sourceTree = ""; }; + 6578F9332C87139C00A320CD /* BatteryWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryWidget.swift; sourceTree = ""; }; 65B31A442C400D8500F72D36 /* Backend */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Backend; path = Packages/Backend; sourceTree = ""; }; 65CAD4C12BD6946000628789 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 8B064D5C2BD6C8F6008C00B3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -204,7 +230,33 @@ F0DBFA2424EF2F9900EB2880 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 6578F93C2C87380400A320CD /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + BatteryViewCircular.swift, + BatteryViewInline.swift, + BatteryViewRectangular.swift, + "Entry+UI.swift", + ); + target = 654B00402C86FC6000B72D5B /* BatteryWidgetExtension */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 6578F9362C87358D00A320CD /* Components */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (6578F93C2C87380400A320CD /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Components; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ + 654B003E2C86FC6000B72D5B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 654B00432C86FC6000B72D5B /* SwiftUI.framework in Frameworks */, + 654B00422C86FC6000B72D5B /* WidgetKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8B1A481429C113A5000169E9 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -282,6 +334,22 @@ name = Frameworks; sourceTree = ""; }; + 654B00442C86FC6000B72D5B /* BatteryWidget */ = { + isa = PBXGroup; + children = ( + 6578F9362C87358D00A320CD /* Components */, + 6578F9312C870A0800A320CD /* Provider.swift */, + 6578F92F2C8709F600A320CD /* EntryView.swift */, + 6578F92D2C87094000A320CD /* Entry.swift */, + 654B00492C86FC6100B72D5B /* Assets.xcassets */, + 654B004B2C86FC6100B72D5B /* Info.plist */, + 6578F92A2C87044200A320CD /* BatteryWidgetExtension.entitlements */, + 6578F92B2C8707EC00A320CD /* Main.swift */, + 6578F9332C87139C00A320CD /* BatteryWidget.swift */, + ); + path = BatteryWidget; + sourceTree = ""; + }; 8B1A481C29C113A5000169E9 /* ActivityWidget */ = { isa = PBXGroup; children = ( @@ -403,6 +471,7 @@ 8B1A481C29C113A5000169E9 /* ActivityWidget */, 0AD5C05A29D9A02300E0F97E /* AppIntents */, 8B2F29862C1CD73400FEB48E /* LiveWidget */, + 654B00442C86FC6000B72D5B /* BatteryWidget */, F0DBFA2224EF2F9900EB2880 /* Products */, 44A5B5B124F05FCE009EE7FB /* Frameworks */, ); @@ -430,6 +499,7 @@ 8BF9CEC828E21A4400CD5D31 /* TodayWidget.appex */, 8B1A481729C113A5000169E9 /* ActivityWidget.appex */, 8B2F29832C1CD73400FEB48E /* LiveWidgetExtension.appex */, + 654B00412C86FC6000B72D5B /* BatteryWidgetExtension.appex */, ); name = Products; sourceTree = ""; @@ -448,6 +518,23 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 654B00402C86FC6000B72D5B /* BatteryWidgetExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 654B00512C86FC6100B72D5B /* Build configuration list for PBXNativeTarget "BatteryWidgetExtension" */; + buildPhases = ( + 654B003D2C86FC6000B72D5B /* Sources */, + 654B003E2C86FC6000B72D5B /* Frameworks */, + 654B003F2C86FC6000B72D5B /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BatteryWidgetExtension; + productName = BatteryWidgetExtension; + productReference = 654B00412C86FC6000B72D5B /* BatteryWidgetExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; 8B1A481629C113A5000169E9 /* ActivityWidget */ = { isa = PBXNativeTarget; buildConfigurationList = 8B1A482E29C113A6000169E9 /* Build configuration list for PBXNativeTarget "ActivityWidget" */; @@ -542,6 +629,7 @@ 8BF9CED328E21A4400CD5D31 /* PBXTargetDependency */, 8B1A482A29C113A6000169E9 /* PBXTargetDependency */, 8B2F29912C1CD73500FEB48E /* PBXTargetDependency */, + 654B004D2C86FC6100B72D5B /* PBXTargetDependency */, ); name = "Flipper(iOS)"; packageProductDependencies = ( @@ -563,6 +651,9 @@ LastSwiftUpdateCheck = 1610; LastUpgradeCheck = 1610; TargetAttributes = { + 654B00402C86FC6000B72D5B = { + CreatedOnToolsVersion = 16.0; + }; 8B1A481629C113A5000169E9 = { CreatedOnToolsVersion = 14.3; }; @@ -600,11 +691,20 @@ 8BF9CEC728E21A4400CD5D31 /* TodayWidget */, 8B1A481629C113A5000169E9 /* ActivityWidget */, 8B2F29822C1CD73400FEB48E /* LiveWidgetExtension */, + 654B00402C86FC6000B72D5B /* BatteryWidgetExtension */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 654B003F2C86FC6000B72D5B /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 654B004A2C86FC6100B72D5B /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8B1A481529C113A5000169E9 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -701,6 +801,18 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 654B003D2C86FC6000B72D5B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6578F92E2C87094000A320CD /* Entry.swift in Sources */, + 6578F9342C87139C00A320CD /* BatteryWidget.swift in Sources */, + 6578F92C2C8707EC00A320CD /* Main.swift in Sources */, + 6578F9322C870A0800A320CD /* Provider.swift in Sources */, + 6578F9302C8709F600A320CD /* EntryView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 8B1A481329C113A5000169E9 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -768,6 +880,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 654B004D2C86FC6100B72D5B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 654B00402C86FC6000B72D5B /* BatteryWidgetExtension */; + targetProxy = 654B004C2C86FC6100B72D5B /* PBXContainerItemProxy */; + }; 8B1A482A29C113A6000169E9 /* PBXTargetDependency */ = { isa = PBXTargetDependency; platformFilter = ios; @@ -794,6 +911,86 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 654B004F2C86FC6100B72D5B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = BatteryWidget/BatteryWidgetExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEVELOPMENT_TEAM = SXH69675TZ; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = BatteryWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = BatteryWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = 1.7.1; + PRODUCT_BUNDLE_IDENTIFIER = com.flipperdevices.main.BatteryWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 654B00502C86FC6100B72D5B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CODE_SIGN_ENTITLEMENTS = BatteryWidget/BatteryWidgetExtension.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 0; + DEVELOPMENT_TEAM = SXH69675TZ; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = BatteryWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = BatteryWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.0; + MARKETING_VERSION = 1.7.1; + PRODUCT_BUNDLE_IDENTIFIER = com.flipperdevices.main.BatteryWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; 8B1A482C29C113A6000169E9 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1257,6 +1454,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 654B00512C86FC6100B72D5B /* Build configuration list for PBXNativeTarget "BatteryWidgetExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 654B004F2C86FC6100B72D5B /* Debug */, + 654B00502C86FC6100B72D5B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 8B1A482E29C113A6000169E9 /* Build configuration list for PBXNativeTarget "ActivityWidget" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Flipper/Flipper.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Flipper/Flipper.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 55895e656..000000000 --- a/Flipper/Flipper.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,140 +0,0 @@ -{ - "pins" : [ - { - "identity" : "countly-sdk-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Countly/countly-sdk-ios.git", - "state" : { - "revision" : "e12be8153743a17c1389f80f9f7401c9f7f7de61", - "version" : "24.4.0" - } - }, - { - "identity" : "dcompression", - "kind" : "remoteSourceControl", - "location" : "https://github.com/swiftstack/dcompression.git", - "state" : { - "branch" : "dev", - "revision" : "9bb4eda095d9e9d962bcf3618993614f417c95c4" - } - }, - { - "identity" : "firebase-ios-sdk", - "kind" : "remoteSourceControl", - "location" : "https://github.com/tonyfreeman/firebase-ios-sdk", - "state" : { - "branch" : "master", - "revision" : "fccc5e45f145baf6c9ac73f9579180efd9334f8d" - } - }, - { - "identity" : "googledatatransport", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleDataTransport.git", - "state" : { - "revision" : "a637d318ae7ae246b02d7305121275bc75ed5565", - "version" : "9.4.0" - } - }, - { - "identity" : "googleutilities", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/GoogleUtilities.git", - "state" : { - "revision" : "26c898aed8bed13b8a63057ee26500abbbcb8d55", - "version" : "7.13.1" - } - }, - { - "identity" : "nanopb", - "kind" : "remoteSourceControl", - "location" : "https://github.com/firebase/nanopb.git", - "state" : { - "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", - "version" : "2.30910.0" - } - }, - { - "identity" : "networkimage", - "kind" : "remoteSourceControl", - "location" : "https://github.com/gonzalezreal/NetworkImage", - "state" : { - "revision" : "7aff8d1b31148d32c5933d75557d42f6323ee3d1", - "version" : "6.0.0" - } - }, - { - "identity" : "promises", - "kind" : "remoteSourceControl", - "location" : "https://github.com/google/promises.git", - "state" : { - "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", - "version" : "2.4.0" - } - }, - { - "identity" : "radix", - "kind" : "remoteSourceControl", - "location" : "https://github.com/swiftstack/radix.git", - "state" : { - "branch" : "dev", - "revision" : "21246051060c7050e707eb9f4b6796d99e7a1c60" - } - }, - { - "identity" : "stream", - "kind" : "remoteSourceControl", - "location" : "https://github.com/swiftstack/stream.git", - "state" : { - "branch" : "dev", - "revision" : "67deefcfc7af25a914677ce15b9c42981529419a" - } - }, - { - "identity" : "swift-collections", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-collections.git", - "state" : { - "revision" : "94cf62b3ba8d4bed62680a282d4c25f9c63c2efb", - "version" : "1.1.0" - } - }, - { - "identity" : "swift-log", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-log.git", - "state" : { - "revision" : "e97a6fcb1ab07462881ac165fdbb37f067e205d5", - "version" : "1.5.4" - } - }, - { - "identity" : "swift-markdown-ui", - "kind" : "remoteSourceControl", - "location" : "https://github.com/gonzalezreal/swift-markdown-ui", - "state" : { - "revision" : "9a8119b37e09a770367eeb26e05267c75d854053", - "version" : "2.3.1" - } - }, - { - "identity" : "swift-protobuf", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-protobuf.git", - "state" : { - "revision" : "9f0c76544701845ad98716f3f6a774a892152bcb", - "version" : "1.26.0" - } - }, - { - "identity" : "swift-syntax", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-syntax.git", - "state" : { - "revision" : "64889f0c732f210a935a0ad7cda38f77f876262d", - "version" : "509.1.1" - } - } - ], - "version" : 2 -} diff --git a/Flipper/Packages/UI/Sources/Main/MainView.swift b/Flipper/Packages/UI/Sources/Main/MainView.swift index 843ed558f..3436fab3b 100644 --- a/Flipper/Packages/UI/Sources/Main/MainView.swift +++ b/Flipper/Packages/UI/Sources/Main/MainView.swift @@ -45,10 +45,23 @@ struct MainView: View { .onReceive(device.$status) { status in if status == .disconnected { UserDefaults.group.set("", forKey: "emulating") + UserDefaults.group.set(-1, forKey: "battery_level") + UserDefaults.group.set(false, forKey: "battery_charging") UserDefaults.group.synchronize() + emulate.stopEmulate() WidgetCenter.shared.reloadTimelines(ofKind: "LiveWidget") + WidgetCenter.shared.reloadTimelines(ofKind: "BatteryWidget") } } + .onReceive(device.$flipper) { flipper in + let battery = flipper?.battery?.level ?? -1 + let isCharging = flipper?.battery?.state == .charging + + UserDefaults.group.set(battery, forKey: "battery_level") + UserDefaults.group.set(isCharging, forKey: "battery_charging") + UserDefaults.group.synchronize() + WidgetCenter.shared.reloadTimelines(ofKind: "BatteryWidget") + } } }