diff --git a/busyness.xcodeproj/project.pbxproj b/busyness.xcodeproj/project.pbxproj index 5d33c11..0a794f4 100644 --- a/busyness.xcodeproj/project.pbxproj +++ b/busyness.xcodeproj/project.pbxproj @@ -16,6 +16,11 @@ BDA9339624EFAF1500345985 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BDA9339424EFAF1500345985 /* LaunchScreen.storyboard */; }; BDA933A124EFAF1600345985 /* busynessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA933A024EFAF1600345985 /* busynessTests.swift */; }; BDA933AC24EFAF1600345985 /* busynessUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA933AB24EFAF1600345985 /* busynessUITests.swift */; }; + BDA933C024EFB1C900345985 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BDA933BF24EFB1C900345985 /* WidgetKit.framework */; }; + BDA933C224EFB1C900345985 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BDA933C124EFB1C900345985 /* SwiftUI.framework */; }; + BDA933C524EFB1C900345985 /* card.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDA933C424EFB1C900345985 /* card.swift */; }; + BDA933C724EFB1CF00345985 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BDA933C624EFB1CF00345985 /* Assets.xcassets */; }; + BDA933CB24EFB1CF00345985 /* cardExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = BDA933BD24EFB1C900345985 /* cardExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -33,8 +38,39 @@ remoteGlobalIDString = BDA9338324EFAF0600345985; remoteInfo = busyness; }; + BDA933C924EFB1CF00345985 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BDA9337C24EFAF0600345985 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BDA933BC24EFB1C900345985; + remoteInfo = cardExtension; + }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + BDA933CF24EFB1CF00345985 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + BDA933CB24EFB1CF00345985 /* cardExtension.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; + BDA933FD24F1C3F700345985 /* Embed Watch Content */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; + dstSubfolderSpec = 16; + files = ( + ); + name = "Embed Watch Content"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ BDA9338424EFAF0600345985 /* busyness.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = busyness.app; sourceTree = BUILT_PRODUCTS_DIR; }; BDA9338724EFAF0600345985 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -51,6 +87,14 @@ BDA933A724EFAF1600345985 /* busynessUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = busynessUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; BDA933AB24EFAF1600345985 /* busynessUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = busynessUITests.swift; sourceTree = ""; }; BDA933AD24EFAF1600345985 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BDA933BD24EFB1C900345985 /* cardExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = cardExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + BDA933BF24EFB1C900345985 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + BDA933C124EFB1C900345985 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; + BDA933C424EFB1C900345985 /* card.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = card.swift; sourceTree = ""; }; + BDA933C624EFB1CF00345985 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + BDA933C824EFB1CF00345985 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BDA933D024EFE28900345985 /* cardExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = cardExtension.entitlements; sourceTree = ""; }; + BDA933D124EFE2AA00345985 /* busyness.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = busyness.entitlements; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -75,15 +119,27 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BDA933BA24EFB1C900345985 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BDA933C224EFB1C900345985 /* SwiftUI.framework in Frameworks */, + BDA933C024EFB1C900345985 /* WidgetKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ BDA9337B24EFAF0600345985 = { isa = PBXGroup; children = ( + BDA933D024EFE28900345985 /* cardExtension.entitlements */, BDA9338624EFAF0600345985 /* busyness */, BDA9339F24EFAF1600345985 /* busynessTests */, BDA933AA24EFAF1600345985 /* busynessUITests */, + BDA933C324EFB1C900345985 /* card */, + BDA933BE24EFB1C900345985 /* Frameworks */, BDA9338524EFAF0600345985 /* Products */, ); sourceTree = ""; @@ -94,6 +150,7 @@ BDA9338424EFAF0600345985 /* busyness.app */, BDA9339C24EFAF1600345985 /* busynessTests.xctest */, BDA933A724EFAF1600345985 /* busynessUITests.xctest */, + BDA933BD24EFB1C900345985 /* cardExtension.appex */, ); name = Products; sourceTree = ""; @@ -101,6 +158,7 @@ BDA9338624EFAF0600345985 /* busyness */ = { isa = PBXGroup; children = ( + BDA933D124EFE2AA00345985 /* busyness.entitlements */, BDA9338724EFAF0600345985 /* AppDelegate.swift */, BDA9338924EFAF0600345985 /* SceneDelegate.swift */, BDA9338B24EFAF0600345985 /* ViewController.swift */, @@ -131,6 +189,25 @@ path = busynessUITests; sourceTree = ""; }; + BDA933BE24EFB1C900345985 /* Frameworks */ = { + isa = PBXGroup; + children = ( + BDA933BF24EFB1C900345985 /* WidgetKit.framework */, + BDA933C124EFB1C900345985 /* SwiftUI.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + BDA933C324EFB1C900345985 /* card */ = { + isa = PBXGroup; + children = ( + BDA933C424EFB1C900345985 /* card.swift */, + BDA933C624EFB1CF00345985 /* Assets.xcassets */, + BDA933C824EFB1CF00345985 /* Info.plist */, + ); + path = card; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -141,10 +218,13 @@ BDA9338024EFAF0600345985 /* Sources */, BDA9338124EFAF0600345985 /* Frameworks */, BDA9338224EFAF0600345985 /* Resources */, + BDA933CF24EFB1CF00345985 /* Embed App Extensions */, + BDA933FD24F1C3F700345985 /* Embed Watch Content */, ); buildRules = ( ); dependencies = ( + BDA933CA24EFB1CF00345985 /* PBXTargetDependency */, ); name = busyness; productName = busyness; @@ -187,6 +267,23 @@ productReference = BDA933A724EFAF1600345985 /* busynessUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; + BDA933BC24EFB1C900345985 /* cardExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = BDA933CC24EFB1CF00345985 /* Build configuration list for PBXNativeTarget "cardExtension" */; + buildPhases = ( + BDA933B924EFB1C900345985 /* Sources */, + BDA933BA24EFB1C900345985 /* Frameworks */, + BDA933BB24EFB1C900345985 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = cardExtension; + productName = cardExtension; + productReference = BDA933BD24EFB1C900345985 /* cardExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -207,6 +304,9 @@ CreatedOnToolsVersion = 12.0; TestTargetID = BDA9338324EFAF0600345985; }; + BDA933BC24EFB1C900345985 = { + CreatedOnToolsVersion = 12.0; + }; }; }; buildConfigurationList = BDA9337F24EFAF0600345985 /* Build configuration list for PBXProject "busyness" */; @@ -225,6 +325,7 @@ BDA9338324EFAF0600345985 /* busyness */, BDA9339B24EFAF1600345985 /* busynessTests */, BDA933A624EFAF1600345985 /* busynessUITests */, + BDA933BC24EFB1C900345985 /* cardExtension */, ); }; /* End PBXProject section */ @@ -254,6 +355,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BDA933BB24EFB1C900345985 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BDA933C724EFB1CF00345985 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -284,6 +393,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BDA933B924EFB1C900345985 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BDA933C524EFB1C900345985 /* card.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -297,6 +414,11 @@ target = BDA9338324EFAF0600345985 /* busyness */; targetProxy = BDA933A824EFAF1600345985 /* PBXContainerItemProxy */; }; + BDA933CA24EFB1CF00345985 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BDA933BC24EFB1C900345985 /* cardExtension */; + targetProxy = BDA933C924EFB1CF00345985 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -438,8 +560,10 @@ BDA933B124EFAF1600345985 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = busyness/busyness.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = PRBH2T6668; INFOPLIST_FILE = busyness/Info.plist; @@ -449,6 +573,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = dev.mpg13.busyness; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -457,8 +582,10 @@ BDA933B224EFAF1600345985 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = busyness/busyness.entitlements; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = PRBH2T6668; INFOPLIST_FILE = busyness/Info.plist; @@ -468,6 +595,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = dev.mpg13.busyness; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTS_MACCATALYST = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -557,6 +685,52 @@ }; name = Release; }; + BDA933CD24EFB1CF00345985 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CODE_SIGN_ENTITLEMENTS = cardExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = PRBH2T6668; + INFOPLIST_FILE = card/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.mpg13.busyness.card; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,6"; + }; + name = Debug; + }; + BDA933CE24EFB1CF00345985 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CODE_SIGN_ENTITLEMENTS = cardExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = PRBH2T6668; + INFOPLIST_FILE = card/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = dev.mpg13.busyness.card; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SUPPORTS_MACCATALYST = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,6"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -596,6 +770,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + BDA933CC24EFB1CF00345985 /* Build configuration list for PBXNativeTarget "cardExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BDA933CD24EFB1CF00345985 /* Debug */, + BDA933CE24EFB1CF00345985 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = BDA9337C24EFAF0600345985 /* Project object */; diff --git a/busyness.xcodeproj/xcuserdata/micahgomez.xcuserdatad/xcschemes/xcschememanagement.plist b/busyness.xcodeproj/xcuserdata/micahgomez.xcuserdatad/xcschemes/xcschememanagement.plist index 1247ee6..c47851e 100644 --- a/busyness.xcodeproj/xcuserdata/micahgomez.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/busyness.xcodeproj/xcuserdata/micahgomez.xcuserdatad/xcschemes/xcschememanagement.plist @@ -9,6 +9,26 @@ orderHint 0 + cardExtension.xcscheme_^#shared#^_ + + orderHint + 1 + + watchcard (Complication).xcscheme_^#shared#^_ + + orderHint + 4 + + watchcard (Notification).xcscheme_^#shared#^_ + + orderHint + 3 + + watchcard.xcscheme_^#shared#^_ + + orderHint + 2 + diff --git a/busyness/Base.lproj/Main.storyboard b/busyness/Base.lproj/Main.storyboard index 25a7638..9a26a01 100644 --- a/busyness/Base.lproj/Main.storyboard +++ b/busyness/Base.lproj/Main.storyboard @@ -1,24 +1,126 @@ - + + - + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/busyness/ViewController.swift b/busyness/ViewController.swift index 8826d14..241abdd 100644 --- a/busyness/ViewController.swift +++ b/busyness/ViewController.swift @@ -7,13 +7,237 @@ import UIKit -class ViewController: UIViewController { - - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view. +extension UserDefaults { + func colorForKey(key: String) -> UIColor? { + var colorReturnded: UIColor? + if let colorData = data(forKey: key) { + do { + if let color = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(colorData) as? UIColor { + colorReturnded = color + } + } catch { + print("Error UserDefaults") + } } - - + return colorReturnded + } + + func setColor(color: UIColor?, forKey key: String) { + var colorData: NSData? + if let color = color { + do { + let data = try NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: false) as NSData? + colorData = data + } catch { + print("Error UserDefaults") + } + } + set(colorData, forKey: key) + } +} + +extension CIImage { + /// Inverts the colors and creates a transparent image by converting the mask to alpha. + /// Input image should be black and white. + var transparent: CIImage? { + return inverted?.blackTransparent + } + + /// Inverts the colors. + var inverted: CIImage? { + guard let invertedColorFilter = CIFilter(name: "CIColorInvert") else { return nil } + + invertedColorFilter.setValue(self, forKey: "inputImage") + return invertedColorFilter.outputImage + } + + /// Converts all black to transparent. + var blackTransparent: CIImage? { + guard let blackTransparentFilter = CIFilter(name: "CIMaskToAlpha") else { return nil } + blackTransparentFilter.setValue(self, forKey: "inputImage") + return blackTransparentFilter.outputImage + } + + /// Applies the given color as a tint color. + func tinted(using color: UIColor) -> CIImage? + { + guard + let transparentQRImage = transparent, + let filter = CIFilter(name: "CIMultiplyCompositing"), + let colorFilter = CIFilter(name: "CIConstantColorGenerator") else { return nil } + + let ciColor = CIColor(color: color) + colorFilter.setValue(ciColor, forKey: kCIInputColorKey) + let colorImage = colorFilter.outputImage + + filter.setValue(colorImage, forKey: kCIInputImageKey) + filter.setValue(transparentQRImage, forKey: kCIInputBackgroundImageKey) + + return filter.outputImage! + } +} + +extension URL { + + /// Creates a QR code for the current URL in the given color. + func qrImage(using color: UIColor) -> CIImage? { + return qrImage?.tinted(using: color) + } + + /// Returns a black and white QR code for this URL. + var qrImage: CIImage? { + guard let qrFilter = CIFilter(name: "CIQRCodeGenerator") else { return nil } + let qrData = absoluteString.data(using: String.Encoding.ascii) + qrFilter.setValue(qrData, forKey: "inputMessage") + + let qrTransform = CGAffineTransform(scaleX: 12, y: 12) + return qrFilter.outputImage?.transformed(by: qrTransform) + } +} + +class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UIColorPickerViewControllerDelegate { + + //let prefs = UserDefaults(suiteName:"group.dev.mpg13.busyness") + let defaults = UserDefaults(suiteName:"group.dev.mpg13.busyness") + + @IBOutlet var fullView: UIView! + + + @IBOutlet var cardPhoto: UIButton! + + @IBOutlet var qrCodeImage: UIButton! + + @IBOutlet var nameTextField: UITextField! + + @IBOutlet var phoneTextField: UITextField! + + @IBOutlet var emailTextField: UITextField! + + let imagePicker = UIImagePickerController() + let colorPicker = UIColorPickerViewController() + + override func viewDidLoad() { + super.viewDidLoad() + imagePicker.delegate = self + colorPicker.delegate = self + + if(defaults?.colorForKey(key: "savedColor") != nil){ + fullView.backgroundColor = defaults?.colorForKey(key: "savedColor") + } + + let qrURL = defaults?.string(forKey: "savedQRURL") + if ((qrURL == nil) || (qrURL == "")) { + let defaultQR = UIImage(systemName: "qrcode") + qrCodeImage.setBackgroundImage(defaultQR, for: qrCodeImage.state) + } else { + let qrCode = URL(string: qrURL!)?.qrImage(using: UIColor.black) + qrCodeImage.setBackgroundImage(UIImage(ciImage: qrCode!), for: qrCodeImage.state) + } + + nameTextField.text = defaults?.string(forKey: "savedCardName") + phoneTextField.text = defaults?.string(forKey: "savedCardPhone") + emailTextField.text = defaults?.string(forKey: "savedCardEmail") + if let cardImageData = defaults?.data(forKey: "savedCardPhoto"){ + cardPhoto.setBackgroundImage(UIImage(data: cardImageData), for: cardPhoto.state)} + + // Do any additional setup after loading the view. + } + + @IBAction func nameTextFieldEntered(_ sender: Any) { + defaults?.setValue(nameTextField.text, forKey: "savedCardName") + } + + @IBAction func phoneTextFieldEntered(_ sender: Any) { + defaults?.setValue(phoneTextField.text, forKey: "savedCardPhone") + } + + @IBAction func emailTextFieldEntered(_ sender: Any) { + defaults?.setValue(emailTextField.text, forKey: "savedCardEmail") + } + + @IBAction func cardPhotoPressed(_ sender: Any) { + imagePicker.allowsEditing = true + imagePicker.sourceType = .photoLibrary + + present(imagePicker, animated: true, completion: nil) + } + + @IBAction func qrCodeButtonPressed(_ sender: Any) { + + + + var alertController:UIAlertController? + alertController = UIAlertController(title: "Enter a URL Below", + message: "Text entered below will be turned into a QR code, which can be used as a quick link from your card.", + preferredStyle: .alert) + + alertController!.addTextField( + configurationHandler: {(textField: UITextField!) in + textField.placeholder = "https://micahpgomez.dev" + }) + + let action = UIAlertAction(title: "Submit", + style: UIAlertAction.Style.default, + handler: {[weak self] + (paramAction:UIAlertAction!) in + if let textFields = alertController?.textFields{ + let theTextFields = textFields as [UITextField] + let enteredText = theTextFields[0].text + self?.defaults?.setValue(enteredText, forKey: "savedQRURL") + + if (enteredText == "") { + let defaultQR = UIImage(systemName: "qrcode") + self!.qrCodeImage.setBackgroundImage(defaultQR, for: self!.qrCodeImage.state) + } else { + let qrCode = URL(string: enteredText!)?.qrImage(using: UIColor.black) + self!.qrCodeImage.setBackgroundImage(UIImage(ciImage: qrCode!), for: self!.qrCodeImage.state) + self!.defaults?.setValue(UIImage(ciImage: qrCode!).pngData(), forKey: "savedQRPhoto") + } + + } + }) + + + + alertController?.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) + alertController?.addAction(action) + self.present(alertController!, + animated: true, + completion: nil) + } + + @IBAction func qrButtonLongPressed(_ sender: Any) { + guard let url = URL(string: defaults?.string(forKey: "savedQRURL")! ?? "") else { return } + UIApplication.shared.open(url) + } + + @IBAction func colorPickerButton(_ sender: Any) { + present(colorPicker, animated: true, completion: nil) + } + + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { + if let pickedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage { + cardPhoto.setBackgroundImage(pickedImage, for: cardPhoto.state) + defaults?.setValue(pickedImage.pngData(), forKey: "savedCardPhoto") + } + + dismiss(animated: true, completion: nil) + } + + func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { + dismiss(animated: true, completion: nil) + } + + func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) { + dismiss(animated: true, completion: nil) + } + + func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) { + let color = viewController.selectedColor + + fullView.backgroundColor = color + defaults?.setColor(color: color, forKey: "savedColor") + } + } diff --git a/busyness/busyness.entitlements b/busyness/busyness.entitlements new file mode 100644 index 0000000..9bf5643 --- /dev/null +++ b/busyness/busyness.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.application-groups + + group.dev.mpg13.busyness + + com.apple.security.network.client + + + diff --git a/card/Assets.xcassets/AccentColor.colorset/Contents.json b/card/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/card/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/card/Assets.xcassets/AppIcon.appiconset/Contents.json b/card/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/card/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/card/Assets.xcassets/Contents.json b/card/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/card/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/card/Assets.xcassets/WidgetBackground.colorset/Contents.json b/card/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/card/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/card/Info.plist b/card/Info.plist new file mode 100644 index 0000000..e641646 --- /dev/null +++ b/card/Info.plist @@ -0,0 +1,29 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + card + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/card/card.swift b/card/card.swift new file mode 100644 index 0000000..a81af00 --- /dev/null +++ b/card/card.swift @@ -0,0 +1,230 @@ +// +// card.swift +// card +// +// Created by Micah Gomez on 8/21/20. +// + +import WidgetKit +import SwiftUI + +extension UserDefaults { + func colorForKey(key: String) -> UIColor? { + var colorReturnded: UIColor? + if let colorData = data(forKey: key) { + do { + if let color = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(colorData) as? UIColor { + colorReturnded = color + } + } catch { + print("Error UserDefaults") + } + } + return colorReturnded + } + + func setColor(color: UIColor?, forKey key: String) { + var colorData: NSData? + if let color = color { + do { + let data = try NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: false) as NSData? + colorData = data + } catch { + print("Error UserDefaults") + } + } + set(colorData, forKey: key) + } +} + + +struct Provider: TimelineProvider { + func placeholder(in context: Context) -> SimpleEntry { + SimpleEntry(date: Date(), cardName: "Johnny Appleseed", cardPhone: "(123)456-7890", cardEmail: "johnny@appleseed.com", cardPhoto: UIImage(systemName: "person.badge.plus")!, cardQR: UIImage(systemName: "qrcode")!) + } + + func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { + let entry = SimpleEntry(date: Date(), cardName: "Johnny Appleseed", cardPhone: "(123)456-7890", cardEmail: "johnny@appleseed.com", cardPhoto: UIImage(systemName: "person.badge.plus")!, cardQR: UIImage(systemName: "qrcode")!) + completion(entry) + } + + func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { + + let defaults = UserDefaults(suiteName:"group.dev.mpg13.busyness") + var cardName: String + if defaults?.string(forKey: "savedCardName") != nil { + cardName = (defaults?.string(forKey: "savedCardName"))! + } else { + cardName = "Johnny Appleseed" + } + let cardPhone = defaults?.string(forKey: "savedCardPhone") ?? "Phone/Other" + let cardEmail = defaults?.string(forKey: "savedCardEmail") ?? "Email" + var cardPhoto: UIImage + if defaults?.data(forKey: "savedCardPhoto") != nil { + cardPhoto = UIImage(data: (defaults?.data(forKey: "savedCardPhoto"))!)! + } else { + cardPhoto = UIImage(systemName: "person.badge.plus")! + } + var cardQR: UIImage + if defaults?.data(forKey: "savedQRPhoto") != nil { + cardQR = UIImage(data: (defaults?.data(forKey: "savedQRPhoto"))!)! + } else { + cardQR = UIImage(systemName: "qrcode")! + } + + let date = Date() + let entry = SimpleEntry( + date: date, + cardName: cardName, + cardPhone: cardPhone, + cardEmail: cardEmail, + cardPhoto: cardPhoto, + cardQR: cardQR + ) + let nextUpdateDate = Calendar.current.date(byAdding: .minute, value: 15, to: date)! + let timeline = Timeline( + entries:[entry], + policy: .after(nextUpdateDate) + ) + + + /* + let currentDate = Date() + for minuteOffset in 0 ..< 5 { + let entryDate = Calendar.current.date(byAdding: .minute, value: minuteOffset, to: currentDate)! + print(entryDate) + let entry = SimpleEntry(date: entryDate) + entries.append(entry) + } + + let timeline = Timeline(entries: entries, policy: .atEnd)*/ + + completion(timeline) + } +} + +struct SimpleEntry: TimelineEntry { + let date: Date + let cardName: String + let cardPhone: String + let cardEmail: String + let cardPhoto: UIImage + let cardQR: UIImage +} + +struct cardSmallView : View { + var entry: Provider.Entry + + var body: some View{ + VStack{ + Spacer() + Text(entry.cardName).fontWeight(.heavy).lineLimit(2).minimumScaleFactor(0.5) + HStack{ + Image(uiImage: entry.cardPhoto).resizable().scaledToFit().cornerRadius(16) + Image(uiImage: entry.cardQR).resizable().scaledToFit() + } + Spacer() + } + } +} + +struct cardMediumView : View { + var entry: Provider.Entry + + var body: some View{ + GeometryReader{g in + VStack(spacing: 5){ + Spacer() + Text(entry.cardName).fontWeight(.heavy).lineLimit(1).font(.system(size: 24)).minimumScaleFactor(0.5).multilineTextAlignment(.center).padding(.top) + HStack{ + Image(uiImage: entry.cardPhoto).resizable() + .frame(width: g.size.width / 5, height: g.size.width / 5, alignment: .bottomLeading).cornerRadius(16) + Image(uiImage: entry.cardQR).resizable() + .frame(width: g.size.width / 5, height: g.size.width / 5, alignment: .bottomLeading) + VStack{ + Spacer() + Text(entry.cardPhone).lineLimit(1).font(.system(size: 18, design: .rounded)).minimumScaleFactor(0.3) + Text(entry.cardEmail).lineLimit(1).font(.system(size: 18, design: .rounded)).minimumScaleFactor(0.6) + Spacer() + }.padding(.bottom) + } + Spacer() + } + } + } +} + +struct cardLargeView : View { + var entry: Provider.Entry + + var body: some View{ + VStack{ + Spacer() + Text(entry.cardName).fontWeight(.heavy).lineLimit(2).font(.system(size: 36)).minimumScaleFactor(0.5) + Spacer() + HStack{ + Image(uiImage: entry.cardPhoto).resizable().scaledToFit().cornerRadius(16) + Image(uiImage: entry.cardQR).resizable().scaledToFit() + } + Spacer() + Text(entry.cardPhone).lineLimit(1).font(.system(size: 20, design: .rounded)).minimumScaleFactor(0.5) + Spacer() + Text(entry.cardEmail).lineLimit(1).font(.system(size: 20, design: .rounded)).minimumScaleFactor(0.5) + Spacer() + } + } +} + +struct detailsNotAvailable : View { + var entry: Provider.Entry + + var body: some View{ + VStack{ + Text("Nothing Available") + } + } +} + +struct cardEntryView : View { + var entry: Provider.Entry + @Environment(\.widgetFamily) var family: WidgetFamily + + let defaults = UserDefaults(suiteName:"group.dev.mpg13.busyness") + let viewColor = UserDefaults(suiteName:"group.dev.mpg13.busyness")?.colorForKey(key: "savedColor") + + var body: some View { + ZStack + { + Color(viewColor ?? UIColor.white) + .edgesIgnoringSafeArea(.all) + Group{ + switch family { + case .systemSmall: cardSmallView(entry: entry) + case .systemMedium: cardMediumView(entry: entry) + case .systemLarge: cardLargeView(entry: entry) + default: detailsNotAvailable(entry: entry) + } + }.padding(.horizontal) + } + } +} + +@main +struct card: Widget { + let kind: String = "card" + + var body: some WidgetConfiguration { + StaticConfiguration(kind: kind, provider: Provider()) { entry in + cardEntryView(entry: entry) + } + .configurationDisplayName("My Widget") + .description("This is an example widget.") + } +} + +struct card_Previews: PreviewProvider { + static var previews: some View { + cardEntryView(entry: SimpleEntry(date: Date(), cardName: "Johnny Appleseed", cardPhone: "(123)456-7890", cardEmail: "johnny@appleseed.com", cardPhoto: UIImage(systemName: "person.badge.plus")!, cardQR: UIImage(systemName: "qrcode")!)) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + } +} diff --git a/cardExtension.entitlements b/cardExtension.entitlements new file mode 100644 index 0000000..9bf5643 --- /dev/null +++ b/cardExtension.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.application-groups + + group.dev.mpg13.busyness + + com.apple.security.network.client + + +