diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 7454c11b..56fc73b1 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -33,7 +33,7 @@ containerPortal = 97C146E61CF9000F007C117D /* Project object */; proxyType = 1; remoteGlobalIDString = 297F6FC42AD06E0D00FF159E; - remoteInfo = "Wonderous WidgetExtension"; + remoteInfo = WonderousWidgetExtension; }; E214FC8827C5A18E005F78FB /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -72,7 +72,7 @@ 1475293CB8660AC785DF56AB /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 297F6FC52AD06E0D00FF159E /* Wonderous WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Wonderous WidgetExtension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + 297F6FC52AD06E0D00FF159E /* Wonderous WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; name = "Wonderous WidgetExtension.appex"; path = WonderousWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 297F6FC62AD06E0D00FF159E /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; 297F6FC82AD06E0D00FF159E /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; 297F6FCB2AD06E0D00FF159E /* WonderousWidgetBundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WonderousWidgetBundle.swift; sourceTree = ""; }; @@ -80,7 +80,7 @@ 297F6FCF2AD06E0D00FF159E /* WonderousWidget.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = WonderousWidget.intentdefinition; sourceTree = ""; }; 297F6FD02AD06E0F00FF159E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 297F6FD22AD06E0F00FF159E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 297FD56C2ADF0DAB008D8BFE /* Wonderous WidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Wonderous WidgetExtension.entitlements"; sourceTree = ""; }; + 297FD56C2ADF0DAB008D8BFE /* WonderousWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WonderousWidgetExtension.entitlements; sourceTree = ""; }; 297FD5732AE18011008D8BFE /* WonderousWidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WonderousWidgetView.swift; sourceTree = ""; }; 297FD5752AE19BD9008D8BFE /* WonderWidgetViewComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WonderWidgetViewComponents.swift; sourceTree = ""; }; 297FD5772AE19C25008D8BFE /* FlutterUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlutterUtils.swift; sourceTree = ""; }; @@ -130,7 +130,7 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 297F6FCA2AD06E0D00FF159E /* Wonderous Widget */ = { + 297F6FCA2AD06E0D00FF159E /* WonderousWidget */ = { isa = PBXGroup; children = ( 297F6FCB2AD06E0D00FF159E /* WonderousWidgetBundle.swift */, @@ -142,7 +142,7 @@ 297FD5752AE19BD9008D8BFE /* WonderWidgetViewComponents.swift */, 297FD5772AE19C25008D8BFE /* FlutterUtils.swift */, ); - path = "Wonderous Widget"; + path = WonderousWidget; sourceTree = ""; }; 5073AC1D92C10773F20D12A2 /* Frameworks */ = { @@ -169,10 +169,10 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( - 297FD56C2ADF0DAB008D8BFE /* Wonderous WidgetExtension.entitlements */, + 297FD56C2ADF0DAB008D8BFE /* WonderousWidgetExtension.entitlements */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, - 297F6FCA2AD06E0D00FF159E /* Wonderous Widget */, + 297F6FCA2AD06E0D00FF159E /* WonderousWidget */, 97C146EF1CF9000F007C117D /* Products */, 5073AC1D92C10773F20D12A2 /* Frameworks */, E090BB04291350D10AF9DE4E /* Pods */, @@ -218,9 +218,9 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 297F6FC42AD06E0D00FF159E /* Wonderous WidgetExtension */ = { + 297F6FC42AD06E0D00FF159E /* WonderousWidgetExtension */ = { isa = PBXNativeTarget; - buildConfigurationList = 297F6FDC2AD06E0F00FF159E /* Build configuration list for PBXNativeTarget "Wonderous WidgetExtension" */; + buildConfigurationList = 297F6FDC2AD06E0F00FF159E /* Build configuration list for PBXNativeTarget "WonderousWidgetExtension" */; buildPhases = ( 297F6FC12AD06E0D00FF159E /* Sources */, 297F6FC22AD06E0D00FF159E /* Frameworks */, @@ -230,8 +230,8 @@ ); dependencies = ( ); - name = "Wonderous WidgetExtension"; - productName = "Wonderous WidgetExtension"; + name = WonderousWidgetExtension; + productName = WonderousWidgetExtension; productReference = 297F6FC52AD06E0D00FF159E /* Wonderous WidgetExtension.appex */; productType = "com.apple.product-type.app-extension"; }; @@ -319,7 +319,7 @@ targets = ( 97C146ED1CF9000F007C117D /* Runner */, E214FC8127C5A18D005F78FB /* wondersUITests */, - 297F6FC42AD06E0D00FF159E /* Wonderous WidgetExtension */, + 297F6FC42AD06E0D00FF159E /* WonderousWidgetExtension */, ); }; /* End PBXProject section */ @@ -479,7 +479,7 @@ /* Begin PBXTargetDependency section */ 297F6FD62AD06E0F00FF159E /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 297F6FC42AD06E0D00FF159E /* Wonderous WidgetExtension */; + target = 297F6FC42AD06E0D00FF159E /* WonderousWidgetExtension */; targetProxy = 297F6FD52AD06E0F00FF159E /* PBXContainerItemProxy */; }; E214FC8927C5A18E005F78FB /* PBXTargetDependency */ = { @@ -600,14 +600,14 @@ CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_ENTITLEMENTS = "Wonderous WidgetExtension.entitlements"; + CODE_SIGN_ENTITLEMENTS = WonderousWidgetExtension.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = S3TL5AY6Y3; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "Wonderous Widget/Info.plist"; + INFOPLIST_FILE = WonderousWidget/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Wonderous Widget"; INFOPLIST_KEY_NSHumanReadableCopyright = ""; IPHONEOS_DEPLOYMENT_TARGET = 16.4; @@ -958,7 +958,7 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 297F6FDC2AD06E0F00FF159E /* Build configuration list for PBXNativeTarget "Wonderous WidgetExtension" */ = { + 297F6FDC2AD06E0F00FF159E /* Build configuration list for PBXNativeTarget "WonderousWidgetExtension" */ = { isa = XCConfigurationList; buildConfigurations = ( 297F6FD92AD06E0F00FF159E /* Debug */, diff --git a/ios/Wonderous Widget/Assets.xcassets/AccentColor.colorset/Contents.json b/ios/Wonderous Widget/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index 2bc2d6bf..00000000 --- a/ios/Wonderous Widget/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.360", - "green" : "0.580", - "red" : "0.890" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/ios/Wonderous Widget/Assets.xcassets/GreyMediumColor.colorset/Contents.json b/ios/Wonderous Widget/Assets.xcassets/GreyMediumColor.colorset/Contents.json deleted file mode 100644 index 574414aa..00000000 --- a/ios/Wonderous Widget/Assets.xcassets/GreyMediumColor.colorset/Contents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.580", - "green" : "0.600", - "red" : "0.620" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/ios/Wonderous Widget/WonderWidgetViewComponents.swift b/ios/Wonderous Widget/WonderWidgetViewComponents.swift deleted file mode 100644 index 42e09237..00000000 --- a/ios/Wonderous Widget/WonderWidgetViewComponents.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// File.swift -// Wonderous WidgetExtension -// -// Created by Shawn on 2023-10-19. -// - -import Foundation -import SwiftUI - - -// TODO: Add support for showing the last-found artifact from the app -// Load an image from the flutter assets bundle -struct BgImage : View { - var entry: WonderousEntry - var body: some View { - let image = bundle.appending(path: "/assets/images/widget/background-empty.jpg").path(); - //print(image) - if let uiImage = UIImage(contentsOfFile: image) { - let image = Image(uiImage: uiImage) - .resizable() - .aspectRatio(contentMode: .fill) // Fill the entire view - .edgesIgnoringSafeArea(.all) // Ignore the safe area - return AnyView(image) - } - print("The image file could not be loaded") - return AnyView(EmptyView()) - } - -} - -// Display a previously loaded remote image -struct NetImage : View { - var imageData: Data? - var body: some View { - if imageData != nil, let uiImage = UIImage(data: imageData!) { - return Image(uiImage: uiImage) - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 80, height: 26.0) - } else { - return Image("EmptyChart") - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: 80, height: 26.0) - } - } -} - -struct GaugeProgressStyle: ProgressViewStyle { - let color:Color - func makeBody(configuration: Configuration) -> some View { - let fractionCompleted = configuration.fractionCompleted ?? 0 - - return ZStack { - Circle() - .stroke(.gray, style: StrokeStyle(lineWidth: 2)) - Circle() - .trim(from: 0, to: fractionCompleted) - .stroke(color, style: StrokeStyle(lineWidth: 4, lineCap: .round)) - .rotationEffect(.degrees(90)) - } - } -} diff --git a/ios/Wonderous Widget/WonderousWidgetView.swift b/ios/Wonderous Widget/WonderousWidgetView.swift deleted file mode 100644 index 14d303bd..00000000 --- a/ios/Wonderous Widget/WonderousWidgetView.swift +++ /dev/null @@ -1,70 +0,0 @@ -import WidgetKit -import SwiftUI -import Intents - -// Defines the view / layout of the widget -struct WonderousWidgetView : View { - @Environment(\.widgetFamily) var family: WidgetFamily - var entry: Provider.Entry - var body: some View { - let showTitle = family == .systemLarge - let showIcon = family != .systemSmall - let showTitleAndDesc = family != .systemSmall - let accentColor:Color = Color("AccentColor") - let progress = 7.0 / 32.0; - let image = bundle.appending(path: "/assets/images/widget/wonderous-icon.png").path(); - let content = VStack{ - HStack { - if(showTitle) { - Text("Collection").foregroundColor(accentColor) - } - Spacer(); - if(showIcon || true) { - Image(uiImage: UIImage(contentsOfFile: image)!) - .resizable().scaledToFit().frame(height: 24) - } - } - Spacer(); - HStack { - if(showTitleAndDesc) { - VStack(alignment: .leading){ - Text("Wonderous") - .font(.system(size: 22)) - .foregroundColor(accentColor); - Text("Search for hidden artifacts") - .font(.system(size: 15)) - .foregroundColor(Color("GreyMediumColor")); - } - } - Spacer(); - ZStack{ - ProgressView(value: progress) - .progressViewStyle( - GaugeProgressStyle(color: accentColor) - ) - .frame(width: 48, height: 48) - Text("\(Int(progress * 100))%").font(.system(size: 12)).foregroundColor(accentColor) - } - } - //NetImage(imageData: netImgData) - }.widgetURL(URL(string: "wonderous://collections")) - - ZStack{ - BgImage(entry: entry) - LinearGradient( - gradient: Gradient(colors: [.black.opacity(0), .black]), - startPoint: .center, - endPoint: .bottom) - switch(family) { - case .systemSmall: - content.padding(16) - case .systemMedium: - content.padding(24) - default: - content.padding(32) - } - - } - - } -} diff --git a/ios/Wonderous Widget/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/WonderousWidget/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from ios/Wonderous Widget/Assets.xcassets/AppIcon.appiconset/Contents.json rename to ios/WonderousWidget/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/ios/Wonderous Widget/Assets.xcassets/Contents.json b/ios/WonderousWidget/Assets.xcassets/Contents.json similarity index 100% rename from ios/Wonderous Widget/Assets.xcassets/Contents.json rename to ios/WonderousWidget/Assets.xcassets/Contents.json diff --git a/ios/Wonderous Widget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/ios/WonderousWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json similarity index 100% rename from ios/Wonderous Widget/Assets.xcassets/WidgetBackground.colorset/Contents.json rename to ios/WonderousWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json diff --git a/ios/Wonderous Widget/FlutterUtils.swift b/ios/WonderousWidget/FlutterUtils.swift similarity index 100% rename from ios/Wonderous Widget/FlutterUtils.swift rename to ios/WonderousWidget/FlutterUtils.swift diff --git a/ios/Wonderous Widget/Info.plist b/ios/WonderousWidget/Info.plist similarity index 100% rename from ios/Wonderous Widget/Info.plist rename to ios/WonderousWidget/Info.plist diff --git a/ios/WonderousWidget/WonderWidgetViewComponents.swift b/ios/WonderousWidget/WonderWidgetViewComponents.swift new file mode 100644 index 00000000..639974b3 --- /dev/null +++ b/ios/WonderousWidget/WonderWidgetViewComponents.swift @@ -0,0 +1,47 @@ +import Foundation +import SwiftUI + + +// Loads a default image from the flutter assets bundle, +// or displays a base64 encoded image +struct BgImage : View { + var entry: WonderousEntry + var body: some View { + var uiImage:UIImage?; + if(entry.imageData.isEmpty){ + let defaultImage = bundle.appending(path: "/assets/images/widget/background-empty.jpg").path(); + uiImage = UIImage(contentsOfFile: defaultImage); + } else { + uiImage = UIImage(data: Data(base64Encoded: entry.imageData)!) + } + if(uiImage != nil){ + let image = GeometryReader { geometry in + Image(uiImage: uiImage!) + .resizable() + .aspectRatio(contentMode: .fill) + .edgesIgnoringSafeArea(.all) // Ignore the safe area + .frame(maxWidth: geometry.size.width, maxHeight: geometry.size.height) + } + return AnyView(image) + } + print("The image file could not be loaded") + return AnyView(EmptyView()) + } + +} + +struct GaugeProgressStyle: ProgressViewStyle { + + func makeBody(configuration: Configuration) -> some View { + let fractionCompleted = configuration.fractionCompleted ?? 0 + + return ZStack { + Circle() + .stroke(Colors.darkGrey, style: StrokeStyle(lineWidth: 2)) + Circle() + .trim(from: 0, to: fractionCompleted) + .stroke(Colors.accentColor, style: StrokeStyle(lineWidth: 4, lineCap: .round)) + .rotationEffect(.degrees(90)) + } + } +} diff --git a/ios/Wonderous Widget/WonderousWidget.intentdefinition b/ios/WonderousWidget/WonderousWidget.intentdefinition similarity index 100% rename from ios/Wonderous Widget/WonderousWidget.intentdefinition rename to ios/WonderousWidget/WonderousWidget.intentdefinition diff --git a/ios/Wonderous Widget/WonderousWidget.swift b/ios/WonderousWidget/WonderousWidget.swift similarity index 55% rename from ios/Wonderous Widget/WonderousWidget.swift rename to ios/WonderousWidget/WonderousWidget.swift index 843e14ca..52c0687c 100644 --- a/ios/Wonderous Widget/WonderousWidget.swift +++ b/ios/WonderousWidget/WonderousWidget.swift @@ -12,6 +12,19 @@ import Intents var netImgData: Data? = nil +/// Entry, is passed into the view and defines the data it needs +struct WonderousEntry : TimelineEntry { + let date: Date + //let displaySize: CGSize + //let imageData: Data? + let discoveredCount:Int; + var title:String = ""; + var subTitle:String = ""; + var imageData:String = ""; + +} + + // Widget, defines the display name and description, and also wraps the View struct WonderousWidget: Widget { let kind: String = "WonderousWidget" @@ -30,30 +43,40 @@ struct WonderousWidget: Widget { struct Provider: TimelineProvider { // Provide an entry for a placeholder version of the widget func placeholder(in context: Context) -> WonderousEntry { - WonderousEntry(date: Date(), count: 0, displaySize: context.displaySize, imageData: netImgData) + WonderousEntry(date: Date(), discoveredCount: 0) } // Provide an entry for the current time and state of the widget func getSnapshot(in context: Context, completion: @escaping (WonderousEntry) -> ()) { let entry:WonderousEntry - if(context.isPreview){ - // entry = placeholder(in: context) - entry = WonderousEntry(date: Date(), count: 0, displaySize: context.displaySize, imageData: netImgData) - } else { - let userDefaults = UserDefaults(suiteName: "group.com.gskinner.homewidget") - let count = userDefaults?.integer(forKey: "counter") ?? 0; - entry = WonderousEntry(date: Date(), count: count, displaySize: context.displaySize, imageData: netImgData) - } + let userDefaults = UserDefaults(suiteName: "group.com.gskinner.flutter.wonders.widget") + let discoveredCount = userDefaults?.integer(forKey: "discoveredCount") ?? 0 + let title = userDefaults?.string(forKey: "lastDiscoveredTitle") ?? "" + let subTitle = userDefaults?.string(forKey: "lastDiscoveredSubTitle") ?? "" + let imageData = userDefaults?.string(forKey: "lastDiscoveredImageData") ?? "" +// if(context.isPreview){ +// entry = WonderousEntry(date: Date(), discoveredCount: discoveredCount) +// } + entry = WonderousEntry( + date: Date(), + discoveredCount:discoveredCount, + title: title, + subTitle: subTitle.prefix(1).capitalized + subTitle.dropFirst(), + imageData: imageData + ) completion(entry); } // Provide an array of entries for the current time and, optionally, any future times func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { // Load a remote image so it can be shown later - netImgData = try? Data( - contentsOf: URL(string: "https://www.wonderous.info/unsplash/-e0u9SAFeP4-32.jpg")! - ) - +// let userDefaults = UserDefaults(suiteName: "group.com.gskinner.flutter.wonders.widget") +// let url = userDefaults?.string(forKey: "lastDiscoveredImageUrl"); +// if(url != nil){ +// netImgData = try? Data(contentsOf: URL(string: url!)!) +// } else { +// netImgData = nil; +// } getSnapshot(in: context) { (entry) in let timeline = Timeline(entries: [entry], policy: .atEnd) completion(timeline) @@ -62,15 +85,6 @@ struct Provider: TimelineProvider { } -/// Entry, is passed into the view and defines the data it needs -struct WonderousEntry : TimelineEntry { - let date: Date - let count:Int; - let displaySize: CGSize - let imageData: Data? -} - - diff --git a/ios/Wonderous Widget/WonderousWidgetBundle.swift b/ios/WonderousWidget/WonderousWidgetBundle.swift similarity index 100% rename from ios/Wonderous Widget/WonderousWidgetBundle.swift rename to ios/WonderousWidget/WonderousWidgetBundle.swift diff --git a/ios/WonderousWidget/WonderousWidgetView.swift b/ios/WonderousWidget/WonderousWidgetView.swift new file mode 100644 index 00000000..78ef800c --- /dev/null +++ b/ios/WonderousWidget/WonderousWidgetView.swift @@ -0,0 +1,72 @@ +import WidgetKit +import SwiftUI +import Intents + +struct Colors { + static let accentColor:Color = Color(red: 0.89, green: 0.58, blue: 0.36) + static let offWhiteColor:Color = Color(red: 0.97, green: 0.92, blue: 0.9) + static let mediumGrey:Color = Color(red: 0.62, green: 0.6, blue: 0.58) + static let darkGrey:Color = Color(red: 0.32, green: 0.31, blue: 0.3); +} + +// Defines the view / layout of the widget +struct WonderousWidgetView : View { + @Environment(\.widgetFamily) var family: WidgetFamily + var entry: Provider.Entry + var body: some View { + let showTitle = family == .systemLarge + let showIcon = family != .systemSmall + let showTitleAndDesc = family != .systemSmall + + let progress = Double(entry.discoveredCount) / 24.0 + let iconImage = bundle.appending(path: "/assets/images/widget/wonderous-icon.png").path() + let title:String = entry.title.isEmpty ? "Wonderous" : entry.title; + let subTitle:String = entry.subTitle.isEmpty ? "Search for hidden artifacts" : entry.subTitle; + let content = VStack{ + HStack { + if(showTitle) { + Text("Collection") + .font(.system(size: 15)) + .foregroundColor(Colors.offWhiteColor) + } + Spacer(); + if(showIcon) { + Image(uiImage: UIImage(contentsOfFile: iconImage)!) + .resizable() + .scaledToFit() + .frame(height: 24) + } + } + Spacer(); + HStack { + if(showTitleAndDesc) { + VStack(alignment: .leading){ + Text(title) + .font(.system(size: 22)) + .foregroundColor(.white); + Text(subTitle) + .font(.system(size: 15)) + .foregroundColor(Colors.mediumGrey); + } + } + Spacer(); + ZStack{ + ProgressView(value: progress) + .progressViewStyle(GaugeProgressStyle()) + .frame(width: 48, height: 48) + Text("\(Int(progress * 100))%").font(.system(size: 13)).foregroundColor(.white) + } + } + } + + ZStack{ + BgImage(entry: entry) + LinearGradient( + gradient: Gradient(colors: [.black.opacity(0), .black]), + startPoint: .center, + endPoint: .bottom) + content.padding(16) + }.widgetURL(URL(string: "wonderous://collection")) + + } +} diff --git a/ios/Wonderous WidgetExtension.entitlements b/ios/WonderousWidgetExtension.entitlements similarity index 100% rename from ios/Wonderous WidgetExtension.entitlements rename to ios/WonderousWidgetExtension.entitlements diff --git a/lib/logic/app_logic.dart b/lib/logic/app_logic.dart index f6b87a31..c696caed 100644 --- a/lib/logic/app_logic.dart +++ b/lib/logic/app_logic.dart @@ -59,6 +59,7 @@ class AppLogic { timelineLogic.init(); // Collectibles + collectiblesLogic.init(); await collectiblesLogic.load(); // Flag bootStrap as complete diff --git a/lib/logic/collectibles_logic.dart b/lib/logic/collectibles_logic.dart index dc6f2b50..e24329b9 100644 --- a/lib/logic/collectibles_logic.dart +++ b/lib/logic/collectibles_logic.dart @@ -1,10 +1,16 @@ +import 'dart:convert'; + +import 'package:home_widget/home_widget.dart'; import 'package:wonders/common_libs.dart'; import 'package:wonders/logic/common/save_load_mixin.dart'; import 'package:wonders/logic/data/collectible_data.dart'; +import 'package:http/http.dart' as http; class CollectiblesLogic with ThrottledSaveLoadMixin { @override String get fileName => 'collectibles.dat'; + static const _appGroupId = 'group.com.gskinner.flutter.wonders.widget'; + static const _appName = 'WonderousWidget'; /// Holds all collectibles that the views should care about final List all = collectiblesData; @@ -13,11 +19,17 @@ class CollectiblesLogic with ThrottledSaveLoadMixin { late final statesById = ValueNotifier>({})..addListener(_updateCounts); int _discoveredCount = 0; + int get discoveredCount => _discoveredCount; int _exploredCount = 0; + int get exploredCount => _exploredCount; + void init() { + HomeWidget.setAppGroupId(_appGroupId); + } + CollectibleData? fromId(String? id) => id == null ? null : all.firstWhereOrNull((o) => o.id == id); List forWonder(WonderType wonder) { @@ -26,6 +38,14 @@ class CollectiblesLogic with ThrottledSaveLoadMixin { void setState(String id, int state) { Map states = Map.of(statesById.value); + if (state == CollectibleState.discovered) { + final data = fromId(id)!; + _updateHomeWidgetTextData( + title: data.title, + id: data.id, + imageUrl: data.imageUrlSmall, + ); + } states[id] = state; statesById.value = states; scheduleSave(); @@ -37,6 +57,9 @@ class CollectiblesLogic with ThrottledSaveLoadMixin { if (state == CollectibleState.discovered) _discoveredCount++; if (state == CollectibleState.explored) _exploredCount++; }); + HomeWidget.saveWidgetData('discoveredCount', _discoveredCount); + HomeWidget.updateWidget(iOSName: _appName); + debugPrint('setting discovered count for home widget $_discoveredCount'); } /// Get a discovered item, sorted by the order of wondersLogic.all @@ -67,11 +90,35 @@ class CollectiblesLogic with ThrottledSaveLoadMixin { for (int i = 0; i < all.length; i++) { states[all[i].id] = CollectibleState.lost; } + _updateHomeWidgetTextData(); // clear home widget data statesById.value = states; debugPrint('collection reset'); scheduleSave(); } + // TODO: Optimize to send both network requests simultaneously + Future _updateHomeWidgetTextData({String title = '', String id = '', String imageUrl = ''}) async { + // Save title + HomeWidget.saveWidgetData('lastDiscoveredTitle', title); + // Subtitle + String subTitle = ''; + if(id.isNotEmpty){ + final artifactData = await artifactLogic.getArtifactByID(id); + subTitle = artifactData?.date ?? ''; + } + HomeWidget.saveWidgetData('lastDiscoveredSubTitle', subTitle); + // Image, + // Download, convert to base64 string and write to shared widget data + String imageBase64 = ''; + if(imageUrl.isNotEmpty){ + var bytes = await http.readBytes(Uri.parse(imageUrl)); + imageBase64 = base64Encode(bytes); + debugPrint('Saving base64 bytes: $imageBase64'); + } + HomeWidget.saveWidgetData('lastDiscoveredImageData', imageBase64); + HomeWidget.updateWidget(iOSName: _appName); + } + @override void copyFromJson(Map value) { Map states = {}; diff --git a/lib/main.dart b/lib/main.dart index fcdb3f77..499218cc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -77,7 +77,7 @@ WondersLogic get wondersLogic => GetIt.I.get(); TimelineLogic get timelineLogic => GetIt.I.get(); SettingsLogic get settingsLogic => GetIt.I.get(); UnsplashLogic get unsplashLogic => GetIt.I.get(); -ArtifactAPILogic get metAPILogic => GetIt.I.get(); +ArtifactAPILogic get artifactLogic => GetIt.I.get(); CollectiblesLogic get collectiblesLogic => GetIt.I.get(); WallPaperLogic get wallpaperLogic => GetIt.I.get(); LocaleLogic get localeLogic => GetIt.I.get(); diff --git a/lib/ui/screens/artifact/artifact_details/artifact_details_screen.dart b/lib/ui/screens/artifact/artifact_details/artifact_details_screen.dart index f9ef8c04..fdde60d6 100644 --- a/lib/ui/screens/artifact/artifact_details/artifact_details_screen.dart +++ b/lib/ui/screens/artifact/artifact_details/artifact_details_screen.dart @@ -18,7 +18,7 @@ class ArtifactDetailsScreen extends StatefulWidget { } class _ArtifactDetailsScreenState extends State { - late final _future = metAPILogic.getArtifactByID(widget.artifactId, selfHosted: true); + late final _future = artifactLogic.getArtifactByID(widget.artifactId, selfHosted: true); @override Widget build(BuildContext context) { diff --git a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig index 20067ec2..858a4206 100644 --- a/macos/Flutter/ephemeral/Flutter-Generated.xcconfig +++ b/macos/Flutter/ephemeral/Flutter-Generated.xcconfig @@ -1,6 +1,6 @@ // This is a generated file; do not edit or check into version control. -FLUTTER_ROOT=C:\_dev\sdks\flutter -FLUTTER_APPLICATION_PATH=C:\_dev\gskinner\flutter_wonders_app +FLUTTER_ROOT=/Users/shawn/Dev/flutter +FLUTTER_APPLICATION_PATH=/Users/shawn/Dev/gskinner/flutter-wonders-app COCOAPODS_PARALLEL_CODE_SIGN=true FLUTTER_BUILD_DIR=build FLUTTER_BUILD_NAME=2.0.19 diff --git a/macos/Flutter/ephemeral/flutter_export_environment.sh b/macos/Flutter/ephemeral/flutter_export_environment.sh index 9337257f..5468a0a4 100755 --- a/macos/Flutter/ephemeral/flutter_export_environment.sh +++ b/macos/Flutter/ephemeral/flutter_export_environment.sh @@ -1,7 +1,7 @@ #!/bin/sh # This is a generated file; do not edit or check into version control. -export "FLUTTER_ROOT=C:\_dev\sdks\flutter" -export "FLUTTER_APPLICATION_PATH=C:\_dev\gskinner\flutter_wonders_app" +export "FLUTTER_ROOT=/Users/shawn/Dev/flutter" +export "FLUTTER_APPLICATION_PATH=/Users/shawn/Dev/gskinner/flutter-wonders-app" export "COCOAPODS_PARALLEL_CODE_SIGN=true" export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_NAME=2.0.19"