From faf98a135dc349b55e4ba235426497ed863bee3f Mon Sep 17 00:00:00 2001 From: panxi Date: Mon, 25 Nov 2019 11:42:38 -0500 Subject: [PATCH 01/15] move project location, fix textview capitalization --- JSONCreator.xcworkspace/contents.xcworkspacedata | 4 ++-- JSONCreator_iOS/JSONCreator/DetailViewController.swift | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/JSONCreator.xcworkspace/contents.xcworkspacedata b/JSONCreator.xcworkspace/contents.xcworkspacedata index 8a88220..21fc6d2 100644 --- a/JSONCreator.xcworkspace/contents.xcworkspacedata +++ b/JSONCreator.xcworkspace/contents.xcworkspacedata @@ -2,10 +2,10 @@ + location = "group:mvm_core/MVMCore/MVMCore.xcodeproj"> + location = "group:mvm_core_ui/MVMCoreUI.xcodeproj"> diff --git a/JSONCreator_iOS/JSONCreator/DetailViewController.swift b/JSONCreator_iOS/JSONCreator/DetailViewController.swift index 09f818b..59f8ce5 100644 --- a/JSONCreator_iOS/JSONCreator/DetailViewController.swift +++ b/JSONCreator_iOS/JSONCreator/DetailViewController.swift @@ -34,6 +34,7 @@ class DetailViewController: UIViewController { textView.smartDashesType = .no textView.smartQuotesType = .no textView.smartInsertDeleteType = .no + textView.autocapitalizationType = .none let shareButton = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(shareButtonPressed)) let buildButton = UIBarButtonItem(barButtonSystemItem: .play, target: self, action: #selector(play)) @@ -56,7 +57,9 @@ class DetailViewController: UIViewController { @objc func play() { do { + let decoder = JSONDecoder() if let data = textView.text.data(using: .utf8), let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as? [AnyHashable: Any] { + let pageModel = try decoder.decode(PageModel.self, from: data) let page = jsonObject.optionalDictionaryForKey(KeyPage) let pageType = page?.optionalStringForKey(KeyPageType) let template = page?.optionalStringForKey("template") From 4d0e3b651ee8c9c310328ef23cb734c3d12d9884 Mon Sep 17 00:00:00 2001 From: panxi Date: Mon, 25 Nov 2019 11:45:01 -0500 Subject: [PATCH 02/15] remove test code --- JSONCreator_iOS/JSONCreator/DetailViewController.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/JSONCreator_iOS/JSONCreator/DetailViewController.swift b/JSONCreator_iOS/JSONCreator/DetailViewController.swift index 59f8ce5..d9705bb 100644 --- a/JSONCreator_iOS/JSONCreator/DetailViewController.swift +++ b/JSONCreator_iOS/JSONCreator/DetailViewController.swift @@ -57,9 +57,7 @@ class DetailViewController: UIViewController { @objc func play() { do { - let decoder = JSONDecoder() if let data = textView.text.data(using: .utf8), let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as? [AnyHashable: Any] { - let pageModel = try decoder.decode(PageModel.self, from: data) let page = jsonObject.optionalDictionaryForKey(KeyPage) let pageType = page?.optionalStringForKey(KeyPageType) let template = page?.optionalStringForKey("template") From f973b442c9b3777c087a8d213d6b45a96b2bf43c Mon Sep 17 00:00:00 2001 From: panxi Date: Tue, 26 Nov 2019 13:25:37 -0500 Subject: [PATCH 03/15] update --- JSONCreator_iOS/JSONCreator/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/JSONCreator_iOS/JSONCreator/AppDelegate.swift b/JSONCreator_iOS/JSONCreator/AppDelegate.swift index 364d09f..d9fe595 100644 --- a/JSONCreator_iOS/JSONCreator/AppDelegate.swift +++ b/JSONCreator_iOS/JSONCreator/AppDelegate.swift @@ -20,7 +20,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { // Setup our core object with the default implementation - MVMCoreUIObject.sharedInstance()?.defaultInitialSetup() + CoreUIObject.sharedInstance()?.defaultInitialSetup() return true } From 74be312bf048b9dfb3fbf72f598da0a49c31df5b Mon Sep 17 00:00:00 2001 From: Kevin G Christiano Date: Tue, 18 Feb 2020 15:52:22 -0500 Subject: [PATCH 04/15] firendly ipad --- JSONCreator_iOS/JSONCreator/DetailViewController.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/JSONCreator_iOS/JSONCreator/DetailViewController.swift b/JSONCreator_iOS/JSONCreator/DetailViewController.swift index d9705bb..67e10f1 100644 --- a/JSONCreator_iOS/JSONCreator/DetailViewController.swift +++ b/JSONCreator_iOS/JSONCreator/DetailViewController.swift @@ -19,6 +19,10 @@ class DetailViewController: UIViewController { guard textView.superview == nil else { return } + + modalPresentationStyle = .formSheet + MVMCoreNavigationHandler.shared()?.viewControllerToPresentOn = self + view.addSubview(textView) if UIDevice.current.userInterfaceIdiom == .pad { textView.font = UIFont.systemFont(ofSize: 40) From d080cbab69f2aef261b0f1296060548a57b07815 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Tue, 2 Aug 2022 10:08:04 -0500 Subject: [PATCH 05/15] Signed-off-by: Matt Bruce --- .../contents.xcworkspacedata | 8 +- .../JSONCreator.xcodeproj/project.pbxproj | 258 ++++++- .../5G/BluetoothPairBehavior.swift | 9 + .../BluetoothDebuggableProtocol.swift | 43 ++ .../5G/Debugger/BluetoothDebugger.swift | 327 +++++++++ .../5G/Debugger/BluetoothDebuggerView.swift | 96 +++ .../5G/Debugger/MulticastDelegate.swift | 56 ++ .../5G/KeyedDecodingContainer+Decode.swift | 59 ++ .../5G/MFFGHSAnalyticsProtocol.swift | 217 ++++++ .../JSONCreator/5G/MFFGHSBluetoothPair.swift | 547 +++++++++++++++ .../JSONCreator/5G/MFFGHSUtility.swift | 17 + .../5G/Models/BluetoothConfigModel.swift | 103 +++ .../5G/Models/CharacteristicModel.swift | 44 ++ .../5G/Models/PeripheralModel.swift | 34 + .../JSONCreator/5G/Models/ServiceModel.swift | 44 ++ .../Protocols/BluetoothPairableProtocol.swift | 65 ++ .../Protocols/BluetoothPairingProtocol.swift | 32 + .../GMFG5GCBandSignalHandler.swift | 105 +++ .../BLE Handlers/GMFG5GSignalHandler.swift | 111 +++ .../BLE Handlers/GMFGFotaHandler.swift | 132 ++++ .../BLE Handlers/GMFGOperationHandler.swift | 34 + .../GMFGPublicInternetAccessHandler.swift | 47 ++ .../BLE Handlers/GMFGRouterWifiHandler.swift | 51 ++ .../BLE Handlers/GMFGSpeedTestHandler.swift | 141 ++++ .../5G/Utility/GMFGBluetoothPair.swift | 509 ++++++++++++++ .../JSONCreator/5G/Utility/GMFGConstant.swift | 87 +++ .../5G/Utility/GMFGLocationManager.swift | 99 +++ .../GMFGMapARViewControllerUtilities.swift | 123 ++++ .../Utility/GMFGSelfInstallTimeTracker.swift | 179 +++++ .../5G/Utility/GMFGStorageManager.swift | 50 ++ .../5G/Utility/GMFGTestScreen.swift | 228 +++++++ .../5G/Utility/GMFGTestScreenData.swift | 30 + .../Protocols/GMFGBLEHandlerProtocol.swift | 21 + JSONCreator_iOS/JSONCreator/AppDelegate.swift | 57 +- .../JSONCreator/DecodableDefaults+VDS.swift | 117 ++++ .../JSON/Samples/5G-AccordianList.json | 215 ++++++ .../JSON/Samples/FormContactInfo.json | 10 +- .../JSON/Samples/MVA3.0/RadioButtons.json | 159 +++-- .../JSON/Samples/RadioButtonGroup.json | 112 +++ .../JSONCreator/JSON/Samples/TabsSample.json | 3 +- .../JSONCreator/JSON/Samples/Wifi/Wifi-1.json | 637 ++++++++++++++++++ .../JSONCreator/JSON/Samples/Wifi/Wifi-2.json | 357 ++++++++++ .../JSON/Samples/accordionViews.json | 67 ++ .../MF/JSONCreatorActionHandler.swift | 2 +- JSONCreator_iOS/JSONCreator/TestToggle.swift | 101 +++ .../JSONCreator/TestToggleModel.swift | 202 ++++++ .../JSONCreator/WifiViewController.swift | 112 +++ JSONCreator_iOS/JSONCreator/WifiWidget.swift | 207 ++++++ .../JSONCreator/WifiWidgetModel.swift | 115 ++++ 49 files changed, 6278 insertions(+), 101 deletions(-) create mode 100644 JSONCreator_iOS/JSONCreator/5G/BluetoothPairBehavior.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Debugger/BluetoothDebuggableProtocol.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Debugger/BluetoothDebugger.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Debugger/BluetoothDebuggerView.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Debugger/MulticastDelegate.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/KeyedDecodingContainer+Decode.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/MFFGHSAnalyticsProtocol.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/MFFGHSBluetoothPair.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/MFFGHSUtility.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Models/BluetoothConfigModel.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Models/CharacteristicModel.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Models/PeripheralModel.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Models/ServiceModel.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Protocols/BluetoothPairableProtocol.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Protocols/BluetoothPairingProtocol.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFG5GCBandSignalHandler.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFG5GSignalHandler.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGFotaHandler.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGOperationHandler.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGPublicInternetAccessHandler.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGRouterWifiHandler.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGSpeedTestHandler.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/GMFGBluetoothPair.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/GMFGConstant.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/GMFGLocationManager.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/GMFGMapARViewControllerUtilities.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/GMFGSelfInstallTimeTracker.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/GMFGStorageManager.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/GMFGTestScreen.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/GMFGTestScreenData.swift create mode 100644 JSONCreator_iOS/JSONCreator/5G/Utility/Protocols/GMFGBLEHandlerProtocol.swift create mode 100644 JSONCreator_iOS/JSONCreator/DecodableDefaults+VDS.swift create mode 100644 JSONCreator_iOS/JSONCreator/JSON/Samples/5G-AccordianList.json create mode 100644 JSONCreator_iOS/JSONCreator/JSON/Samples/RadioButtonGroup.json create mode 100644 JSONCreator_iOS/JSONCreator/JSON/Samples/Wifi/Wifi-1.json create mode 100644 JSONCreator_iOS/JSONCreator/JSON/Samples/Wifi/Wifi-2.json create mode 100644 JSONCreator_iOS/JSONCreator/JSON/Samples/accordionViews.json create mode 100644 JSONCreator_iOS/JSONCreator/TestToggle.swift create mode 100644 JSONCreator_iOS/JSONCreator/TestToggleModel.swift create mode 100644 JSONCreator_iOS/JSONCreator/WifiViewController.swift create mode 100644 JSONCreator_iOS/JSONCreator/WifiWidget.swift create mode 100644 JSONCreator_iOS/JSONCreator/WifiWidgetModel.swift diff --git a/JSONCreator.xcworkspace/contents.xcworkspacedata b/JSONCreator.xcworkspace/contents.xcworkspacedata index 8a88220..a714f9d 100644 --- a/JSONCreator.xcworkspace/contents.xcworkspacedata +++ b/JSONCreator.xcworkspace/contents.xcworkspacedata @@ -2,11 +2,17 @@ + location = "group:VDSSample/VDSSample.xcodeproj"> + + + + diff --git a/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj b/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj index 3b29429..2f12d6d 100644 --- a/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj +++ b/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 52; objects = { /* Begin PBXAggregateTarget section */ @@ -36,16 +36,57 @@ D2B1E3FC22F4A6930065F95C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D2B1E3FB22F4A6930065F95C /* Assets.xcassets */; }; D2B1E3FF22F4A6930065F95C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D2B1E3FD22F4A6930065F95C /* LaunchScreen.storyboard */; }; D2B1E40A22F4C9F00065F95C /* JSON in Resources */ = {isa = PBXBuildFile; fileRef = D2B1E40922F4C9F00065F95C /* JSON */; }; - D2B1E52922FA10BA0065F95C /* MVMAnimationFramework.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2B1E51222F9F9590065F95C /* MVMAnimationFramework.framework */; }; - D2B1E52A22FA10BA0065F95C /* MVMAnimationFramework.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D2B1E51222F9F9590065F95C /* MVMAnimationFramework.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; D2FC4FAD25897ACB00061EA4 /* StepModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FC4FA925897ACB00061EA4 /* StepModel.swift */; }; D2FC4FAE25897ACB00061EA4 /* OrderTrackerModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FC4FAA25897ACB00061EA4 /* OrderTrackerModel.swift */; }; D2FC4FAF25897ACB00061EA4 /* Step.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FC4FAB25897ACB00061EA4 /* Step.swift */; }; D2FC4FB025897ACB00061EA4 /* OrderTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = D2FC4FAC25897ACB00061EA4 /* OrderTracker.swift */; }; - EA7E676B27582F2200ABF773 /* MVMCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA7E676927582F2200ABF773 /* MVMCore.framework */; }; - EA7E676C27582F2200ABF773 /* MVMCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EA7E676927582F2200ABF773 /* MVMCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - EA7E676D27582F2200ABF773 /* MVMCoreUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA7E676A27582F2200ABF773 /* MVMCoreUI.framework */; }; - EA7E676E27582F2200ABF773 /* MVMCoreUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EA7E676A27582F2200ABF773 /* MVMCoreUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + EA09CDBD282C3FD800A7835F /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA09CDBC282C3FD800A7835F /* CoreBluetooth.framework */; }; + EA09CDD1282C40CC00A7835F /* MFFGHSBluetoothPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDBE282C40CB00A7835F /* MFFGHSBluetoothPair.swift */; }; + EA09CDD2282C40CC00A7835F /* GMFGBluetoothPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDC0282C40CB00A7835F /* GMFGBluetoothPair.swift */; }; + EA09CDD6282C40CC00A7835F /* GMFGConstant.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDC4282C40CB00A7835F /* GMFGConstant.swift */; }; + EA09CDD8282C40CC00A7835F /* GMFGBLEHandlerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDC7282C40CC00A7835F /* GMFGBLEHandlerProtocol.swift */; }; + EA09CDD9282C40CC00A7835F /* GMFG5GCBandSignalHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDC9282C40CC00A7835F /* GMFG5GCBandSignalHandler.swift */; }; + EA09CDDA282C40CC00A7835F /* GMFGOperationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDCA282C40CC00A7835F /* GMFGOperationHandler.swift */; }; + EA09CDDB282C40CC00A7835F /* GMFGPublicInternetAccessHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDCB282C40CC00A7835F /* GMFGPublicInternetAccessHandler.swift */; }; + EA09CDDC282C40CC00A7835F /* GMFGFotaHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDCC282C40CC00A7835F /* GMFGFotaHandler.swift */; }; + EA09CDDD282C40CC00A7835F /* GMFGSpeedTestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDCD282C40CC00A7835F /* GMFGSpeedTestHandler.swift */; }; + EA09CDDE282C40CC00A7835F /* GMFG5GSignalHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDCE282C40CC00A7835F /* GMFG5GSignalHandler.swift */; }; + EA09CDDF282C40CC00A7835F /* GMFGRouterWifiHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDCF282C40CC00A7835F /* GMFGRouterWifiHandler.swift */; }; + EA09CDE6282C416C00A7835F /* BluetoothDebuggableProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDE2282C416C00A7835F /* BluetoothDebuggableProtocol.swift */; }; + EA09CDE7282C416C00A7835F /* MulticastDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDE3282C416C00A7835F /* MulticastDelegate.swift */; }; + EA09CDE8282C416C00A7835F /* BluetoothDebugger.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDE4282C416C00A7835F /* BluetoothDebugger.swift */; }; + EA09CDE9282C416C00A7835F /* BluetoothDebuggerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDE5282C416C00A7835F /* BluetoothDebuggerView.swift */; }; + EA09CDEB282C422900A7835F /* GMFGStorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDEA282C422900A7835F /* GMFGStorageManager.swift */; }; + EA09CDED282C423F00A7835F /* GMFGLocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDEC282C423E00A7835F /* GMFGLocationManager.swift */; }; + EA09CDEF282C429800A7835F /* GMFGTestScreenData.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDEE282C429800A7835F /* GMFGTestScreenData.swift */; }; + EA09CDF8282C430400A7835F /* BluetoothPairingProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDF1282C430400A7835F /* BluetoothPairingProtocol.swift */; }; + EA09CDF9282C430400A7835F /* BluetoothPairableProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDF2282C430400A7835F /* BluetoothPairableProtocol.swift */; }; + EA09CDFA282C430400A7835F /* BluetoothConfigModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDF4282C430400A7835F /* BluetoothConfigModel.swift */; }; + EA09CDFB282C430400A7835F /* PeripheralModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDF5282C430400A7835F /* PeripheralModel.swift */; }; + EA09CDFC282C430400A7835F /* CharacteristicModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDF6282C430400A7835F /* CharacteristicModel.swift */; }; + EA09CDFD282C430400A7835F /* ServiceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDF7282C430400A7835F /* ServiceModel.swift */; }; + EA09CDFF282C437C00A7835F /* MFFGHSAnalyticsProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CDFE282C437C00A7835F /* MFFGHSAnalyticsProtocol.swift */; }; + EA09CE01282C43E800A7835F /* KeyedDecodingContainer+Decode.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CE00282C43E800A7835F /* KeyedDecodingContainer+Decode.swift */; }; + EA09CE03282C44A100A7835F /* MFFGHSUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CE02282C44A100A7835F /* MFFGHSUtility.swift */; }; + EA09CE05282C45C200A7835F /* BluetoothPairBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CE04282C45C200A7835F /* BluetoothPairBehavior.swift */; }; + EA1B7BBD2893459E006AF0BC /* DecodableDefaults+VDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1B7BBC2893459E006AF0BC /* DecodableDefaults+VDS.swift */; }; + EA33618B288B1B630071C351 /* VDS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA33618A288B1B630071C351 /* VDS.framework */; }; + EA33618C288B1B630071C351 /* VDS.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EA33618A288B1B630071C351 /* VDS.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + EA3361C1288B37FB0071C351 /* TestToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361C0288B37FB0071C351 /* TestToggle.swift */; }; + EA3361FB2891D54A0071C351 /* VDSTypographyTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA3361FA2891D54A0071C351 /* VDSTypographyTokens.xcframework */; }; + EA3361FC2891D54A0071C351 /* VDSTypographyTokens.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EA3361FA2891D54A0071C351 /* VDSTypographyTokens.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + EA3362342891F5AB0071C351 /* TestToggleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3362332891F5AB0071C351 /* TestToggleModel.swift */; }; + EA3E48A62860BB4D00B524AB /* WifiWidgetModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3E48A52860BB4D00B524AB /* WifiWidgetModel.swift */; }; + EA3E48A82860BB9800B524AB /* WifiWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3E48A72860BB9800B524AB /* WifiWidget.swift */; }; + EA5B696E2866BC1000B17D2E /* MVMCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA5B696C2866BC1000B17D2E /* MVMCore.framework */; }; + EA5B696F2866BC1000B17D2E /* MVMCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EA5B696C2866BC1000B17D2E /* MVMCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + EA5B69702866BC1000B17D2E /* MVMCoreUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA5B696D2866BC1000B17D2E /* MVMCoreUI.framework */; }; + EA5B69712866BC1000B17D2E /* MVMCoreUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EA5B696D2866BC1000B17D2E /* MVMCoreUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + EAA54A92286A47ED00B9136B /* WifiViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA54A91286A47ED00B9136B /* WifiViewController.swift */; }; + EAA658152875FA5E00484A7D /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA658142875FA5E00484A7D /* VDSFormControlsTokens.xcframework */; }; + EAA658162875FA5E00484A7D /* VDSFormControlsTokens.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EAA658142875FA5E00484A7D /* VDSFormControlsTokens.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + EACA5E5E2853DBC900CBA65B /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EACA5E5D2853DBC900CBA65B /* VDSColorTokens.xcframework */; }; + EACA5E5F2853DBC900CBA65B /* VDSColorTokens.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EACA5E5D2853DBC900CBA65B /* VDSColorTokens.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -55,9 +96,12 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - EA7E676E27582F2200ABF773 /* MVMCoreUI.framework in Embed Frameworks */, - EA7E676C27582F2200ABF773 /* MVMCore.framework in Embed Frameworks */, - D2B1E52A22FA10BA0065F95C /* MVMAnimationFramework.framework in Embed Frameworks */, + EA33618C288B1B630071C351 /* VDS.framework in Embed Frameworks */, + EAA658162875FA5E00484A7D /* VDSFormControlsTokens.xcframework in Embed Frameworks */, + EA5B696F2866BC1000B17D2E /* MVMCore.framework in Embed Frameworks */, + EACA5E5F2853DBC900CBA65B /* VDSColorTokens.xcframework in Embed Frameworks */, + EA3361FC2891D54A0071C351 /* VDSTypographyTokens.xcframework in Embed Frameworks */, + EA5B69712866BC1000B17D2E /* MVMCoreUI.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -89,8 +133,53 @@ D2FC4FAA25897ACB00061EA4 /* OrderTrackerModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderTrackerModel.swift; sourceTree = ""; }; D2FC4FAB25897ACB00061EA4 /* Step.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Step.swift; sourceTree = ""; }; D2FC4FAC25897ACB00061EA4 /* OrderTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrderTracker.swift; sourceTree = ""; }; + EA09CDBC282C3FD800A7835F /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; }; + EA09CDBE282C40CB00A7835F /* MFFGHSBluetoothPair.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MFFGHSBluetoothPair.swift; sourceTree = ""; }; + EA09CDC0282C40CB00A7835F /* GMFGBluetoothPair.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMFGBluetoothPair.swift; sourceTree = ""; }; + EA09CDC4282C40CB00A7835F /* GMFGConstant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMFGConstant.swift; sourceTree = ""; }; + EA09CDC7282C40CC00A7835F /* GMFGBLEHandlerProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMFGBLEHandlerProtocol.swift; sourceTree = ""; }; + EA09CDC9282C40CC00A7835F /* GMFG5GCBandSignalHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMFG5GCBandSignalHandler.swift; sourceTree = ""; }; + EA09CDCA282C40CC00A7835F /* GMFGOperationHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMFGOperationHandler.swift; sourceTree = ""; }; + EA09CDCB282C40CC00A7835F /* GMFGPublicInternetAccessHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMFGPublicInternetAccessHandler.swift; sourceTree = ""; }; + EA09CDCC282C40CC00A7835F /* GMFGFotaHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMFGFotaHandler.swift; sourceTree = ""; }; + EA09CDCD282C40CC00A7835F /* GMFGSpeedTestHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMFGSpeedTestHandler.swift; sourceTree = ""; }; + EA09CDCE282C40CC00A7835F /* GMFG5GSignalHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMFG5GSignalHandler.swift; sourceTree = ""; }; + EA09CDCF282C40CC00A7835F /* GMFGRouterWifiHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMFGRouterWifiHandler.swift; sourceTree = ""; }; + EA09CDE2282C416C00A7835F /* BluetoothDebuggableProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothDebuggableProtocol.swift; sourceTree = ""; }; + EA09CDE3282C416C00A7835F /* MulticastDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MulticastDelegate.swift; sourceTree = ""; }; + EA09CDE4282C416C00A7835F /* BluetoothDebugger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothDebugger.swift; sourceTree = ""; }; + EA09CDE5282C416C00A7835F /* BluetoothDebuggerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothDebuggerView.swift; sourceTree = ""; }; + EA09CDEA282C422900A7835F /* GMFGStorageManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMFGStorageManager.swift; sourceTree = ""; }; + EA09CDEC282C423E00A7835F /* GMFGLocationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GMFGLocationManager.swift; sourceTree = ""; }; + EA09CDEE282C429800A7835F /* GMFGTestScreenData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GMFGTestScreenData.swift; sourceTree = ""; }; + EA09CDF1282C430400A7835F /* BluetoothPairingProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPairingProtocol.swift; sourceTree = ""; }; + EA09CDF2282C430400A7835F /* BluetoothPairableProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPairableProtocol.swift; sourceTree = ""; }; + EA09CDF4282C430400A7835F /* BluetoothConfigModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothConfigModel.swift; sourceTree = ""; }; + EA09CDF5282C430400A7835F /* PeripheralModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PeripheralModel.swift; sourceTree = ""; }; + EA09CDF6282C430400A7835F /* CharacteristicModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CharacteristicModel.swift; sourceTree = ""; }; + EA09CDF7282C430400A7835F /* ServiceModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceModel.swift; sourceTree = ""; }; + EA09CDFE282C437C00A7835F /* MFFGHSAnalyticsProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MFFGHSAnalyticsProtocol.swift; sourceTree = ""; }; + EA09CE00282C43E800A7835F /* KeyedDecodingContainer+Decode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KeyedDecodingContainer+Decode.swift"; sourceTree = ""; }; + EA09CE02282C44A100A7835F /* MFFGHSUtility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MFFGHSUtility.swift; sourceTree = ""; }; + EA09CE04282C45C200A7835F /* BluetoothPairBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothPairBehavior.swift; sourceTree = ""; }; + EA1B7BBC2893459E006AF0BC /* DecodableDefaults+VDS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DecodableDefaults+VDS.swift"; sourceTree = ""; }; + EA2ED278285BB3F400781478 /* MVMCoreUI.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MVMCoreUI.xcframework; path = ../SharedFrameworks/MVMCoreUI.xcframework; sourceTree = ""; }; + EA2ED279285BB3F400781478 /* MVMCore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MVMCore.xcframework; path = ../SharedFrameworks/MVMCore.xcframework; sourceTree = ""; }; + EA2ED27E285BD00D00781478 /* MVMCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EA2ED27F285BD00D00781478 /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EA33618A288B1B630071C351 /* VDS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = VDS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EA3361C0288B37FB0071C351 /* TestToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestToggle.swift; sourceTree = ""; }; + EA3361FA2891D54A0071C351 /* VDSTypographyTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSTypographyTokens.xcframework; path = ../SharedFrameworks/VDSTypographyTokens.xcframework; sourceTree = ""; }; + EA3362332891F5AB0071C351 /* TestToggleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestToggleModel.swift; sourceTree = ""; }; + EA3E48A52860BB4D00B524AB /* WifiWidgetModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiWidgetModel.swift; sourceTree = ""; }; + EA3E48A72860BB9800B524AB /* WifiWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiWidget.swift; sourceTree = ""; }; + EA5B696C2866BC1000B17D2E /* MVMCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EA5B696D2866BC1000B17D2E /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA7E676927582F2200ABF773 /* MVMCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA7E676A27582F2200ABF773 /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EAA54A91286A47ED00B9136B /* WifiViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiViewController.swift; sourceTree = ""; }; + EAA658142875FA5E00484A7D /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = ""; }; + EACA5E5D2853DBC900CBA65B /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -98,9 +187,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - EA7E676D27582F2200ABF773 /* MVMCoreUI.framework in Frameworks */, - EA7E676B27582F2200ABF773 /* MVMCore.framework in Frameworks */, - D2B1E52922FA10BA0065F95C /* MVMAnimationFramework.framework in Frameworks */, + EACA5E5E2853DBC900CBA65B /* VDSColorTokens.xcframework in Frameworks */, + EA09CDBD282C3FD800A7835F /* CoreBluetooth.framework in Frameworks */, + EA33618B288B1B630071C351 /* VDS.framework in Frameworks */, + EA3361FB2891D54A0071C351 /* VDSTypographyTokens.xcframework in Frameworks */, + EA5B696E2866BC1000B17D2E /* MVMCore.framework in Frameworks */, + EAA658152875FA5E00484A7D /* VDSFormControlsTokens.xcframework in Frameworks */, + EA5B69702866BC1000B17D2E /* MVMCoreUI.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -137,6 +230,7 @@ D2B1E3F122F4A68F0065F95C /* JSONCreator */ = { isa = PBXGroup; children = ( + EA09CD9A282C3F6B00A7835F /* 5G */, D288D69B26CAE26900A5C365 /* MF */, D2B1E40922F4C9F00065F95C /* JSON */, D2B1E3F222F4A68F0065F95C /* AppDelegate.swift */, @@ -146,6 +240,12 @@ D2B1E3FB22F4A6930065F95C /* Assets.xcassets */, D2B1E3FD22F4A6930065F95C /* LaunchScreen.storyboard */, D2B1E40022F4A6930065F95C /* Info.plist */, + EA3E48A52860BB4D00B524AB /* WifiWidgetModel.swift */, + EA3E48A72860BB9800B524AB /* WifiWidget.swift */, + EAA54A91286A47ED00B9136B /* WifiViewController.swift */, + EA3362332891F5AB0071C351 /* TestToggleModel.swift */, + EA3361C0288B37FB0071C351 /* TestToggle.swift */, + EA1B7BBC2893459E006AF0BC /* DecodableDefaults+VDS.swift */, ); path = JSONCreator; sourceTree = ""; @@ -153,6 +253,17 @@ D2B1E42A22F9D0D90065F95C /* Frameworks */ = { isa = PBXGroup; children = ( + EA3361FA2891D54A0071C351 /* VDSTypographyTokens.xcframework */, + EA33618A288B1B630071C351 /* VDS.framework */, + EAA658142875FA5E00484A7D /* VDSFormControlsTokens.xcframework */, + EA5B696C2866BC1000B17D2E /* MVMCore.framework */, + EA5B696D2866BC1000B17D2E /* MVMCoreUI.framework */, + EA2ED27E285BD00D00781478 /* MVMCore.framework */, + EA2ED27F285BD00D00781478 /* MVMCoreUI.framework */, + EA2ED279285BB3F400781478 /* MVMCore.xcframework */, + EA2ED278285BB3F400781478 /* MVMCoreUI.xcframework */, + EACA5E5D2853DBC900CBA65B /* VDSColorTokens.xcframework */, + EA09CDBC282C3FD800A7835F /* CoreBluetooth.framework */, EA7E676927582F2200ABF773 /* MVMCore.framework */, EA7E676A27582F2200ABF773 /* MVMCoreUI.framework */, D2B1E51C22FA0DF50065F95C /* MVMCore.framework */, @@ -179,6 +290,89 @@ path = "Order Tracker"; sourceTree = ""; }; + EA09CD9A282C3F6B00A7835F /* 5G */ = { + isa = PBXGroup; + children = ( + EA09CE04282C45C200A7835F /* BluetoothPairBehavior.swift */, + EA09CE02282C44A100A7835F /* MFFGHSUtility.swift */, + EA09CE00282C43E800A7835F /* KeyedDecodingContainer+Decode.swift */, + EA09CDBE282C40CB00A7835F /* MFFGHSBluetoothPair.swift */, + EA09CDFE282C437C00A7835F /* MFFGHSAnalyticsProtocol.swift */, + EA09CDF3282C430400A7835F /* Models */, + EA09CDF0282C430400A7835F /* Protocols */, + EA09CDE1282C416C00A7835F /* Debugger */, + EA09CDBF282C40CB00A7835F /* Utility */, + ); + path = 5G; + sourceTree = ""; + }; + EA09CDBF282C40CB00A7835F /* Utility */ = { + isa = PBXGroup; + children = ( + EA09CDEC282C423E00A7835F /* GMFGLocationManager.swift */, + EA09CDEA282C422900A7835F /* GMFGStorageManager.swift */, + EA09CDC0282C40CB00A7835F /* GMFGBluetoothPair.swift */, + EA09CDC4282C40CB00A7835F /* GMFGConstant.swift */, + EA09CDC6282C40CC00A7835F /* Protocols */, + EA09CDC8282C40CC00A7835F /* BLE Handlers */, + EA09CDEE282C429800A7835F /* GMFGTestScreenData.swift */, + ); + path = Utility; + sourceTree = ""; + }; + EA09CDC6282C40CC00A7835F /* Protocols */ = { + isa = PBXGroup; + children = ( + EA09CDC7282C40CC00A7835F /* GMFGBLEHandlerProtocol.swift */, + ); + path = Protocols; + sourceTree = ""; + }; + EA09CDC8282C40CC00A7835F /* BLE Handlers */ = { + isa = PBXGroup; + children = ( + EA09CDC9282C40CC00A7835F /* GMFG5GCBandSignalHandler.swift */, + EA09CDCA282C40CC00A7835F /* GMFGOperationHandler.swift */, + EA09CDCB282C40CC00A7835F /* GMFGPublicInternetAccessHandler.swift */, + EA09CDCC282C40CC00A7835F /* GMFGFotaHandler.swift */, + EA09CDCD282C40CC00A7835F /* GMFGSpeedTestHandler.swift */, + EA09CDCE282C40CC00A7835F /* GMFG5GSignalHandler.swift */, + EA09CDCF282C40CC00A7835F /* GMFGRouterWifiHandler.swift */, + ); + path = "BLE Handlers"; + sourceTree = ""; + }; + EA09CDE1282C416C00A7835F /* Debugger */ = { + isa = PBXGroup; + children = ( + EA09CDE2282C416C00A7835F /* BluetoothDebuggableProtocol.swift */, + EA09CDE3282C416C00A7835F /* MulticastDelegate.swift */, + EA09CDE4282C416C00A7835F /* BluetoothDebugger.swift */, + EA09CDE5282C416C00A7835F /* BluetoothDebuggerView.swift */, + ); + path = Debugger; + sourceTree = ""; + }; + EA09CDF0282C430400A7835F /* Protocols */ = { + isa = PBXGroup; + children = ( + EA09CDF1282C430400A7835F /* BluetoothPairingProtocol.swift */, + EA09CDF2282C430400A7835F /* BluetoothPairableProtocol.swift */, + ); + path = Protocols; + sourceTree = ""; + }; + EA09CDF3282C430400A7835F /* Models */ = { + isa = PBXGroup; + children = ( + EA09CDF4282C430400A7835F /* BluetoothConfigModel.swift */, + EA09CDF5282C430400A7835F /* PeripheralModel.swift */, + EA09CDF6282C430400A7835F /* CharacteristicModel.swift */, + EA09CDF7282C430400A7835F /* ServiceModel.swift */, + ); + path = Models; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -278,20 +472,54 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + EA09CDFC282C430400A7835F /* CharacteristicModel.swift in Sources */, + EA3E48A82860BB9800B524AB /* WifiWidget.swift in Sources */, D2B1E3F722F4A68F0065F95C /* DetailViewController.swift in Sources */, + EA09CDDD282C40CC00A7835F /* GMFGSpeedTestHandler.swift in Sources */, + EA09CDDF282C40CC00A7835F /* GMFGRouterWifiHandler.swift in Sources */, + EA09CDFD282C430400A7835F /* ServiceModel.swift in Sources */, + EA09CE01282C43E800A7835F /* KeyedDecodingContainer+Decode.swift in Sources */, + EA09CDDE282C40CC00A7835F /* GMFG5GSignalHandler.swift in Sources */, + EA09CDD1282C40CC00A7835F /* MFFGHSBluetoothPair.swift in Sources */, + EA09CDEB282C422900A7835F /* GMFGStorageManager.swift in Sources */, + EA1B7BBD2893459E006AF0BC /* DecodableDefaults+VDS.swift in Sources */, + EA09CE05282C45C200A7835F /* BluetoothPairBehavior.swift in Sources */, + EAA54A92286A47ED00B9136B /* WifiViewController.swift in Sources */, + EA09CDFA282C430400A7835F /* BluetoothConfigModel.swift in Sources */, D27564CA25939E91003CA713 /* LinksModel.swift in Sources */, D2B1E3F522F4A68F0065F95C /* MasterViewController.swift in Sources */, D27564B62590FADB003CA713 /* ListDeviceRightVariableCaretModel.swift in Sources */, + EA09CDEF282C429800A7835F /* GMFGTestScreenData.swift in Sources */, + EA09CDED282C423F00A7835F /* GMFGLocationManager.swift in Sources */, + EA09CE03282C44A100A7835F /* MFFGHSUtility.swift in Sources */, + EA09CDF9282C430400A7835F /* BluetoothPairableProtocol.swift in Sources */, D27564B72590FADB003CA713 /* ListDeviceRightVariableCaret.swift in Sources */, D2431DEB25E93A4F001C7AAC /* buttimag.swift in Sources */, + EA3361C1288B37FB0071C351 /* TestToggle.swift in Sources */, D27564C825939E91003CA713 /* LinkCollectionViewCell.swift in Sources */, + EA09CDFB282C430400A7835F /* PeripheralModel.swift in Sources */, D2FC4FB025897ACB00061EA4 /* OrderTracker.swift in Sources */, + EA09CDD8282C40CC00A7835F /* GMFGBLEHandlerProtocol.swift in Sources */, + EA09CDDC282C40CC00A7835F /* GMFGFotaHandler.swift in Sources */, + EA09CDD6282C40CC00A7835F /* GMFGConstant.swift in Sources */, D27564C925939E91003CA713 /* Links.swift in Sources */, + EA09CDE6282C416C00A7835F /* BluetoothDebuggableProtocol.swift in Sources */, + EA3E48A62860BB4D00B524AB /* WifiWidgetModel.swift in Sources */, + EA09CDD2282C40CC00A7835F /* GMFGBluetoothPair.swift in Sources */, + EA09CDE9282C416C00A7835F /* BluetoothDebuggerView.swift in Sources */, D2FC4FAE25897ACB00061EA4 /* OrderTrackerModel.swift in Sources */, + EA3362342891F5AB0071C351 /* TestToggleModel.swift in Sources */, D21B3A27259B93ED001483DC /* SelfSizingCollectionView.swift in Sources */, + EA09CDD9282C40CC00A7835F /* GMFG5GCBandSignalHandler.swift in Sources */, + EA09CDF8282C430400A7835F /* BluetoothPairingProtocol.swift in Sources */, D2B1E3F322F4A68F0065F95C /* AppDelegate.swift in Sources */, + EA09CDDA282C40CC00A7835F /* GMFGOperationHandler.swift in Sources */, + EA09CDE7282C416C00A7835F /* MulticastDelegate.swift in Sources */, D29C557825BF1F340082E7D6 /* JSONCreatorActionHandler.swift in Sources */, + EA09CDDB282C40CC00A7835F /* GMFGPublicInternetAccessHandler.swift in Sources */, D2FC4FAD25897ACB00061EA4 /* StepModel.swift in Sources */, + EA09CDFF282C437C00A7835F /* MFFGHSAnalyticsProtocol.swift in Sources */, + EA09CDE8282C416C00A7835F /* BluetoothDebugger.swift in Sources */, D2FC4FAF25897ACB00061EA4 /* Step.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -445,7 +673,7 @@ "$(PROJECT_DIR)/../SharedFrameworks", ); INFOPLIST_FILE = JSONCreator/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -469,7 +697,7 @@ "$(PROJECT_DIR)/../SharedFrameworks", ); INFOPLIST_FILE = JSONCreator/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/JSONCreator_iOS/JSONCreator/5G/BluetoothPairBehavior.swift b/JSONCreator_iOS/JSONCreator/5G/BluetoothPairBehavior.swift new file mode 100644 index 0000000..891463b --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/BluetoothPairBehavior.swift @@ -0,0 +1,9 @@ +// +// BluetoothPairBehavior.swift +// JSONCreator +// +// Created by Matt Bruce on 5/11/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation diff --git a/JSONCreator_iOS/JSONCreator/5G/Debugger/BluetoothDebuggableProtocol.swift b/JSONCreator_iOS/JSONCreator/5G/Debugger/BluetoothDebuggableProtocol.swift new file mode 100644 index 0000000..a86910f --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Debugger/BluetoothDebuggableProtocol.swift @@ -0,0 +1,43 @@ +// +// BluetoothDebuggableProtocol.swift +// MVM5G +// +// Created by Matt Bruce on 3/18/22. +// Copyright © 2022 Kyle. All rights reserved. +// + +import Foundation +import UIKit + +//This is implemented on ViewControllers +protocol BluetoothDebuggableProtocol: BluetoothDebuggerDelegate { + associatedtype BluetoothPairType: MFFGHSBluetoothPair + var view: UIView! { get set } + var bluetoothPair: BluetoothPairType? { get set } + var bluetoothDebugger: BluetoothDebugger? { get } + var debuggerView: BluetoothDebuggerView? { get set} + func addDebuggerView() +} + +extension BluetoothDebuggableProtocol { + var bluetoothDebugger: BluetoothDebugger? { + return self.bluetoothPair?.bluetoothDebugger + } + + func addDebuggerView(){ + guard GMFGTestScreenData.shared.visualDebugger else { return } + debuggerView = BluetoothDebuggerView() + if let debuggerView = debuggerView { + debuggerView.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(debuggerView) + debuggerView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true + debuggerView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true + debuggerView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true + debuggerView.heightAnchor.constraint(equalToConstant: 400).isActive = true + } + } + + func bluetoothDebuggerUpdate(message: String){ + self.debuggerView?.text = message + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Debugger/BluetoothDebugger.swift b/JSONCreator_iOS/JSONCreator/5G/Debugger/BluetoothDebugger.swift new file mode 100644 index 0000000..0b959ea --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Debugger/BluetoothDebugger.swift @@ -0,0 +1,327 @@ +// +// GMFGBluetoothDebugger.swift +// MVM5G +// +// Created by Matt Bruce on 3/16/22. +// Copyright © 2022 Kyle. All rights reserved. +// + +import Foundation +import CoreBluetooth + +//Delegate used to receive updates from the debugger +//during a onChange() debugger event +protocol BluetoothDebuggerDelegate: AnyObject { + func bluetoothDebuggerUpdate(message: String) +} + +class BluetoothDebugger: NSObject { + weak var delegate: BluetoothDebuggerDelegate? + var deviceId: String + var config: BluetoothConfigModel + var bluetoothState: CBManagerState = .unknown { didSet { onChange() } } + var bluetoothStatus: Bool = false { didSet { onChange() } } + var isDevicePaired: Bool = false { didSet{ onChange() } } + var isDeviceActivated: Bool = false{ didSet{ onChange() } } + var isBluetoothPermissionDenied: Bool = false { didSet{ onChange() } } + var deviceDiscovered: String? { didSet{ onChange() } } + var deviceDiscoveredNameFailed: Set = [] { didSet{ onChange() } } + var deviceMismatch: Set = [] { didSet{ onChange() } } + var signalStatus: String? { didSet{ onChange() } } + var otherErrors: Set = [] { didSet{ onChange() } } + var logs: Set = [] { didSet { onChange() } } + + //computed properties + var deviceInfo: String{ + let value = """ + deviceId: \(deviceId)\r + bluetoothState: \(bluetoothState.description)\r + bluetoothStatus: \(bluetoothStatus)\r + bluetoothPermissionDenied: \(isBluetoothPermissionDenied)\r + devicePaired: \(isDevicePaired)\r + deviceActivated: \(isDeviceActivated)\r + """ + return value + } + + var discoveryInfo: String? { + var value: [String] = [] + + if let deviceDiscovered = deviceDiscovered { + value.append("deviceDiscovered: \(deviceDiscovered)\r") + } + + if deviceDiscoveredNameFailed.isNotEmpty { + value.append("deviceDiscoveredNameFailed: \r\(deviceDiscoveredNameFailed.toString())") + } + + if deviceMismatch.isNotEmpty { + value.append("deviceMismatch: \(deviceMismatch.toString())") + } + + return value.isNotEmpty ? value.joined(separator: "") : nil + } + + var otherErrorsInfo: String? { + guard otherErrors.isNotEmpty else { return nil } + return "\rErrors Occurred:\r\(otherErrors.toString())" + } + + var logsInfo: String { + return "\rlogs:\r\(logs.toString())" + } + + var configInfo: String { + guard let string = config.toJSONString() else { + return "\rConfig:\rdecodeError" + } + return "\rConfig:\r\(string)" + } + + override var description: String { + var descriptions: [String] = [deviceInfo] + + //optional messages + if let discoveryInfo = discoveryInfo { + descriptions.append(discoveryInfo) + } + + if let firmwareInfo = firmwareInfo { + descriptions.append(firmwareInfo) + } + + if let signalStatus = signalStatus { + descriptions.append(signalStatus) + } + + if let publicInternetAccessStatusInfo = publicInternetAccessStatusInfo { + descriptions.append(publicInternetAccessStatusInfo) + } + + if let speedTestStatusInfo = speedTestStatusInfo { + descriptions.append(speedTestStatusInfo) + } + + if let routerWifiInfo = routerWifiInfo { + descriptions.append(routerWifiInfo) + } + + if let signalUpdateInfo = signalUpdateInfo { + descriptions.append(signalUpdateInfo) + } + + if let signalUpdateInfo = signalUpdateCBandInfo { + descriptions.append(signalUpdateInfo) + } + + if let otherErrorsInfo = otherErrorsInfo { + descriptions.append(otherErrorsInfo) + } + + descriptions.append(contentsOf: [logsInfo, configInfo]) + + return descriptions.joined(separator: "") + } + + required init(config: BluetoothConfigModel, delegate: BluetoothDebuggerDelegate?){ + self.deviceId = config.advertisedData + self.config = config + self.delegate = delegate + super.init() + self.onChange() + } + + //methods + func onChange(){ + delegate?.bluetoothDebuggerUpdate(message: description) + } + + func addLog(message: String){ + logs.insert(message) + } + + func addOtherError(error: String){ + otherErrors.insert(error) + } + + func addDiscoveredNameFailures(foundNames: [String]) { + foundNames.forEach { name in + deviceDiscoveredNameFailed.insert(name) + } + } + + func addDeviceIMEIMismatch(found: String) { + deviceMismatch.insert(found) + } + + func updateCurrentSignalStatus(_ status: MFFGHSSignalStatus, strengthCheckStatus: MFFGHSSignalStrengthCheckStatus, rssi: Double?) { + var rssiValue = "none" + if let rssi = rssi { + rssiValue = "\(rssi)" + } + signalStatus = "signalStatus: status: \(status) strengthCheck: \(strengthCheckStatus) rssi: \(rssiValue)\r" + } + + //Handler Delegates Properties + + //GMFGSpeedTestDelegate + var speedTestStatus: GMFGSpeedTestUpdate? { didSet { onChange() } } + var speedTestStatusInfo: String? { + guard let status = speedTestStatus else { return nil } + return "speedTestStatusInfo: \(status)\r" + } + + //GMFGRouterWifiDelegate + var routerWifiCredentials: GMFGRouterWifiInfo? { didSet { onChange() } } + var routerWifiStatus: GMFGRouterWifiStatus? { didSet { onChange() } } + var routerWifiInfo: String? { + var value = "" + if let routerWifiCredentials = routerWifiCredentials { + value = """ + routerWifiCredentials:\r + - ssid: \(routerWifiCredentials.ssid)\r + - password: \(routerWifiCredentials.password)\r + """ + } + if let routerWifiStatus = routerWifiStatus { + value = value + """ + routerWifiStatus: \(routerWifiStatus)\r + """ + } + return value + } + + //GMFGPublicInternetAccessDelegate + var publicInternetAccessStatus: GMPublicInternetAccessStatus? { didSet { onChange() } } + var publicInternetAccessStatusInfo: String? { + guard let status = publicInternetAccessStatus else { return nil } + return "publicInternetAccessStatus: \(status)\r" + } + + //GMFGFotaDelegate + var firmwareResponse: GMFotaResponse? { didSet { onChange() } } + var isCheckingFirmware: Bool? { didSet { onChange() } } + var firmwareInfo: String? { + var value: [String] = [] + if let isCheckingFirmware = isCheckingFirmware { + value.append("checkingFirmware: \(isCheckingFirmware)\r") + } + if let fota = firmwareResponse { + value.append(""" + firmwareStatus: \(fota.status)\r + firmwareVersion: \(fota.version)\r + firmwareError: \(fota.errorMsg ?? "none")\r + """) + } + return value.isNotEmpty ? value.joined(separator: "") : nil + } + + //GMFG5GSignalDelegate + var signalUpdate: GMFG5GSignalUpdate? { didSet { onChange() } } + var signalUpdateInfo: String? { + guard let signalUpdate = signalUpdate else { return nil } + return "\r5G Signal Update: \r\(signalUpdate)\r" + } + + //GMFG5GSignalDelegate + var signalUpdateCBand: GMFG5GCBandSignalUpdate? { didSet { onChange() } } + var signalUpdateCBandInfo: String? { + guard let signalUpdate = signalUpdateCBand, let results = signalUpdate.rawResults else { return nil } + return "\r5G C-Band Signal Update: \r\(results)\r" + } + +} + +//Handler Delegate Implementations +extension BluetoothDebugger: GMFGSpeedTestDelegate { + func didStartSpeedTest() { + //BLELog has this + } + + func didStopSpeedTest() { + //BLELog has this + } + + func speedTestStatus(status: GMFGSpeedTestUpdate) { + speedTestStatus = status + } +} + +extension BluetoothDebugger: GMFGRouterWifiDelegate { + func routerWifi(credentials: GMFGRouterWifiInfo) { + routerWifiCredentials = credentials + } + + func routerWifiStatus(status: GMFGRouterWifiStatus) { + routerWifiStatus = status + } +} + +extension BluetoothDebugger: GMFGPublicInternetAccessDelegate { + func publicInternetAccessStatus(status: GMPublicInternetAccessStatus) { + publicInternetAccessStatus = status + } +} + +extension BluetoothDebugger: GMFGFotaDelegate { + func fotaStatus(statusResponse: GMFotaResponse) { + firmwareResponse = statusResponse + } + + func unknownResponse(rawResponse: [String : Any]?) { + let response: String = rawResponse == nil ? "none" : "\(rawResponse!)" + addLog(message: "unknown response: \(response)") + } +} + +extension BluetoothDebugger: GMFG5GSignalDelegate { + + func updated5GSignal(_ update: GMFG5GSignalUpdate){ + signalUpdate = update + } + + func signalNotReady() { + signalUpdate = nil + addLog(message: "5G Signal Not Ready") + } + +} + +extension BluetoothDebugger: GMFG5GCBandSignalDelegate { + + func updated5GCBandSignal(_ update: GMFG5GCBandSignalUpdate){ + signalUpdateCBand = update + } + + func signal5GCBandNotReady() { + signalUpdateCBand = nil + addLog(message: "5G C-Band Signal Not Ready") + } +} + +extension CBManagerState: CustomStringConvertible { + public var description: String { + switch self { + case .unknown: + return "unknown" + case .resetting: + return "resetting" + case .unsupported: + return "unsupported" + case .unauthorized: + return "unauthorized" + case .poweredOff: + return "poweredOff" + case .poweredOn: + return "poweredOn" + @unknown default: + return "unknown" + } + } +} + +extension Collection { + fileprivate func toString() -> String { + return self.map{" - \($0)\r"}.joined(separator: "") + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Debugger/BluetoothDebuggerView.swift b/JSONCreator_iOS/JSONCreator/5G/Debugger/BluetoothDebuggerView.swift new file mode 100644 index 0000000..ece549e --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Debugger/BluetoothDebuggerView.swift @@ -0,0 +1,96 @@ +// +// BluetoothDebuggerView.swift +// MVM5G +// +// Created by Matt Bruce on 3/19/22. +// Copyright © 2022 Kyle. All rights reserved. +// + +import Foundation +import UIKit + +/// View Used to show on Pairing screens to act as a Console Log +class BluetoothDebuggerView: UIView { + + //console log view + var textView = UITextView() + + //text show in textView + var text: String = "Initial Page Load" { + didSet{ + textView.text = text + } + } + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + + override init(frame: CGRect) { + super.init(frame: frame) + setupView() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setupView() + } + + init() { + super.init(frame: .zero) + setupView() + } + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + private func setupView() { + translatesAutoresizingMaskIntoConstraints = false + + let doneButton = UIButton() + doneButton.setTitle("Done", for: .normal) + doneButton.addTarget(self, action: #selector(close), for: .touchUpInside) + doneButton.translatesAutoresizingMaskIntoConstraints = false + + let headerView = UIView() + headerView.translatesAutoresizingMaskIntoConstraints = false + headerView.backgroundColor = .mvmCoolGray6 + headerView.addSubview(doneButton) + doneButton.rightAnchor.constraint(equalTo: headerView.rightAnchor).isActive = true + doneButton.topAnchor.constraint(equalTo: headerView.topAnchor).isActive = true + doneButton.heightAnchor.constraint(equalToConstant: 40).isActive = true + doneButton.widthAnchor.constraint(equalToConstant: 100).isActive = true + + textView.setContentCompressionResistancePriority(.required, for: .vertical) + textView.isUserInteractionEnabled = true + textView.showsVerticalScrollIndicator = true + textView.isEditable = false + textView.isScrollEnabled = true + textView.scrollRangeToVisible(NSMakeRange(0, 0)) + textView.setContentOffset(CGPoint(x: 0, y: 0), animated: false) + + textView.translatesAutoresizingMaskIntoConstraints = false + textView.backgroundColor = .white + textView.text = text + addSubview(headerView) + addSubview(textView) + + headerView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true + headerView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true + headerView.topAnchor.constraint(equalTo: topAnchor).isActive = true + headerView.bottomAnchor.constraint(equalTo: textView.topAnchor).isActive = true + headerView.heightAnchor.constraint(equalToConstant: 40).isActive = true + + textView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true + textView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true + textView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true + textView.heightAnchor.constraint(equalToConstant: 400).isActive = true + } + + //removes self from View + @objc func close(sender: UIButton){ + removeFromSuperview() + } + +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Debugger/MulticastDelegate.swift b/JSONCreator_iOS/JSONCreator/5G/Debugger/MulticastDelegate.swift new file mode 100644 index 0000000..da63e83 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Debugger/MulticastDelegate.swift @@ -0,0 +1,56 @@ +// +// MulticastDelegate.swift +// MVM5G +// +// Created by Matt Bruce on 3/23/22. +// Copyright © 2022 Kyle. All rights reserved. +// + +import Foundation + +public class MulticastDelegate { + + // MARK: - Properties + private class Wrapper { + weak var delegate: AnyObject? + + init(_ delegate: AnyObject) { + self.delegate = delegate + } + } + + private var wrappers: [Wrapper] = [] + public var delegates: [T] { + return wrappers + .compactMap{ $0.delegate } as! [T] + } + + // MARK: - Actions + public func add(delegate: T) { + let wrapper = Wrapper(delegate as AnyObject) + wrappers.append(wrapper) + } + + public func add(delegates: [T?]){ + for case let delegate? in delegates { + add(delegate: delegate) + } + } + + public func remove(delegate: T) { + guard let index = wrappers.firstIndex(where: { + $0.delegate === (delegate as AnyObject) + }) else { + return + } + wrappers.remove(at: index) + } + + public func removeAll(){ + wrappers.removeAll() + } + + public func invokeForEachDelegate(_ handler: (T) -> ()) { + delegates.forEach { handler($0) } + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/KeyedDecodingContainer+Decode.swift b/JSONCreator_iOS/JSONCreator/5G/KeyedDecodingContainer+Decode.swift new file mode 100644 index 0000000..0c2fad6 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/KeyedDecodingContainer+Decode.swift @@ -0,0 +1,59 @@ +// +// KeyedDecodingContainer+Decode.swift +// MVM5G +// +// Created by Matt Bruce on 4/15/22. +// Copyright © 2022 Kyle. All rights reserved. +// + +import Foundation +import MVMCore + + +public enum KeyedDecodingContainerError: Error { + case decode(objectType: String, key: String, message: String) +} + +extension KeyedDecodingContainerError: LocalizedError, CustomStringConvertible { + public var description: String { + switch self { + case .decode(let objectType, let key, let message): + return "Decoding Error for object: \(objectType) on coding key: \(key) with error: \(message)" + } + } + + public var errorDescription: String? { + return self.description + } +} + +extension KeyedDecodingContainer { + public func decode(_ type: T.Type, + forKey key: Self.Key, + objectType: K.Type) throws -> T { + do { + return try self.decode(type, forKey: key) + } catch { + throw KeyedDecodingContainerError.decode(objectType: "\(objectType)", key: key.stringValue, message: error.localizedDescription) + } + } +} + +extension JSONError: LocalizedError, CustomStringConvertible { + public var description: String { + switch self { + case .pathNotFound: + return "JSON path not found" + case .data(path: let path): + return "JSON data in \(path) is corrupt" + case .other(error: let error): + return error.localizedDescription + case .error(message: let message): + return message + } + } + + public var errorDescription: String? { + return description + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/MFFGHSAnalyticsProtocol.swift b/JSONCreator_iOS/JSONCreator/5G/MFFGHSAnalyticsProtocol.swift new file mode 100644 index 0000000..a5ad26a --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/MFFGHSAnalyticsProtocol.swift @@ -0,0 +1,217 @@ +// +// MFFGHSAnalyticsProtocol.swift +// MVM5G +// +// Created by Gujuluva Santharam, Ajai Prabhu on 20/05/20. +// Copyright © 2020 Kyle. All rights reserved. +// +import Foundation +import UIKit +import MVMCore +import MVMCoreUI + +protocol MFFGHSAnalyticsProtocol { + func trackPage(data: [String: Any]?, additionalData: [String:Any]?) + func trackAction(action: [String: Any]?, pageData:[String: Any]?, additionalData: [String: Any]?) + func trackPageAppear(with pageJSON: [String: Any]?) + func trackPageDisappear(with pageJSON: [String: Any]?) + + /** true means it will stop sending data to server when view appear to reduce data load. Temporary bool, will remove next release */ + func stopInitialEvent() -> Bool +} + +enum AnalyticsLogType: String { + case UI = "UI" + case BLE = "BLE" +} + +struct AnalyticsPageInfo { + var pageType: String? +} + +extension MFFGHSAnalyticsProtocol { + + /** this boolean enable entire analytics feature. enable = false means nothing will send to server */ + private var enable: Bool { + let initialParams:[String: Any] = [:] + return !initialParams.boolForKey("isFGAnalyticsDisabled") + } + + /** enable time tracking feature. TODO - we are reducing call not sending data on start event. */ + private var enableTimeTracker: Bool { return true } + + /** true means it will stop sending data to server when view appear to reduce data load. Temporary bool, will remove next release */ + func stopInitialEvent() -> Bool { return false } + + func trackPage(data: [String: Any]?, additionalData:[String:Any]?) { + let params: [String: Any] = [ + "eventType" : additionalData?["eventType"] ?? "pageDisplay", + "pageType" : data?["pageType"] ?? "NA", + "template" : data?["template"] ?? "NA", + "eventName" : additionalData?["eventName"] ?? "start", + "pageClass" : additionalData?["pageClass"] ?? String(describing: Self.self), + "epochSec" : Int(Date().timeIntervalSince1970), + "tdn" : getVendorId(), + "os" : "iOS" + ] + let mergedParams = params.merging(additionalData ?? [:]) { $1 } + performRequest(params: ["data": mergedParams]) + } + + func trackAction(action: [String: Any]?, pageData:[String: Any]?, additionalData: [String: Any]?) { + /* + let params: [String: Any] = [ + "eventType" : "action", + "actionPageType": action?["pageType"] ?? "NA", + "actionTitle" : action?["title"] ?? "NA", + "pageType" : pageData?.stringForkey("pageType") ?? "NA", + "template" : pageData?.stringForkey("template") ?? "NA", + "epochSec" : Int(Date().timeIntervalSince1970) + ] + performRequest(params: params) + */ + } + + func trackGemini(value: [String: Any], logType: AnalyticsLogType, pageInfo: AnalyticsPageInfo? = nil) { + var result: [String: Any] = [ + "os" : "iOS", + "LogType" : logType.rawValue, + "tdn" : getVendorId() + ] + if let pageInfo = pageInfo { + result["pageType"] = pageInfo.pageType + } + let payload = result.merging(value) { $1 } + performRequest(params: ["data": payload]) + } + + func trackPKI(processStep: String, attributeList: GMFGAnalyticsAttributeList?) { + var payload: [String: Any] = [ + "processStep" : processStep, + "os" : "iOS", + "tdn" : getVendorId() + ] + if let attributeList = attributeList { + payload["attributeList"] = attributeList.getList() + //GlassboxManager.glassboxCustomEvent(forKey: GMFGConstant.Glassbox.fivegSetup, withParameters: ["gen3Data": attributeList.getList()]) + } + performRequest(params: payload, forPageType: "gen3Data") + } + + func trackGen3Data(processStep: String, attributeList: GMFGAnalyticsAttributeList?) { + var payload: [String: Any] = [ + "processStep" : processStep, + "os" : "iOS", + "tdn" : getVendorId() + ] + if let attributeList = attributeList { + payload["attributeList"] = attributeList.getList() + } + performRequest(params: payload, forPageType: "gen3Data", priority: .high) + } + + func resetGMAnalytics() { + LogRequestSerialiser.shared.resetTimer() + } + + // MARK:- Get the Device UUID + func getVendorId() -> String { + let uuid = UIDevice.current.identifierForVendor?.uuidString + return uuid ?? GMFGConstant.empty + } + + private func performRequest(params: [String: Any], forPageType: String = "logUIData", priority: LogPriority = .normal) { + if enable { + LogRequestSerialiser.shared.queueLogRequest(params: params, forPageType: forPageType, priority: priority) + } + } + + // MARK:- Public API - Time Tracker + + func trackPageAppear(with pageJSON: [String: Any]?) { + if !enableTimeTracker { return } + //GMFGSelfInstallTimeTracker.shared.trackPageAppear(with: pageJSON) + } + + func trackPageDisappear(with pageJSON: [String: Any]?) { + if !enableTimeTracker { return } + //GMFGSelfInstallTimeTracker.shared.trackPageDisappear(with: pageJSON) + } +} + +final class LogRequestSerialiser { + + static let shared: LogRequestSerialiser = LogRequestSerialiser() + private lazy var requestQueue: [RequestItem] = [] + private lazy var timer: Timer? = nil + private init() { /* this avoid creating new instance */ } + private var index = 0 + + fileprivate func queueLogRequest(params: [String: Any], forPageType: String, priority: LogPriority) { + let requestItem = RequestItem(priority: priority, endpoint: forPageType, parameters: params) + requestQueue.append(requestItem) + startTimer() + } + + @objc fileprivate func performRequestIfAvailable() { + if requestQueue.count > index { + let objectToBePerformed = requestQueue[index] + if let reqParams = MVMCoreRequestParameters(pageType: objectToBePerformed.endpoint, extraParameters: nil) { + index = index + 1 + reqParams.add(objectToBePerformed.parameters) + MVMCoreLoadHandler.sharedGlobal()?.loadBackgroundRequest(reqParams, dataForPage: nil, delegateObject: nil) + } + } + if requestQueue.count <= index { resetTimer() } + } + + fileprivate func resetTimer() { + timer?.invalidate() + timer = nil + index = 0 + requestQueue.removeAll() + } + + fileprivate func startTimer() { + if timer == nil { + DispatchQueue.main.async { + self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.performRequestIfAvailable), userInfo: nil, repeats: true) + self.timer?.tolerance = 0.5 + } + } + } +} + +fileprivate struct RequestItem { + var priority: LogPriority + var endpoint: String + var parameters: [String: Any] +} + +fileprivate enum LogPriority { + case high + case normal +} + +struct GMFGAnalyticsAttributeList { + private var attrList: [[String: Any]] = [] + + @available(*, deprecated, message: "use add(name:, value:)") + mutating func addAttribute(name: String, value: Any) { + attrList.append([ + "attributeName": name, + "attributeValue": value + ]) + } + + mutating func add(_ name: String, _ value: Any) { + attrList.append([ + "attributeName": name, + "attributeValue": value + ]) + } + + func getList() -> [[String: Any]] { + return attrList + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/MFFGHSBluetoothPair.swift b/JSONCreator_iOS/JSONCreator/5G/MFFGHSBluetoothPair.swift new file mode 100644 index 0000000..afc59b4 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/MFFGHSBluetoothPair.swift @@ -0,0 +1,547 @@ +// +// MFFGHSBluetoothPair.swift +// MobileFirstFramework +// +// Created by Chowdhury, Shohrab on 3/29/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import CoreBluetooth + +public enum MFFGHSSignalStatus: NSNumber { + case noBluetoothConnection = -1, noSignal = 1, poor, good, confirmedGood, confirmedPoor +} + +public enum MFFGHSSignalStrengthCheckStatus: NSNumber { + case idle = 0, processing, complete +} + +public protocol MFFGHSBluetoothTestingProtocol { + var continueTesting: Bool { get set } +} + +public protocol MFFGHSBluetoothPairDelegate : NSObjectProtocol { + + /// Receive ble status ON/OFF + func bluetoothStatus(isOn: Bool) + + /// New status that's more reliable as a callback if BLE is off + func bluetoothOff() + + /// Bluetooth does not have access from the user + func bluetoothPermissionsDenied() + + /// BLE found a device that doesn't match what it expects to find. + func deviceIMEIMismatch(expected: String, found: String) + + /// An attempted pairing failed due to a broadcast naming mismatch. + func onBluetoothDiscoveredNameFailed(expectedName: String, foundNames: [String]) + + /// The bluetooth service was discovered. + func onBluetoothDiscovered(expectedName: String, foundName: String) + + /// Check device is paired with phone + func pairUpdate(isPaired: Bool) + + /// Check device is activated or not + func updateActivatedStatus(isActivated: Bool) + + /// Receive current RSSI + func updateCurrentSignalStatus(_ status: MFFGHSSignalStatus, strengthCheckStatus: MFFGHSSignalStrengthCheckStatus, rssi: Double?) + +} + +extension MFFGHSBluetoothPairDelegate { + public func bluetoothStatus(isOn: Bool) {} + public func bluetoothOff() {} + public func bluetoothPermissionsDenied() {} + public func deviceIMEIMismatch(expected: String, found: String) {} + public func onBluetoothDiscoveredNameFailed(expectedName: String, foundNames: [String]) {} + public func onBluetoothDiscovered(expectedName: String, foundName: String) {} + public func pairUpdate(isPaired: Bool) {} + public func updateActivatedStatus(isActivated:Bool) {} + public func updateCurrentSignalStatus(_ status: MFFGHSSignalStatus, strengthCheckStatus: MFFGHSSignalStrengthCheckStatus, rssi: Double?) {} +} + +class MFFGHSBluetoothPair: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, BluetoothParingProtocol { + + // !!Major change!! Some controllers are relying on bluetooth object being passed. + static weak var instance:MFFGHSBluetoothPair? + + /// Weak instance returned. Controller that uses it should hold onto strong reference. When all controllers deallocate, this will also. + static var sharedInstance: MFFGHSBluetoothPair { + get { + if let instance = instance { + return instance + } else { + let newInstance = MFFGHSBluetoothPair() + instance = newInstance + return newInstance + } + } + } + + // Bluetooth control objects. + var centralManager: CBCentralManager? + var peripheral: CBPeripheral? + + // Event delegate. + weak var delegate: MFFGHSBluetoothPairDelegate? + + //debugger for event delegate + var bluetoothDebugger: BluetoothDebugger? + + // Data from bluetooth connection + var isPaired = false + var isCPEActivated = false + var currentSignalStatus: MFFGHSSignalStatus = .noBluetoothConnection + var currentRSSIValue: Double? + + // Configured properties. + var bluetoothConfig: BluetoothConfigModel? { + didSet { + if (oldValue != bluetoothConfig) { // TODO: Needs deep check for config changes. + setupBluetoothScanner() + } + } + } + + var signalStrengthObserveDuration: TimeInterval = 30 + var signalPassingPercentage: Double = 100 + var signalStrengthCheckStatus: MFFGHSSignalStrengthCheckStatus = .idle + var signalStrengthObserverTimer: Timer? + var lastSignalStatusTimeStamp: Date? + var signalStrengthGoodTotalTime: Double = 0 + var signalStrengthFailTotalTime: Double = 0 + + override init() { + super.init() + setupBluetoothScanner() + } + + #if DEBUG + deinit { + print("bluetooth destroy") + } + #endif + + var bluetoothEnabled:Bool { + #if targetEnvironment(simulator) + return true + #else + return self.centralManager?.state == CBManagerState.poweredOn + #endif + } + + func scanForPeripherals(central:CBCentralManager) { + let advertiseIds = getBleAdvertiseUUIDs() + if advertiseIds.count > 0 { + central.scanForPeripherals(withServices: advertiseIds, options: nil) + } else { + central.stopScan() + } + } + + func setupBluetoothScanner(showPowerAlert:Bool = false) { + // Reset connection on new config. + if let peripheral = self.peripheral { + centralManager?.cancelPeripheralConnection(peripheral) + self.peripheral = nil + self.isPaired = false + self.currentSignalStatus = .noBluetoothConnection + } + if self.centralManager == nil || showPowerAlert { + centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main, options: [CBCentralManagerOptionShowPowerAlertKey: showPowerAlert]) + } else if (bluetoothEnabled) { + scanForPeripherals(central: centralManager!) + } // else wait for powered on status + + if GMFGTestScreenData.shared.isCPESimulated { + #if DEBUG + simulateBluetoothMessaging() + #endif + } else { + #if DEBUG && targetEnvironment(simulator) + simulateBluetoothMessaging() + #endif + } + } + + #if DEBUG + // For simulating RSSI changes. + var timer: Timer? + private func simulateBluetoothMessaging() { + // If we don't get advertisement data, technically nothing will come. + if getBleAdvertiseUUIDs().count == 0 { return } + + if timer != nil { return } + + var signalRotate = 0 + _ = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { (timer) in + self.delegate?.bluetoothStatus(isOn: true) + self.isCPEActivated = true + self.delegate?.updateActivatedStatus(isActivated: self.isCPEActivated) + self.isPaired = true + self.delegate?.pairUpdate(isPaired: self.isPaired) + signalRotate = (signalRotate + 1) % 5 + self.currentRSSIValue = Double(signalRotate) + if (signalRotate >= 3) { + self.currentRSSIValue = -76 + self.currentSignalStatus = .confirmedGood + } else if (signalRotate >= 2) { + self.currentRSSIValue = -86 + self.currentSignalStatus = .good + } else if (signalRotate >= 1) { + self.currentRSSIValue = -101 + self.currentSignalStatus = .poor + } else { + self.currentRSSIValue = -999 + self.currentSignalStatus = .noSignal + } + + self.delegate?.updateCurrentSignalStatus(self.currentSignalStatus, strengthCheckStatus:self.signalStrengthCheckStatus, rssi: self.currentRSSIValue) + self.bluetoothDebugger?.bluetoothStatus = true + self.bluetoothDebugger?.isDeviceActivated = true + self.bluetoothDebugger?.isDevicePaired = self.isPaired + self.bluetoothDebugger?.updateCurrentSignalStatus(self.currentSignalStatus, strengthCheckStatus:self.signalStrengthCheckStatus, rssi: self.currentRSSIValue) + } + } + #endif + + // Should be used when we detect scanning is no longer required. + private func stopScanning() { + if let peripheral = self.peripheral { + centralManager?.cancelPeripheralConnection(peripheral) + self.peripheral = nil; + } + centralManager?.stopScan() + centralManager = nil + } + + open func shutdown() { + stopScanning() + MFFGHSBluetoothPair.instance = nil + } + + public func stopNotify5G(controller: MFFGHSBluetoothPairDelegate) { + if let del = delegate, del === controller { + delegate = nil + } + resetSignalStrengthCheckValue() + } + + public func setup(config: BluetoothConfigModel, delegate: BluetoothDebuggerDelegate) { + bluetoothDebugger = BluetoothDebugger(config: config, delegate: delegate) + bluetoothConfig = config + } + + public func startNotify5G(controller: MFFGHSBluetoothPairDelegate) { + delegate = controller + // Initialize + delegate?.pairUpdate(isPaired: isPaired) + delegate?.updateActivatedStatus(isActivated: isCPEActivated) + delegate?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue) + + if let state = centralManager?.state { + bluetoothDebugger?.bluetoothState = state + } + bluetoothDebugger?.bluetoothStatus = bluetoothEnabled + bluetoothDebugger?.isDevicePaired = isPaired + bluetoothDebugger?.isDeviceActivated = isCPEActivated + bluetoothDebugger?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus:signalStrengthCheckStatus, rssi: currentRSSIValue) + } + + // MARK:- Bluetooth delegate + + public func centralManagerDidUpdateState(_ central: CBCentralManager) { + bluetoothDebugger?.bluetoothState = central.state + switch (central.state) { + case .poweredOn: + delegate?.bluetoothStatus(isOn: true) + bluetoothDebugger?.bluetoothStatus = true + scanForPeripherals(central: central) + case .poweredOff, .unsupported, .unauthorized, .unknown: + delegate?.bluetoothStatus(isOn: false) + bluetoothDebugger?.bluetoothStatus = false + default: + print("bluetooth not powered on") + } + } + + public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { + if findPeripheralName(getBleAdvertiseName(), in: advertisementData) { + self.peripheral = peripheral + peripheral.delegate = self + central.connect(peripheral, options: nil) + central.stopScan() + } else { + if let foundName = advertisementData[CBAdvertisementDataLocalNameKey] as? String, foundName.hasPrefix(GMFGConstant.BLE.advertisePrefix) { + delegate?.deviceIMEIMismatch(expected: getBleAdvertiseName(), found: foundName) + bluetoothDebugger?.addDeviceIMEIMismatch(found: foundName) + } + } + } + + private func findPeripheralName(_ name: String, in advertisementData: [String : Any]) -> Bool { + var found = false + var foundAdvertisedNames: [String] = [] + + // Check the local data name. (Where it should be. Used for testing app.) + if let localServiceName = advertisementData[CBAdvertisementDataLocalNameKey] as? String { + if localServiceName == name { + found = true + } else { + foundAdvertisedNames.append(localServiceName) + } + } + // Check the service data. (Where it is actually implemented by CPE.) + if !found, let avdService = advertisementData[CBAdvertisementDataServiceDataKey] as? [CBUUID:Data] { + for key in avdService.keys { + if let rawName = avdService[key], + let advertisedName = String(bytes: rawName, encoding: .utf8) { + if (advertisedName == name) { + found = true + break + } else { + foundAdvertisedNames.append(advertisedName) + } + } + } + } + + if found { + // Currently exact equality passes. Later can adjust to be actual advertised name if the requirement relaxes. + delegate?.onBluetoothDiscovered(expectedName: name, foundName: name) + bluetoothDebugger?.deviceDiscovered = name + let value: [String : Any] = [ + "blePairStatus": "discovered", + "expectedBroadcastName": name, + "foundBroadcastName": name + ] + track(value) + + return true; + } else { + delegate?.onBluetoothDiscoveredNameFailed(expectedName: name, foundNames: foundAdvertisedNames) + bluetoothDebugger?.addDiscoveredNameFailures(foundNames: foundAdvertisedNames) + let value: [String : Any] = [ + "blePairStatus": "mismatch", + "expectedBroadcastName": name, + "foundBroadcastName": foundAdvertisedNames.joined(separator: " / ").prefix(500) + ] + track(value) + + return false + } + } + + public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { + isPaired = true + delegate?.pairUpdate(isPaired: true) + bluetoothDebugger?.isDevicePaired = true + peripheral.discoverServices(getBleAdvertiseUUIDs()) // For testing app which can only advertise on 1 service. + peripheral.discoverServices(getSeviceUUIDs()) + } + + public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { + for service in peripheral.services ?? [] { + peripheral.discoverCharacteristics(getCharacteristicUUIDs(serviceId: service.uuid), for: service) + } + } + + public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { + for characteristic in service.characteristics ?? [] { + peripheral.setNotifyValue(true, for: characteristic) + } + } + + public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { + + let cpeResponse = formatCharacteristicData(characteristic) + + handleActivated(response: cpeResponse) + + handleFiveGSignal(response: cpeResponse) + } + + public func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { } + + public func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { + // We only connect to 1 peripheral. Using that assumption below. + isPaired = false + currentSignalStatus = .noBluetoothConnection + currentRSSIValue = nil + delegate?.pairUpdate(isPaired: false) + delegate?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue) + bluetoothDebugger?.isDevicePaired = false + bluetoothDebugger?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue) + self.peripheral = nil + scanForPeripherals(central: central) + } + + func formatCharacteristicData(_ characteristic: CBCharacteristic) -> [String: Any] { + guard + let advertiseData = characteristic.value, !advertiseData.isEmpty, + let advertiseDict = try? JSONSerialization.jsonObject(with: advertiseData, options: []) as? [String: Any] + else { + return [:] + } + return advertiseDict + } + + // MARK: Handle Activation + func handleActivated(response: [String: Any]) { + if let activation = response["Activation"] as? [String: Any] { + isCPEActivated = activation.stringForkey("Status") == "Activated" + delegate?.updateActivatedStatus(isActivated: isCPEActivated) + bluetoothDebugger?.isDeviceActivated = isCPEActivated + } + } + + // MARK: Handle FiveGSignal + func handleFiveGSignal(response: [String: Any]) { + if let fivegSignal = response["5GSignal"] as? [String: Any] { + if let rssiValue = parseRSRPFromJSON(fiveGData: fivegSignal) { + currentRSSIValue = rssiValue + + // This need to call before currentSignalStatus set with new status. This is to gather the amount of time on the previous signal status before the update. + if signalStrengthCheckStatus == .processing { + //lastSignalStatusTimeStamp = lastSignalStatusTimeStamp != nil ? lastSignalStatusTimeStamp : Date() + checkSignalStrengthContinueRequired() + } + + if GMFGTestScreenData.shared.isEnable5GSignal { + currentSignalStatus = currentSignalStatus == .confirmedGood ? .confirmedGood : .good + } else { + if rssiValue >= higherThreshold || rssiValue <= lowerThreshold { + // Future reference: -999 means no signal from CPE. Need to make sure it will always be included in check. + // Invalid value. We should not be above the upper limits or beneath the lower limits. + currentSignalStatus = .noSignal + } else if rssiValue >= rssiThreshold { + currentSignalStatus = currentSignalStatus == .confirmedGood ? .confirmedGood : .good + } else { + currentSignalStatus = .poor + } + } + } else { + currentSignalStatus = .noSignal // Invalid value or doesn't exist. + } + + delegate?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue) + bluetoothDebugger?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue) + } + } + + // MARK:- Handle Signal History + + private func resetSignalStrengthCheckValue() { + signalStrengthCheckStatus = .idle + signalStrengthObserverTimer?.invalidate() + signalStrengthObserverTimer = nil + lastSignalStatusTimeStamp = nil + signalStrengthGoodTotalTime = 0 + signalStrengthFailTotalTime = 0 + } + + func checkSignalStrengthContinueRequired() { + let lastTrackDate = lastSignalStatusTimeStamp ?? Date() + if [.good, .confirmedGood].contains(currentSignalStatus) { + signalStrengthGoodTotalTime += Date().timeIntervalSince(lastTrackDate) + } else { + signalStrengthFailTotalTime += Date().timeIntervalSince(lastTrackDate) + } + lastSignalStatusTimeStamp = Date() + evaluateSignalStrength() + } + + private func evaluateSignalStrength() { + let maxFailPercentage = 100 - signalPassingPercentage + let maxFailTime = (maxFailPercentage / 100) * signalStrengthObserveDuration + let maxPassTime = (signalPassingPercentage / 100) * signalStrengthObserveDuration + + #if DEBUG + print("[ MFFGHSBluetoothPair ] Good Signal Time >>>> \(signalStrengthGoodTotalTime)") + print("[ MFFGHSBluetoothPair ] Bad Signal Time >>>> \(signalStrengthFailTotalTime)") + #endif + + if signalStrengthFailTotalTime > maxFailTime { + // already Fail, stop testing + resetSignalStrengthCheckValue() + signalStrengthCheckStatus = .complete + currentSignalStatus = .confirmedPoor + delegate?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue) + bluetoothDebugger?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue) + } else if signalStrengthGoodTotalTime >= maxPassTime || signalStrengthCheckStatus == .complete { + // already PASS. stop testing and handle + resetSignalStrengthCheckValue() + signalStrengthCheckStatus = .complete + currentSignalStatus = .confirmedGood + delegate?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue) + bluetoothDebugger?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue) + } else { + //contiue + } + } + + public func startSignalStrengthCheckValue(duration: TimeInterval, percentage: Double){ + if signalStrengthCheckStatus != .processing { + resetSignalStrengthCheckValue() + signalStrengthObserveDuration = duration + signalPassingPercentage = percentage + if currentSignalStatus == .confirmedGood { + currentSignalStatus = .good + } + signalStrengthCheckStatus = .processing + lastSignalStatusTimeStamp = Date() + signalStrengthObserverTimer = Timer(timeInterval: (signalStrengthObserveDuration + 0.3), repeats: false) { (timer) in + #if DEBUG + print("[ MFFGHSBluetoothPair ] Signal Test Timeout") + #endif + self.signalStrengthCheckStatus = .complete + self.checkSignalStrengthContinueRequired() + } + RunLoop.main.add(signalStrengthObserverTimer!, forMode: .common) + } + } + + // MARK:- Utility Functions + func parseRSRPFromJSON(fiveGData: [String: Any]) -> Double? { + return fiveGData["SS-RSRP"] as? Double ?? nil + } + + // MARK:- Analytics + func track(_ data: [String: Any]) { + if let _delegate = delegate { + trackGemini(value: data.merging(["delegateName": String(describing: _delegate)]) { $1 }, logType: .BLE) + } + } + + // MARK: BluetoothConfigModel methods + var rssiThreshold: Double { + return bluetoothConfig?.bleSignalThreshold ?? 0 + } + + var lowerThreshold: Double { + return bluetoothConfig?.bleSignalLowerBound ?? 0 + } + + var higherThreshold: Double { + return bluetoothConfig?.bleSignalUpperBound ?? 0 + } + + func getBleAdvertiseUUIDs() -> [CBUUID] { + return bluetoothConfig?.peripherals.map{ $0.uuid } ?? [] + } + + open func getBleAdvertiseName() -> String { + return bluetoothConfig?.advertisedData ?? "" + } + + func getSeviceUUIDs() -> [CBUUID] { + return bluetoothConfig?.services.map { $0.uuid } ?? [] + } + + func getCharacteristicUUIDs(serviceId: CBUUID) -> [CBUUID] { + return bluetoothConfig?.services.first?.characteristics.map { $0.uuid } ?? [] + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/MFFGHSUtility.swift b/JSONCreator_iOS/JSONCreator/5G/MFFGHSUtility.swift new file mode 100644 index 0000000..8415ee2 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/MFFGHSUtility.swift @@ -0,0 +1,17 @@ +import MVMCore +import MVMCoreUI + +extension ViewController { + public func fivegBLEConfig() throws -> BluetoothConfigModel { + guard let json = loadObject?.modulesJSON?.dictionaryWithChainOfKeysOrIndexes(["fivegBleUuid", "fivegBleUuid"]) else { + throw BluetoothConfigError.noJSONFound + } + return try BluetoothConfigModel.decode(jsonDict: json, delegateObject: nil) + } +} + +extension Collection { + var isNotEmpty: Bool { + return !isEmpty + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Models/BluetoothConfigModel.swift b/JSONCreator_iOS/JSONCreator/5G/Models/BluetoothConfigModel.swift new file mode 100644 index 0000000..3fdcd80 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Models/BluetoothConfigModel.swift @@ -0,0 +1,103 @@ +// +// BluetoothConfig.swift +// MVM5G +// +// Created by Matt Bruce on 4/13/22. +// Copyright © 2022 Kyle. All rights reserved. +// + +import Foundation +import CoreBluetooth +import MVMCore + +public enum BluetoothConfigError: Error, LocalizedError, CustomStringConvertible { + case decode(key: String) + case noJSONFound + + public var description: String { + switch self { + case .decode: + return GMFGConstant.BLE.LogMsg.bleDecodeError + case .noJSONFound: + return GMFGConstant.BLE.LogMsg.bleModuleMissing + } + } + + public var errorDescription: String? { + return description + } +} + +public struct BluetoothConfigModel: Codable, Equatable { + var advertisedData: String + var pin: String + var bleAPIVersion: Double + var bleSignalThreshold: Double + var bleSignalLowerBound: Double + var bleSignalUpperBound: Double + var peripherals: [PeripheralModel] + var services: [ServiceModel] + var logger: ((String, String, [String: Any])-> Void)? + + enum CodingKeys: String, CodingKey { + case bleAPIVersion + case pin + case bleSignalThreshold + case bleSignalLowerBound + case bleSignalUpperBound + case advertisedData + case bleAdv + case service + } + + public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + let _bleAPIVersion = try typeContainer.decode(String.self, forKey: .bleAPIVersion, objectType: BluetoothConfigModel.self) + let _bleSignalThreshold = try typeContainer.decode(String.self, forKey: .bleSignalThreshold, objectType: BluetoothConfigModel.self) + let _bleSignalLowerBound = try typeContainer.decode(String.self, forKey: .bleSignalLowerBound, objectType: BluetoothConfigModel.self) + let _bleSignalUpperBound = try typeContainer.decode(String.self, forKey: .bleSignalUpperBound, objectType: BluetoothConfigModel.self) + + guard let double = Double(_bleAPIVersion) else { + throw BluetoothConfigError.decode(key: "bleAPIVersion: \(_bleAPIVersion)") + } + bleAPIVersion = double + + guard let double = Double(_bleSignalThreshold) else { + throw BluetoothConfigError.decode(key: "bleSignalThreshold: \(_bleSignalThreshold)") + } + bleSignalThreshold = double + + guard let double = Double(_bleSignalLowerBound) else { + throw BluetoothConfigError.decode(key: "bleSignalLowerBound: \(_bleSignalLowerBound)") + } + bleSignalLowerBound = double + + guard let double = Double(_bleSignalUpperBound) else { + throw BluetoothConfigError.decode(key: "bleSignalUpperBound: \(_bleSignalUpperBound)") + } + bleSignalUpperBound = double + pin = try typeContainer.decode(String.self, forKey: .pin, objectType: BluetoothConfigModel.self) + advertisedData = try typeContainer.decode(String.self, forKey: .advertisedData, objectType: BluetoothConfigModel.self) + peripherals = try typeContainer.decode([PeripheralModel].self, forKey: .bleAdv, objectType: BluetoothConfigModel.self) + services = try typeContainer.decode([ServiceModel].self, forKey: .service, objectType: BluetoothConfigModel.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(bleAPIVersion, forKey: .bleAPIVersion) + try container.encode(pin, forKey: .pin) + try container.encode(bleSignalThreshold, forKey: .bleSignalThreshold) + try container.encode(bleSignalLowerBound, forKey: .bleSignalLowerBound) + try container.encode(bleSignalUpperBound, forKey: .bleSignalUpperBound) + try container.encode(advertisedData, forKey: .advertisedData) + try container.encode(peripherals, forKey: .bleAdv) + try container.encode(services, forKey: .service) + } + + public static func == (lhs: BluetoothConfigModel, rhs: BluetoothConfigModel) -> Bool { + return lhs.advertisedData == rhs.advertisedData && + lhs.pin == rhs.pin && + lhs.peripherals == rhs.peripherals && + lhs.services == rhs.services + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Models/CharacteristicModel.swift b/JSONCreator_iOS/JSONCreator/5G/Models/CharacteristicModel.swift new file mode 100644 index 0000000..c1a24cb --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Models/CharacteristicModel.swift @@ -0,0 +1,44 @@ +// +// Characteristic.swift +// MVM5G +// +// Created by Matt Bruce on 4/13/22. +// Copyright © 2022 Kyle. All rights reserved. +// + +import Foundation +import CoreBluetooth + +protocol CBUUIDIdentifiable { + var uuid: CBUUID { get set} +} + +public enum CharacteristicType: String { + case speedTest = "RouterSpeedTest" + case speedTestResults = "RouterSpeedTestResults" + case internetAccess = "RouterPublicInternetAccess" + case operationMode = "ReceiverMode" + case fotaCheck = "FOTACheck" + case fotaStatus = "FOTAStatus" + case fourGSignal = "Fourg" + case fiveGSignal = "Fiveg" + case fiveGCBandSignal = "FivegCBand" + case routerWiFi = "RouterWifi" + case routerMode = "RouterMode" + case repeaterOperations = "RepeaterOperations" + case repeaterPairStatus = "RepeaterPairStatus" + case repeaterMode = "RepeaterMode" + case repeaterPair = "RepeaterPair" + case radiocontrol = "Radiocontrol" + case activation = "Activation" + case unknown = "unknown" +} + +public struct CharacteristicModel: CBUUIDIdentifiable, Equatable{ + var uuid: CBUUID + var name: CharacteristicType + + public static func == (lhs: CharacteristicModel, rhs: CharacteristicModel) -> Bool { + return lhs.uuid == rhs.uuid && lhs.name == rhs.name + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Models/PeripheralModel.swift b/JSONCreator_iOS/JSONCreator/5G/Models/PeripheralModel.swift new file mode 100644 index 0000000..c24e801 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Models/PeripheralModel.swift @@ -0,0 +1,34 @@ +// +// Peripheral.swift +// MVM5G +// +// Created by Matt Bruce on 4/13/22. +// Copyright © 2022 Kyle. All rights reserved. +// + +import Foundation +import CoreBluetooth + +public struct PeripheralModel: CBUUIDIdentifiable, Codable, Equatable { + var uuid: CBUUID + + enum CodingKeys: String, CodingKey { + case uuid + } + + public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + let _uuid = try typeContainer.decode(String.self, forKey: .uuid) + uuid = CBUUID(string: _uuid) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(uuid.uuidString, forKey: .uuid) + } + + public static func == (lhs: PeripheralModel, rhs: PeripheralModel) -> Bool { + return lhs.uuid == rhs.uuid + } + +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Models/ServiceModel.swift b/JSONCreator_iOS/JSONCreator/5G/Models/ServiceModel.swift new file mode 100644 index 0000000..4161b3a --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Models/ServiceModel.swift @@ -0,0 +1,44 @@ +// +// Service.swift +// MVM5G +// +// Created by Matt Bruce on 4/13/22. +// Copyright © 2022 Kyle. All rights reserved. +// + +import Foundation +import CoreBluetooth + +public struct ServiceModel: CBUUIDIdentifiable, Codable, Equatable { + var uuid: CBUUID + var characteristics: [CharacteristicModel] + + enum CodingKeys: String, CodingKey { + case uuid + case characteristicUUID + } + + public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + let _uuid = try typeContainer.decode(String.self, forKey: .uuid) + uuid = CBUUID(string: _uuid) + let _characteristics = try typeContainer.decode([String: String].self, forKey: .characteristicUUID) + characteristics = _characteristics.map { key, value in + return CharacteristicModel(uuid: CBUUID(string: value), name: CharacteristicType(rawValue: key) ?? .unknown) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(uuid.uuidString, forKey: .uuid) + var _characteristics: [String: String] = [:] + characteristics.forEach { c in + _characteristics[c.name.rawValue] = c.uuid.uuidString + } + try container.encode(_characteristics, forKey: .characteristicUUID) + } + + public static func == (lhs: ServiceModel, rhs: ServiceModel) -> Bool { + return lhs.uuid == rhs.uuid && lhs.characteristics == rhs.characteristics + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Protocols/BluetoothPairableProtocol.swift b/JSONCreator_iOS/JSONCreator/5G/Protocols/BluetoothPairableProtocol.swift new file mode 100644 index 0000000..988e6e5 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Protocols/BluetoothPairableProtocol.swift @@ -0,0 +1,65 @@ +// +// BluetoothPairableProtocol.swift +// MVM5G +// +// Created by Matt Bruce on 4/15/22. +// Copyright © 2022 Kyle. All rights reserved. +// + +import Foundation +import MVMCore +import MVMCoreUI + +/// This Protocol will be implemented by UI +protocol BluetoothPairableProtocol: AnyObject { + associatedtype BluetoothPairingType: BluetoothParingProtocol + var bluetoothPair: BluetoothPairingType? { get set } + func setBluetoothConfig() -> Bool + func track(_ message: Any, logType: AnalyticsLogType) +} + +extension BluetoothPairableProtocol where Self: ViewController, Self: MFFGHSAnalyticsProtocol { + @discardableResult + func setBluetoothConfig() -> Bool { + do { + let config = try fivegBLEConfig() + bluetoothPair?.bluetoothConfig = config + return true + } catch { + track(error.localizedDescription) + return false + } + } + + func track(_ message: Any, logType: AnalyticsLogType = .UI) { + if let message = message as? String { + trackGemini(value: [KeyMessage: message], logType: logType, pageInfo: AnalyticsPageInfo(pageType: loadObject?.pageType)) + } else if let dict = message as? [String: Any] { + trackGemini(value: dict, logType: logType, pageInfo: AnalyticsPageInfo(pageType: loadObject?.pageType)) + } + } +} + +extension BluetoothPairableProtocol where Self: ViewController, Self: MFFGHSAnalyticsProtocol, Self: BluetoothDebuggableProtocol { + @discardableResult + func setBluetoothConfig() -> Bool { + do { + let config = try fivegBLEConfig() + bluetoothPair?.setup(config: config, delegate: self) + return true + } catch { + track(error.localizedDescription) + return false + } + } + + func track(_ message: Any, logType: AnalyticsLogType = .UI) { + if let message = message as? String { + bluetoothDebugger?.addLog(message: message) + trackGemini(value: [KeyMessage: message], logType: logType, pageInfo: AnalyticsPageInfo(pageType: loadObject?.pageType)) + } else if let dict = message as? [String: Any] { + bluetoothDebugger?.addLog(message: dict.description) + trackGemini(value: dict, logType: logType, pageInfo: AnalyticsPageInfo(pageType: loadObject?.pageType)) + } + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Protocols/BluetoothPairingProtocol.swift b/JSONCreator_iOS/JSONCreator/5G/Protocols/BluetoothPairingProtocol.swift new file mode 100644 index 0000000..5837f0f --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Protocols/BluetoothPairingProtocol.swift @@ -0,0 +1,32 @@ +// +// BluetoothPairingProtocol.swift +// MVM5G +// +// Created by Matt Bruce on 4/15/22. +// Copyright © 2022 Kyle. All rights reserved. +// + +import Foundation + +/// This Protocol will be implemented by a variation of BluetoothPairing Classes +protocol BluetoothParingProtocol: AnyObject, MFFGHSAnalyticsProtocol { + var bluetoothConfig: BluetoothConfigModel? { get set } + var bluetoothDebugger: BluetoothDebugger? { get set } + func getBleAdvertiseName() -> String + func track(_ data: [String: Any]) + func track(_ message: String, function: String, additionalData: [String: Any]) +} + +extension BluetoothParingProtocol { + func track(_ message: String, function: String = #function, additionalData: [String: Any] = [:]) { + guard bluetoothConfig != nil else { return } + + let data = additionalData.values.isEmpty ? "" : "\r \(additionalData)" + bluetoothDebugger?.addLog(message: "\(message)\(data)") + + trackGemini(value: ([GMFGConstant.functionName: function, + GMFGConstant.BLE.advertisedData: getBleAdvertiseName(), + "message": message + ]).merging(additionalData, uniquingKeysWith: { $1 }), logType: .BLE) + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFG5GCBandSignalHandler.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFG5GCBandSignalHandler.swift new file mode 100644 index 0000000..eaeda77 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFG5GCBandSignalHandler.swift @@ -0,0 +1,105 @@ +// +// GMFG5GSignalHandler.swift +// MVM5G +// +// Created by Jason Beck on 5/11/21. +// Copyright © 2021 Kyle. All rights reserved. +// +import CoreBluetooth + +protocol GMFG5GCBandSignalDelegate: AnyObject { + func updated5GCBandSignal(_ update: GMFG5GCBandSignalUpdate) + func signal5GCBandNotReady() +} + +private enum ResultKeys: String { + case signalNoiseRatio = "SINR" + case referencePower = "RSRP" + case pci = "PCI" + case ssbIndex = "SSBI" + case referenceSignalQuality = "RSRQ" + case modulationOrder = "MO" + case mcsIndex = "MCS " //This is on purpose. The firmware has a trailing space in the key name. + case arfcn5G = "5GARFCN" + case ledColor = "LED" +} + +struct GMFG5GCBandSignalUpdate { + var signalToNoiseRatio: Int = 0 + var referencePower: Int = 0 + var pci: Int = 0 + var ssbIndex: Int = 0 + var referenceSignalQuality: Int = 0 + var modulationOrder: String = "NA" + var mcsIndex: Int = 0 + var arfcn5G: Int = 0 + var ledColor: GMFGCPELEDColor? + var rawResults: [String: Any]? +} + +class GMFG5GCBandSignalHandler: GMFGBLEHandlerProtocol { + + private let multicast = MulticastDelegate() + public func setDelegate(delegates: [GMFG5GCBandSignalDelegate?]) { + multicast.add(delegates: delegates) + } + + public func reset() { + multicast.removeAll() + } + + //MARK:- Helper + private func rawKey(_ type: ResultKeys) -> String { return type.rawValue } + private func rawStatus(_ type: GMFGCPELEDColor) -> String { return type.rawValue } + + public func get5GSignal(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic) { + peripheral.readValue(for: characteristic) + } + + //It is possible to try and call the device before it's done loading + public func deviceNotReady() { + multicast.invokeForEachDelegate{ $0.signal5GCBandNotReady() } + } + + public func response(response: [String : Any]) { + if let validResponse = responseFormat(response: response) { + multicast.invokeForEachDelegate{ $0.updated5GCBandSignal(validResponse) } + } + } + + func responseFormat(response: [String: Any]) -> GMFG5GCBandSignalUpdate? { + guard let fiveGData = response["5GSignal"] as? [String: Any] else { + return nil + } + var returnSignal = GMFG5GCBandSignalUpdate() + if let signalToNoise = (fiveGData[rawKey(.signalNoiseRatio)] as? String), let snrInt = Int(signalToNoise) { + returnSignal.signalToNoiseRatio = snrInt + } + if let referenceSignalPower = (fiveGData[rawKey(.referencePower)] as? String), let refPowerInt = Int(referenceSignalPower) { + returnSignal.referencePower = refPowerInt + } + if let fiveGARFCN = (fiveGData[rawKey(.arfcn5G)] as? String), let arfcnInt = Int(fiveGARFCN) { + returnSignal.arfcn5G = arfcnInt + } + if let referenceQuality = (fiveGData[rawKey(.referenceSignalQuality)] as? String), let sqInt = Int(referenceQuality) { + returnSignal.referenceSignalQuality = sqInt + } + if let ssbi = (fiveGData[rawKey(.ssbIndex)] as? String), let ssbInt = Int(ssbi) { + returnSignal.ssbIndex = ssbInt + } + if let ledColorRaw = (fiveGData[rawKey(.ledColor)] as? String), let ledColor = GMFGCPELEDColor(rawValue: ledColorRaw) { + returnSignal.ledColor = ledColor + } + if let mcs = (fiveGData[rawKey(.mcsIndex)] as? String), let mcsInt = Int(mcs) { + returnSignal.mcsIndex = mcsInt + } + if let pci = (fiveGData[rawKey(.pci)] as? String), let pciInt = Int(pci) { + returnSignal.pci = pciInt + } + if let mo = (fiveGData[rawKey(.modulationOrder)] as? String) { + returnSignal.modulationOrder = mo + } + returnSignal.rawResults = fiveGData + return returnSignal + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFG5GSignalHandler.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFG5GSignalHandler.swift new file mode 100644 index 0000000..d3898d1 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFG5GSignalHandler.swift @@ -0,0 +1,111 @@ +// +// GMFG5GSignalHandler.swift +// MVM5G +// +// Created by Jason Beck on 5/11/21. +// Copyright © 2021 Kyle. All rights reserved. +// +import CoreBluetooth + +protocol GMFG5GSignalDelegate: AnyObject { + func updated5GSignal(_ update: GMFG5GSignalUpdate) + func signalNotReady() +} + +private enum ResultKeys: String { + case signalNoiseRatio = "SINR" + case referencePower = "RSRP" + case pci = "PCI" + case ssbIndex = "SSBI" + case referenceSignalQuality = "RSRQ" + case modulationOrder = "MO" + case mcsIndex = "MCS " //This is on purpose. The firmware has a trailing space in the key name. + case arfcn5G = "5GARFCN" + case ledColor = "LED" +} + +enum GMFGCPELEDColor: String { + case red = "R" + case yellow = "Y" + case green = "G" +} + +struct GMFG5GSignalUpdate { + var signalToNoiseRatio: Int = 0 + var referencePower: Int = 0 + var pci: Int = 0 + var ssbIndex: Int = 0 + var referenceSignalQuality: Int = 0 + var modulationOrder: String = "NA" + var mcsIndex: Int = 0 + var arfcn5G: Int = 0 + var ledColor: GMFGCPELEDColor? + var rawResults: [String: Any]? +} + +class GMFG5GSignalHandler: GMFGBLEHandlerProtocol { + + private let multicast = MulticastDelegate() + public func setDelegate(delegates: [GMFG5GSignalDelegate?]) { + multicast.add(delegates: delegates) + } + + public func reset() { + multicast.removeAll() + } + + //MARK:- Helper + private func rawKey(_ type: ResultKeys) -> String { return type.rawValue } + private func rawStatus(_ type: GMFGCPELEDColor) -> String { return type.rawValue } + + public func get5GSignal(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic) { + peripheral.readValue(for: characteristic) + } + + //It is possible to try and call the device before it's done loading + public func deviceNotReady() { + multicast.invokeForEachDelegate{ $0.signalNotReady() } + } + + public func response(response: [String : Any]) { + if let validResponse = responseFormat(response: response) { + multicast.invokeForEachDelegate{ $0.updated5GSignal(validResponse) } + } + } + + func responseFormat(response: [String: Any]) -> GMFG5GSignalUpdate? { + guard let fiveGData = response["5GSignal"] as? [String: Any] else { + return nil + } + var returnSignal = GMFG5GSignalUpdate() + if let signalToNoise = (fiveGData[rawKey(.signalNoiseRatio)] as? String), let snrInt = Int(signalToNoise) { + returnSignal.signalToNoiseRatio = snrInt + } + if let referenceSignalPower = (fiveGData[rawKey(.referencePower)] as? String), let refPowerInt = Int(referenceSignalPower) { + returnSignal.referencePower = refPowerInt + } + if let fiveGARFCN = (fiveGData[rawKey(.arfcn5G)] as? String), let arfcnInt = Int(fiveGARFCN) { + returnSignal.arfcn5G = arfcnInt + } + if let referenceQuality = (fiveGData[rawKey(.referenceSignalQuality)] as? String), let sqInt = Int(referenceQuality) { + returnSignal.referenceSignalQuality = sqInt + } + if let ssbi = (fiveGData[rawKey(.ssbIndex)] as? String), let ssbInt = Int(ssbi) { + returnSignal.ssbIndex = ssbInt + } + if let ledColorRaw = (fiveGData[rawKey(.ledColor)] as? String), let ledColor = GMFGCPELEDColor(rawValue: ledColorRaw) { + returnSignal.ledColor = ledColor + } + if let mcs = (fiveGData[rawKey(.mcsIndex)] as? String), let mcsInt = Int(mcs) { + returnSignal.mcsIndex = mcsInt + } + if let pci = (fiveGData[rawKey(.pci)] as? String), let pciInt = Int(pci) { + returnSignal.pci = pciInt + } + if let mo = (fiveGData[rawKey(.modulationOrder)] as? String) { + returnSignal.modulationOrder = mo + } + returnSignal.rawResults = fiveGData + return returnSignal + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGFotaHandler.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGFotaHandler.swift new file mode 100644 index 0000000..813d15f --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGFotaHandler.swift @@ -0,0 +1,132 @@ +// +// GMFGFotaHandler.swift +// MVM5G +// +// Created by Jason Beck on 4/20/20. +// Copyright © 2020 Kyle. All rights reserved. +// +import CoreBluetooth + +private enum GMFotaKey: String { + case status = "FOTAStatus" + case error = "ErrorMessage" + case version = "FirmwareVersion" + case check = "Check" + case fotaOp = "FOTAOperation" +} + +public enum GMFotaStatus: String { + case notReady = "NotReady" + case latest = "Latest" + case available = "NewAvailable" + case success = "SuccessfulUpgrade" + case fail = "FailedUpgrade" + case failDownload = "FailedDownload" + case progress = "DownloadProgress" + case upgrading = "Upgrading" +} + +protocol GMFGFotaDelegate: AnyObject { + func didStartUpdate() + func fotaStatus(statusResponse: GMFotaResponse) + func unknownResponse(rawResponse: [String: Any]?) +} + +extension GMFGFotaDelegate { + func didStartUpdate() {} +} + +final class GMFotaResponse: CustomStringConvertible { + var status: GMFotaStatus + var errorMsg: String? + private var _version = "" + var version: String { + set { + if (newValue.isEmpty) || (newValue == "XXX.YYY.ZZZZ") || (newValue == "Unknown") { + _version = "0.0.0.0" + } else { + _version = newValue + } + } + get { + return _version + } + } + var rawResponse: [String: Any] + + init(status: GMFotaStatus, version: String, rawResponse: [String: Any]) { + self.status = status + self.rawResponse = rawResponse + self.version = version + } + + var description: String { + return """ + Status: \(self.status.rawValue) + Error Message: \(self.errorMsg ?? GMFGConstant.empty) + Version: \(self.version) + Raw Response:\n\(self.rawResponse) + """ + } +} + +final class GMFGFotaHandler: GMFGBLEHandlerProtocol { + + private let multicast = MulticastDelegate() + + public func setDelegate(delegates: [GMFGFotaDelegate?]) { + self.multicast.add(delegates: delegates) + } + + public func reset() { + self.multicast.removeAll() + } + + //MARK:- Helper + private func rawKey(_ type: GMFotaKey) -> String { return type.rawValue } + private func rawStatus(_ type: GMFotaStatus) -> String { return type.rawValue } + + //MARK:- Fota Functions + public func requestVersion(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic) { + peripheral.readValue(for: characteristic) + } + + public func startUpdate(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic) { + let requestValue = [rawKey(.fotaOp): rawKey(.check)] + guard let data = convertToData(requestValue) else { return } + let writeType: CBCharacteristicWriteType = characteristic.properties.contains(.writeWithoutResponse) ? .withoutResponse : .withResponse + peripheral.writeValue(data, for: characteristic, type: writeType) + multicast.invokeForEachDelegate { $0.didStartUpdate() } + } + + public func response(response: [String : Any]) { + let status: GMFotaStatus! + var version = "0.0.0.0" + + guard let statusString = response[rawKey(.status)] as? String else { + multicast.invokeForEachDelegate{ $0.unknownResponse(rawResponse: response) } + return + } + + if statusString.contains("DownloadProgress") { + status = .progress + } else if let _status = GMFotaStatus(rawValue: statusString), + let _version = response[rawKey(.version)] as? String { + status = _status + version = _version + } else { + multicast.invokeForEachDelegate{ $0.unknownResponse(rawResponse: response) } + return + } + + let responseObj = GMFotaResponse(status: status, version: version, rawResponse: response) + responseObj.errorMsg = response[rawKey(.error)] as? String + multicast.invokeForEachDelegate{ $0.fotaStatus(statusResponse: responseObj) } + } + + public func deviceNotReady() { + let responseObj = GMFotaResponse(status: .notReady, version: "Unknown", rawResponse: [:]) + multicast.invokeForEachDelegate{ $0.fotaStatus(statusResponse: responseObj) } + } +} + diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGOperationHandler.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGOperationHandler.swift new file mode 100644 index 0000000..8ffe54f --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGOperationHandler.swift @@ -0,0 +1,34 @@ +// +// GMFGOperationHandler.swift +// MVM5G +// +// Created by Jason Beck on 7/28/20. +// Copyright © 2020 Kyle. All rights reserved. +// +import CoreBluetooth + +enum GMFGOperationMode: String { + case installation = "Installation" + case operational = "Operational" +} + +class GMFGOperationHandler: GMFGBLEHandlerProtocol { + public func setOpMode(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic, mode: GMFGOperationMode, disableBLE: Bool = false) -> Bool { + var requestValue: [String: Any] = ["ReceiverMode": mode.rawValue] + if disableBLE { + requestValue["DisableBLE"] = disableBLE + } + guard let data = convertToData(requestValue) else { return false } + let writeType: CBCharacteristicWriteType = characteristic.properties.contains(.writeWithoutResponse) ? .withoutResponse : .withResponse + peripheral.writeValue(data, for: characteristic, type: writeType) + return true + } + + func response(response: [String : Any]) { + //This class does not currently respond to the VC, so this function is here only to satisfy protocol conformance + } + + func deviceNotReady() { + //This class does not currently respond to the VC, so this function is here only to satisfy protocol conformance + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGPublicInternetAccessHandler.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGPublicInternetAccessHandler.swift new file mode 100644 index 0000000..7502b70 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGPublicInternetAccessHandler.swift @@ -0,0 +1,47 @@ +// +// GMFGPublicInternetAccessHandler.swift +// MVM5G +// +// Created by Jason Beck on 5/27/20. +// Copyright © 2020 Kyle. All rights reserved. +// +import CoreBluetooth + +protocol GMFGPublicInternetAccessDelegate: AnyObject { + func publicInternetAccessStatus(status: GMPublicInternetAccessStatus) +} + +enum GMPublicInternetAccessStatus: String { + case enabled = "Enabled" + case disabled = "Disabled" + case notReady = "NotReady" +} + +class GMFGPublicInternetAccessHandler: GMFGBLEHandlerProtocol { + + private let multicast = MulticastDelegate() + + public func setDelegate(delegates: [GMFGPublicInternetAccessDelegate?]) { + self.multicast.add(delegates: delegates) + } + + public func reset() { + self.multicast.removeAll() + } + + public func getPublicInternetAccess(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic) { + peripheral.readValue(for: characteristic) + } + + public func response(response: [String : Any]) { + guard let statusString = response["PublicInternetAccess"] as? String, let statusEnum = GMPublicInternetAccessStatus.init(rawValue: statusString) else { + return + } + multicast.invokeForEachDelegate{ $0.publicInternetAccessStatus(status: statusEnum) } + reset() + } + + public func deviceNotReady() { + multicast.invokeForEachDelegate{ $0.publicInternetAccessStatus(status: .notReady) } + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGRouterWifiHandler.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGRouterWifiHandler.swift new file mode 100644 index 0000000..06e8026 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGRouterWifiHandler.swift @@ -0,0 +1,51 @@ +// +// GMFGRouterWifiHandler.swift +// MVM5G +// +// Created by Rajesh on 12/9/20. +// Copyright © 2020 Kyle. All rights reserved. +// +import Foundation +import CoreBluetooth + +protocol GMFGRouterWifiDelegate: AnyObject { + func routerWifi(credentials: GMFGRouterWifiInfo) + func routerWifiStatus(status: GMFGRouterWifiStatus) +} + +struct GMFGRouterWifiInfo { + var ssid: String + var password: String +} + +enum GMFGRouterWifiStatus: String { + case disabled = "Disabled" + case notReady = "NotReady" +} + +class GMFGRouterWifiHandler: GMFGBLEHandlerProtocol { + + private let multicast = MulticastDelegate() + + public func setDelegate(delegates: [GMFGRouterWifiDelegate?]) { + self.multicast.add(delegates: delegates) + } + + public func reset() { + self.multicast.removeAll() + } + + public func fetchRouterWifiCredentials(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic) { + peripheral.readValue(for: characteristic) + } + + public func response(response: [String : Any]) { + guard let wifiSSID = response["SSID"] as? String, let wifiPwd = response["Password"] as? String else { return } + multicast.invokeForEachDelegate{ $0.routerWifi(credentials: GMFGRouterWifiInfo.init(ssid: wifiSSID, password: wifiPwd)) } + reset() + } + + public func deviceNotReady() { + multicast.invokeForEachDelegate{ $0.routerWifiStatus(status: .notReady) } + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGSpeedTestHandler.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGSpeedTestHandler.swift new file mode 100644 index 0000000..bb001bb --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/BLE Handlers/GMFGSpeedTestHandler.swift @@ -0,0 +1,141 @@ +// +// GMFGSpeedTestHandler.swift +// MVM5G +// +// Created by Jason Beck on 5/26/20. +// Copyright © 2020 Kyle. All rights reserved. +// +import CoreBluetooth + +protocol GMFGSpeedTestDelegate: AnyObject { + func didStartSpeedTest() + func didStopSpeedTest() + func speedTestStatus(status: GMFGSpeedTestUpdate) +} + +extension GMFGSpeedTestDelegate { + func didStartSpeedTest(){} + func didStopSpeedTest(){} +} + +private enum ResultKeys: String { + case result = "Result" + case speedTestServer = "SIP" + case speedTestServerID = "SID" + case routerIP = "RIP" + case downlink = "DL" + case uplink = "UL" + case latency = "Lat" + case jitter = "Jitt" + case progress = "Prog" + case errorMessage = "Error" +} + +enum Status: String { + case ready = "Ready" + case running = "Running" + case failed = "Failed" + case done = "Done" + case notReady = "NotReady" +} + +struct GMFGSpeedTestUpdate { + var result: Status + var speedTestServerIP: String? + var speedTestServerID: Int? + var routerIP: String? + var downlink: String? + var uplink: String? + var latency: String? + var jitter: Int? + var progress: Double? + var errorMessage: String? + var rawResults: [String: Any]? + + func getDictFromSelf() -> [String: Any]? { + let dataDict: [String: Any] = [ + ResultKeys.result.rawValue : result.rawValue, + ResultKeys.speedTestServer.rawValue : speedTestServerIP ?? GMFGConstant.empty, + ResultKeys.speedTestServerID.rawValue: speedTestServerID ?? -1, + ResultKeys.routerIP.rawValue : routerIP ?? GMFGConstant.empty, + ResultKeys.downlink.rawValue : downlink ?? GMFGConstant.empty, + ResultKeys.uplink.rawValue : uplink ?? GMFGConstant.empty, + ResultKeys.latency.rawValue : latency ?? GMFGConstant.empty, + ResultKeys.jitter.rawValue : jitter ?? -1, + ResultKeys.progress.rawValue : progress ?? 0.0, + ResultKeys.errorMessage.rawValue : errorMessage ?? GMFGConstant.empty + ] + return dataDict + } +} + +class GMFGSpeedTestHandler: GMFGBLEHandlerProtocol { + + private let multicast = MulticastDelegate() + public func setDelegate(delegates: [GMFGSpeedTestDelegate?]) { + self.multicast.add(delegates: delegates) + } + + public func reset() { + multicast.removeAll() + } + + //MARK:- Helper + private func rawKey(_ type: ResultKeys) -> String { return type.rawValue } + private func rawStatus(_ type: Status) -> String { return type.rawValue } + + public func start(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic) { + let requestValue = ["SpeedTest": "Start"] + guard let data = convertToData(requestValue) else { return } + let writeType: CBCharacteristicWriteType = characteristic.properties.contains(.writeWithoutResponse) ? .withoutResponse : .withResponse + peripheral.writeValue(data, for: characteristic, type: writeType) + multicast.invokeForEachDelegate{ $0.didStartSpeedTest() } + + //Notify process started having status = running + let responseObj = GMFGSpeedTestUpdate(result: .running) + multicast.invokeForEachDelegate{ $0.speedTestStatus(status: responseObj) } + } + + public func stop(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic) { + let requestValue = ["SpeedTest": "Stop"] + guard let data = convertToData(requestValue) else { return } + let writeType: CBCharacteristicWriteType = characteristic.properties.contains(.writeWithoutResponse) ? .withoutResponse : .withResponse + peripheral.writeValue(data, for: characteristic, type: writeType) + multicast.invokeForEachDelegate{ $0.didStopSpeedTest() } + } + + public func update(_ peripheral: CBPeripheral, _ characteristic: CBCharacteristic) { + peripheral.readValue(for: characteristic) + } + + //It is possible to try and call the device before it's done loading + public func deviceNotReady() { + let responseObj = GMFGSpeedTestUpdate(result: .notReady) + multicast.invokeForEachDelegate{ $0.speedTestStatus(status: responseObj) } + } + + public func response(response: [String : Any]) { + guard let statusString = response[rawKey(.result)] as? String, + let status = Status(rawValue: statusString) else { + return + } + + let responseObj = responseFormat(status: status, response: response) + if responseObj.result == .done || responseObj.result == .failed { + multicast.invokeForEachDelegate{ $0.didStopSpeedTest() } + } + multicast.invokeForEachDelegate{ $0.speedTestStatus(status: responseObj) } + } + + func responseFormat(status: Status, response: [String: Any]) -> GMFGSpeedTestUpdate { + return GMFGSpeedTestUpdate(result: status, speedTestServerIP: response[rawKey(.speedTestServer)] as? String, + speedTestServerID: response[rawKey(.speedTestServerID)] as? Int, + routerIP: response[rawKey(.routerIP)] as? String, + downlink: response[rawKey(.downlink)] as? String, + uplink: response[rawKey(.uplink)] as? String, + latency: response[rawKey(.latency)] as? String, + jitter: response[rawKey(.jitter)] as? Int, + progress: (response[rawKey(.progress)] as? NSString)?.doubleValue, + errorMessage: response[rawKey(.errorMessage)] as? String, rawResults: response) + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGBluetoothPair.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGBluetoothPair.swift new file mode 100644 index 0000000..5bc9c00 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGBluetoothPair.swift @@ -0,0 +1,509 @@ +// +// GMFGBluetoothPair.swift +// MobileFirstFramework +// +// Created by Chowdhury, Shohrab on 4/20/20. +// Copyright © 2020 Verizon Wireless. All rights reserved. +// + +import CoreBluetooth + +class GMFGBluetoothPair: MFFGHSBluetoothPair { + private static weak var ginstance: GMFGBluetoothPair? + static var shared: GMFGBluetoothPair { + get { + if let ginstance = ginstance { + return ginstance + } else { + let newInstance = GMFGBluetoothPair() + ginstance = newInstance + return newInstance + } + } + } + + override var bluetoothConfig: BluetoothConfigModel? { + didSet { + if (oldValue != bluetoothConfig) { // TODO: Needs deep check for config changes. + setupBluetoothScanner(showPowerAlert: true) + } + } + } + + override func shutdown() { + super.shutdown() + GMFGBluetoothPair.ginstance = nil + } + + typealias ResponseHandler = ([String : Any]) -> Void + private var speedTestHandler = GMFGSpeedTestHandler() + private var internetAccessHandler = GMFGPublicInternetAccessHandler() + private var routerWifiHandler = GMFGRouterWifiHandler() + private var operationModeHandler = GMFGOperationHandler() + private var fotaHandler = GMFGFotaHandler() + private var fiveGSignalHandler = GMFG5GSignalHandler() + private var fiveGCBandSignalHandler = GMFG5GCBandSignalHandler() + private var responseHandlers: [CBUUID: ResponseHandler] = [:] + + private var max5GSignal: Double? + var max5GSignalSeen: Double? { + set { + if (max5GSignal == nil && newValue != nil) || (max5GSignal != nil && newValue == nil) { + max5GSignal = newValue + return + } + guard let value = newValue, let maxSignal = max5GSignal else { return } + if value > maxSignal { max5GSignal = newValue } + } + get { return max5GSignal } + } + + private var min5GSignal: Double? + var min5GSignalSeen: Double? { + set { + if (min5GSignal == nil && newValue != nil) || (min5GSignal != nil && newValue == nil) { + min5GSignal = newValue + return + } + guard let value = newValue, let minSignal = min5GSignal else { return } + if value < minSignal { min5GSignal = newValue } + } + get { return min5GSignal } + } + var samples5GSignalSeen = 0 + + public class func isValidAdvertisedData(_ testData: String) -> Bool { + return testData.contains(GMFGConstant.BLE.advertisePrefix) && testData.count == 20 + } + + // MARK:- Notification Functions + func updateAllNotifyValue(_ shouldNotify: Bool) { + if let _peripheral = peripheral, let services = _peripheral.services { + for service in services { + if let characteristics = service.characteristics { + for characteristic in characteristics { + if characteristic.isNotifying { + _peripheral.setNotifyValue(shouldNotify, for: characteristic) + } + } + } + } + } + } + + @discardableResult func sendNotifications(for characteristic: CharacteristicType, shouldNotify: Bool) -> Bool { + if let _peripheral = peripheral, let _characteristic = getCharacteristic(peripheral: _peripheral, fromType: characteristic) { + _peripheral.setNotifyValue(shouldNotify, for: _characteristic) + return true + } + return false + } + + + /// This will ensure that the handlers are registered only once + /// All this logic was originally happening in a case statement + /// therefore each comparison / lookup was happening everytime + /// the callback occurred. This is registering a characteric to a handler + /// which happens 1 time and used then with a dictionary lookup for the handler. + /// - Parameter peripheral: comes in from delegate callback + func ensureResponseHandlers(peripheral: CBPeripheral) { + //compare local peripheral vs one passed in + //make sure bluetoothConfig is set + //make sure there are no responseHandlers registered + guard let localPeripheral = self.peripheral, + peripheral.identifier == localPeripheral.identifier, + responseHandlers.isEmpty, + bluetoothConfig != nil else { return } + + // activation + if let uuid = getCharacteristic(peripheral: peripheral, fromType: .activation)?.uuid { + responseHandlers[uuid] = handleActivated(response:) + } + + // speedTest + if let uuid = getCharacteristic(peripheral: peripheral, fromType: .speedTest)?.uuid { + responseHandlers[uuid] = speedTestHandler.response(response:) + } + if let uuid = getCharacteristic(peripheral: peripheral, fromType: .speedTestResults)?.uuid { + responseHandlers[uuid] = speedTestHandler.response(response:) + } + + //internetAccess + if let uuid = getCharacteristic(peripheral: peripheral, fromType: .internetAccess)?.uuid { + responseHandlers[uuid] = internetAccessHandler.response(response:) + } + + //fota + if let uuid = getCharacteristic(peripheral: peripheral, fromType: .fotaCheck)?.uuid { + responseHandlers[uuid] = fotaHandler.response(response:) + } + if let uuid = getCharacteristic(peripheral: peripheral, fromType: .fotaStatus)?.uuid { + responseHandlers[uuid] = fotaHandler.response(response:) + } + + //routerWifi + if let uuid = getCharacteristic(peripheral: peripheral, fromType: .routerWiFi)?.uuid { + responseHandlers[uuid] = routerWifiHandler.response(response:) + } + + //fiveGCBandSignal + if let uuid = getCharacteristic(peripheral: peripheral, fromType: .fiveGCBandSignal)?.uuid { + responseHandlers[uuid] = fiveGCBandSignalHandler.response(response:) + } + + //fiveGSignal + if let uuid = getCharacteristic(peripheral: peripheral, fromType: .fiveGSignal)?.uuid { + func fiveGResponse(response: [String: Any]){ + updateFiveGStatistics(cpeResponse: response) + handleFiveGSignal(response: response) + fiveGSignalHandler.response(response: response) + } + responseHandlers[uuid] = fiveGResponse(response:) + } + } + + // MARK:- CBPeripheralDelegate + override public func centralManagerDidUpdateState(_ central: CBCentralManager) { + bluetoothDebugger?.bluetoothState = central.state + if central.state == .unauthorized { + delegate?.bluetoothPermissionsDenied() //User denied BLE + bluetoothDebugger?.isBluetoothPermissionDenied = true + } else if central.state == .poweredOff { + delegate?.bluetoothOff() //User turned off BLE + } + super.centralManagerDidUpdateState(central) + } + + override func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { + let cpeResponse = formatCharacteristicData(characteristic) + track("Message From CPE", additionalData: cpeResponse.merging(["CBUUID":characteristic.uuid.uuidString], uniquingKeysWith: { $1 })) + + //make sure responseHandlers are registered + ensureResponseHandlers(peripheral: peripheral) + + if let responseHandler = responseHandlers[characteristic.uuid] { + responseHandler(cpeResponse) + } + } + + override public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { + if let peripheralName = advertisementData[CBAdvertisementDataLocalNameKey] as? String, peripheralName.hasPrefix(GMFGConstant.BLE.advertisePrefix) { + track("Scanning found peripheral \(peripheralName) with RSSI \(RSSI).") + } else { + track("Scanning found peripheral, but not a VZ5G device") + } + super.centralManager(central, didDiscover: peripheral, advertisementData: advertisementData, rssi: RSSI) + } + + override public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { + track("Connected to \(String(describing: peripheral.name))") + super.centralManager(central, didConnect: peripheral) + } + + override public func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { + track("Disconnected from \(String(describing: peripheral.name))") + fiveGSignalHandler.deviceNotReady() + super.centralManager(central, didDisconnectPeripheral: peripheral, error: error) + } + + override public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { + if let error = error { + track("Discover Service Error: \(error.localizedDescription)") + } + super.peripheral(peripheral, didDiscoverServices: error) + } + + public func peripheral(_ peripheral: CBPeripheral, didReadRSSI RSSI: NSNumber, error: Error?) { + if let error = error { + track("Read error: \(error.localizedDescription) with RSSI: \(RSSI)") + } + } + + public func peripheral(_ peripheral: CBPeripheral, didOpen channel: CBL2CAPChannel?, error: Error?) { + if let error = error { + track("Error opening channel: \(error.localizedDescription)") + } + } + + public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) { + if let error = error { + track("Error writing to characteristic: \(String(describing: descriptor.characteristic?.uuid.uuidString)) holding value: \(String(describing: descriptor.value)) with error: \(error.localizedDescription)") + } + } + + public func peripheral(_ peripheral: CBPeripheral, didDiscoverIncludedServicesFor service: CBService, error: Error?) { + if let error = error { + track("Error in discovered device characteristics: \(error.localizedDescription)") + } + } + + override func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { + track(error?.localizedDescription ?? "Unknown", function: "Central Manager Failed Connection") + } + + override func scanForPeripherals(central: CBCentralManager) { + if !central.isScanning { + track("Starting scan for peripherals") + central.scanForPeripherals(withServices: nil, options: nil) + } else { + track("Already Scanning for peripherals") + } + } + + //5GSignal Characteristic + private func updateFiveGStatistics(cpeResponse: [String: Any]) { + if let fivegSignal = cpeResponse["5GSignal"] as? [String: Any] { + if let rssiValue = parseRSRPFromJSON(fiveGData: fivegSignal) { + max5GSignalSeen = rssiValue + min5GSignalSeen = rssiValue + samples5GSignalSeen = samples5GSignalSeen + 1 + } + } + } + + // MARK:- Speed Test Functions + + public func startSpeedTest(_ speedTestDelegate: GMFGSpeedTestDelegate, withNotifications: Bool) { + speedTestHandler.setDelegate(delegates: [speedTestDelegate, bluetoothDebugger]) + if let _peripheral = peripheral, let _cTriggerSpeedTest = getCharacteristic(peripheral: _peripheral, fromType: .speedTest) { + if withNotifications { + updateAllNotifyValue(false) //Turn them all off to reduce traffic. + _ = sendNotifications(for: .speedTestResults, shouldNotify: true) + } + track("Starting Speed Test") + speedTestHandler.start(_peripheral, _cTriggerSpeedTest) + } else { + let errorMessage = peripheral == nil ? GMFGConstant.BLE.LogMsg.peripheralError : GMFGConstant.BLE.LogMsg.characteristicError + track(errorMessage) + speedTestHandler.deviceNotReady() + } + } + + public func stopSpeedTest() { + speedTestHandler.reset() + if let _peripheral = peripheral, let _cTriggerSpeedTest = getCharacteristic(peripheral: _peripheral, fromType: .speedTest) { + _ = sendNotifications(for: .speedTestResults, shouldNotify: false) + track("Speed Test Stopping") + speedTestHandler.stop(_peripheral, _cTriggerSpeedTest) + } else { + let errorMessage = peripheral == nil ? GMFGConstant.BLE.LogMsg.peripheralError : GMFGConstant.BLE.LogMsg.characteristicError + track(errorMessage) + speedTestHandler.deviceNotReady() + } + } + + //MARK:- Router WIFI Info + public func fetchRouterWifi(delegate: GMFGRouterWifiDelegate) { + routerWifiHandler.setDelegate(delegates: [delegate, bluetoothDebugger]) + if let _peripheral = peripheral, let _cRouterWifi = getCharacteristic(peripheral: _peripheral, fromType: .routerWiFi) { + track("Started fetching Wifi info") + routerWifiHandler.fetchRouterWifiCredentials(_peripheral, _cRouterWifi) + } else { + let errorMessage = peripheral == nil ? GMFGConstant.BLE.LogMsg.peripheralError : GMFGConstant.BLE.LogMsg.characteristicError + track(errorMessage) + routerWifiHandler.deviceNotReady() + } + } + + // MARK:- Public Internet Functions + public func checkInternetStatus(delegate: GMFGPublicInternetAccessDelegate) { + internetAccessHandler.setDelegate(delegates: [delegate, bluetoothDebugger]) + if let _peripheral = peripheral, let _cPublicInternetAccess = getCharacteristic(peripheral: _peripheral, fromType: .internetAccess) { + track("Getting Internet Access") + internetAccessHandler.getPublicInternetAccess(_peripheral, _cPublicInternetAccess) + } else { + let errorMessage = peripheral == nil ? GMFGConstant.BLE.LogMsg.peripheralError : GMFGConstant.BLE.LogMsg.characteristicError + track(errorMessage) + internetAccessHandler.deviceNotReady() + } + } + + // MARK:- Operational Mode Functions + @discardableResult + public func setOpMode(mode: GMFGOperationMode, disableBLE: Bool = false) -> Bool { + let additionalLogData: [String: Any] = ["Mode": mode.rawValue, "DisableBLE": disableBLE] + if let _peripheral = peripheral, let _cOperationMode = getCharacteristic(peripheral: _peripheral, fromType: .operationMode) { + track("Disabling BLE", additionalData: additionalLogData) + return operationModeHandler.setOpMode(_peripheral, _cOperationMode, mode: mode, disableBLE: disableBLE) + } + let errorMessage = peripheral == nil ? GMFGConstant.BLE.LogMsg.peripheralError : GMFGConstant.BLE.LogMsg.characteristicError + track(errorMessage, additionalData: additionalLogData) + return false + } + + // MARK:- FOTA Functions + public func startGetFirmwareVersion(delegate: GMFGFotaDelegate) { + bluetoothDebugger?.isCheckingFirmware = true + fotaHandler.setDelegate(delegates: [delegate, bluetoothDebugger]) + if let _peripheral = peripheral, let _cFota = getCharacteristic(peripheral: _peripheral, fromType: .fotaStatus) { + track("Started getting firmware version from device") + fotaHandler.requestVersion(_peripheral, _cFota) + } else { + let errorMessage = peripheral == nil ? GMFGConstant.BLE.LogMsg.peripheralError : GMFGConstant.BLE.LogMsg.characteristicError + track(errorMessage) + fotaHandler.deviceNotReady() + } + } + + public func stopGetFirmwareVersion() { + bluetoothDebugger?.isCheckingFirmware = false + fotaHandler.reset() + track("Stopped getting firmware version") + } + + public func startFirmwareUpgrade(delegate: GMFGFotaDelegate, useNotifications: Bool) { + fotaHandler.setDelegate(delegates: [delegate, bluetoothDebugger]) + if let _peripheral = peripheral, let _cFota = getCharacteristic(peripheral: _peripheral, fromType: .fotaCheck), getCharacteristic(peripheral: _peripheral,fromType: .fotaStatus) != nil { + track("Starting firmware update") + updateAllNotifyValue(false) + _ = self.sendNotifications(for: .fotaStatus, shouldNotify: useNotifications) + fotaHandler.startUpdate(_peripheral, _cFota) + } else { + let errorMessage = peripheral == nil ? GMFGConstant.BLE.LogMsg.peripheralError : GMFGConstant.BLE.LogMsg.characteristicError + track(errorMessage) + fotaHandler.deviceNotReady() + } + } + + public func readFOTAStatus(delegate: GMFGFotaDelegate) { + fotaHandler.setDelegate(delegates: [delegate, bluetoothDebugger]) + if let _peripheral = peripheral, let _cFota = getCharacteristic(peripheral: _peripheral, fromType: .fotaStatus) { + track("Checking FOTA status") + fotaHandler.requestVersion(_peripheral, _cFota) + } else { + let errorMessage = peripheral == nil ? GMFGConstant.BLE.LogMsg.peripheralError : GMFGConstant.BLE.LogMsg.characteristicError + track(errorMessage) + fotaHandler.deviceNotReady() + } + } + + public func stopFirmwareUpgrade() { + fotaHandler.reset() + track("Stopped firmware update") + } + + public func isFotaInProgress() -> Bool { + if let params = GMFGStorageManager.retrieve(key: GMFGConstant.fiveGParams), let inProgress = params[GMFGConstant.BLE.fotaInProgress] as? Bool { + return inProgress + } + return false + } + + public func setFotaInProgress(_ inProgress: Bool) { + GMFGStorageManager.addEntries(dictionary: [GMFGConstant.BLE.fotaInProgress: inProgress]) + } + + public func getOldFirmwareVersion() -> String? { + if let params = GMFGStorageManager.retrieve(key: GMFGConstant.fiveGParams), let oldFirmware = params[GMFGConstant.BLE.firmwareVersion] as? String { + return oldFirmware + } + return nil + } + + public func isFirmwareNewer(_ newFirmware: String) -> Bool { + if getOldFirmwareVersion() == newFirmware { + return false + } + return true + } + + // MARK:- 5G Signal Handler + public func start5GSignalUpdates(_ delegate: GMFG5GSignalDelegate, useNotifications: Bool) { + fiveGSignalHandler.setDelegate(delegates: [delegate, bluetoothDebugger]) + if let _peripheral = peripheral, let _c5GSignal = getCharacteristic(peripheral: _peripheral, fromType: .fiveGSignal) { + track("Getting 5G Signal") + sendNotifications(for: .fiveGSignal, shouldNotify: useNotifications) + fiveGSignalHandler.get5GSignal(_peripheral, _c5GSignal) + } else { + let errorMessage = peripheral == nil ? GMFGConstant.BLE.LogMsg.peripheralError : GMFGConstant.BLE.LogMsg.characteristicError + track(errorMessage) + fiveGSignalHandler.deviceNotReady() + } + } + + public func stop5GSignalUpdates() { + fiveGSignalHandler.reset() + track("Stop getting 5G Signal") + updateAllNotifyValue(false) + } + + // MARK:- 5G CBand Signal Handler + public func start5GCBandSignalUpdates(_ delegate: GMFG5GCBandSignalDelegate, useNotifications: Bool) { + fiveGCBandSignalHandler.setDelegate(delegates: [delegate, bluetoothDebugger]) + if let _peripheral = peripheral, let _c5GSignal = getCharacteristic(peripheral: _peripheral, fromType: .fiveGCBandSignal) { + track("Getting 5G C-Band Signal") + sendNotifications(for: .fiveGCBandSignal, shouldNotify: useNotifications) + fiveGCBandSignalHandler.get5GSignal(_peripheral, _c5GSignal) + } else { + let errorMessage = peripheral == nil ? GMFGConstant.BLE.LogMsg.peripheralError : GMFGConstant.BLE.LogMsg.characteristicError + track(errorMessage) + fiveGCBandSignalHandler.deviceNotReady() + } + } + + public func stop5GCBandSignalUpdates() { + fiveGCBandSignalHandler.reset() + track("Stop getting 5G C-Band Signal") + updateAllNotifyValue(false) + } + + // MARK:- Utility Functions + override func parseRSRPFromJSON(fiveGData: [String: Any]) -> Double? { + return (fiveGData["RSRP"] as? NSString)?.doubleValue ?? nil + } + + // MARK:- BluetoothConfigModel Functions + override func getBleAdvertiseName() -> String { + let remoteAdvData = bluetoothConfig?.advertisedData ?? "" + if GMFGBluetoothPair.isValidAdvertisedData(remoteAdvData) { + GMFGStorageManager.addEntries(dictionary: [GMFGConstant.BLE.advertisedData: remoteAdvData]) + return remoteAdvData + } else if let localData = GMFGStorageManager.retrieve(key: GMFGConstant.fiveGParams), let localAdvData = localData[GMFGConstant.BLE.advertisedData] as? String { + track(GMFGConstant.BLE.LogMsg.cachedAdvertised) + return localAdvData + } + return remoteAdvData + } + + func getCharacteristic(peripheral: CBPeripheral, fromType: CharacteristicType) -> CBCharacteristic? { + guard let service = bluetoothConfig?.services.first else { + track("SERVICE was empty in the BLE Module JSON") + return nil + } + + guard let expectedCharacteristicUUID = service.characteristics.first(where: {$0.name == fromType})?.uuid else { + track("No characteristic found for key \(fromType.rawValue) in the BLE Module JSON") + return nil + } + + guard let expectedServiceUUID = getSeviceUUIDs().first else { + track("No services found in the BLE Module JSON") + return nil + } + + guard peripheral.state == .connected else { + track("Tried to get a characteristic from the peripheral, but it is still in state: \(peripheral.state.rawValue)") + return nil + } + + guard let services = peripheral.services else { + return nil + } + + if services.count != 1 { + track("Found \(services.count) services!") + } + + guard let service = services.first(where: {$0.uuid == expectedServiceUUID}) else { + track("Could not find service: \(expectedServiceUUID) on device.") + return nil + } + + guard let characteristic = service.characteristics?.first(where: {$0.uuid == expectedCharacteristicUUID}) else { + track("Could not find characteristic: \(expectedCharacteristicUUID) on device.") + return nil + } + return characteristic + } + +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGConstant.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGConstant.swift new file mode 100644 index 0000000..01b5377 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGConstant.swift @@ -0,0 +1,87 @@ +// +// GMFGConstant.swift +// MVM5G +// +// Created by Shohrab Chowdhury on 6/23/20. +// Copyright © 2020 Kyle. All rights reserved. +// + +struct GMFGConstant { + struct BLE { + static let pin = "pin" + static let advertisedData = "advertisedData" + static let fiveGCurrentSignal = "currentSignal" + static let fiveGHighSignal = "highSignal" + static let fiveGLowSignal = "lowSignal" + static let firmwareVersion = "firmwareVersion" + static let fotaInProgress = "fotaInProgress" + static let fotaStatus = "FOTAStatus" + static let cpeBLEOperation = "CPEBLEOperation" + static let advertisePrefix = "VZ5G_RECEIVER_" + static let routerWifiSSID = "routerWifiSSID" + static let routerWifiPwd = "routerWifiPwd" + struct LogMsg { + static let startingBLE = "Starting BLE Connection" + static let leavingPage = "Disconnecting BT because user is leaving page" + static let noAdvertisedName = "No Advertised Name" + static let attemptInstallationMode = "Attempting to switch to Installation mode" + static let attemptOperationalMode = "Attempting to switch to Operational mode" + static let internetNotEnabled = "CPE Internet Not Enabled. Starting speed test anyway..." + static let attemptDisableBleOpMode = "Attempting to disable BLE and set Operational mode" + static let fotaGotVersion = "Got version from CPE" + static let fotaUnexpectedCPEStatus = "Unexpected FOTA status returned from CPE" + static let cachedAdvertised = "Using local cached advertising name" + static let peripheralError = "Peripheral not found" + static let characteristicError = "Characteristic not found" + static let unknownCPEResponse = "Unknown response from CPE" + static let bleModuleMissing = "Ble Module missing" + static let bleDecodeError = "Ble Module decocde error" + } + } + struct AR { + static let capable = "arCapable" + static let capableStatusReq = "arCapableStatusRequired" + static let deviceLocation = "deviceLocation" + static let overlayShown = "AROverlayShown" + static let infoAccessibilityLabel = "Information icon button" + } + struct Map { + static let fivegSignalExposures = "fivegSignalExposures" + static let antennaList = "antennasList" + static let antennaLocation = "antennaLocation" + static let selectedAntennaIndex = "selectedAntennaIndex" + static let maxZoomLevel: Float = 18.0 + } + struct Test { + static let hardcodeToggle = "isHardcode" + static let CPEToggle = "isCPESupported" + static let fiveGSignalToggle = "is5GSignalEnabled" + static let forceBCS = "isBCSForce" + static let visualDebugger = "isVisualDebugger" + } + struct LogMsg { + static let invalidTimer = "Timer is invalid" + } + + struct TimeTracker { + static let details = "GMFGTimeTrackingDetails" + } + + struct Glassbox { + static let fivegSetup = "5GSetup" + } + static let empty = "" + static let index = "index" + static let imei = "imei" + static let extenderMacId = "extenderMacId" + static let overlayShown = "OverlayShown" + static let fiveGParams = "fivegParams" + static let orderNumber = "orderNumber" + static let functionName = "functionName" + static let birthCertificate = "BirthCertificate" + static let feedbackShown = "isFeedbackShown" + static let feedback = "feedback" + static let clearCache = "clearCache" + static let blinkingColor = "blinkingColor" + static let blinkSec = "blinkSec" +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGLocationManager.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGLocationManager.swift new file mode 100644 index 0000000..10a0acc --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGLocationManager.swift @@ -0,0 +1,99 @@ +// +// GMFGLocationManager.swift +// MVM5G +// +// Created by Muthulingam, Muthuraj on 03/06/20. +// Copyright © 2020 Kyle. All rights reserved. +// + +import Foundation +import CoreLocation + +protocol GMFGLocationManagerDelegate: AnyObject { + func locationManager(_ manager: GMFGLocationManager, didUpdateLocations locations: [CLLocation]) + func locationManager(_ manager: GMFGLocationManager, didHeadUpdate newHeading: CLHeading) + func locationManager(_ manager: GMFGLocationManager, didUpdateLocationPermissions isAuthorized: Bool) +} + +final class GMFGLocationManager: NSObject { + + // MARK: - Public Properties + weak var delegate: GMFGLocationManagerDelegate? + static let shared = GMFGLocationManager() + var locationServicesEnabled: Bool { + if CLLocationManager.locationServicesEnabled() { + switch CLLocationManager.authorizationStatus() { + case .notDetermined, .restricted, .denied: + return false + case .authorizedAlways, .authorizedWhenInUse: + return true + @unknown default: + return false + } + } else { + return false + } + } + + /// Fetches Current Location if available + private(set) var currentLocation: CLLocation? + + // MARK: - Private Properties + private lazy var locationManager = CLLocationManager() + + /// to avoid called to create instance + private override init() { + super.init() + } + + // MARK: - Public Helpers + func prepareLocationManager() { + locationManager.delegate = self + locationManager.desiredAccuracy = kCLLocationAccuracyBest + locationManager.headingFilter = kCLHeadingFilterNone + locationManager.requestWhenInUseAuthorization() + locationManager.startUpdatingLocation() + locationManager.startUpdatingHeading() + } + + func startLocationMonitoring() { + locationManager.startUpdatingHeading() + locationManager.startUpdatingLocation() + } + + func stopLocationMonitoring() { + locationManager.stopUpdatingHeading() + locationManager.stopUpdatingLocation() + } +} + +extension GMFGLocationManager: CLLocationManagerDelegate { + + func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { + switch status { + case .authorizedAlways, .authorizedWhenInUse: + locationManager.startUpdatingLocation() + locationManager.startUpdatingHeading() + case .denied, .notDetermined, .restricted: + delegate?.locationManager(self, didUpdateLocationPermissions: false) + /// Resets the location + currentLocation = nil + @unknown default: currentLocation = nil + } + } + + func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + if let currentLocation = locations.last { + self.currentLocation = currentLocation + } + delegate?.locationManager(self, didUpdateLocations: locations) + } + + func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading) { + delegate?.locationManager(self, didHeadUpdate: newHeading) + } +} + +extension CLLocation { + var array: [Double] { return [coordinate.latitude, coordinate.longitude] } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGMapARViewControllerUtilities.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGMapARViewControllerUtilities.swift new file mode 100644 index 0000000..60a9938 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGMapARViewControllerUtilities.swift @@ -0,0 +1,123 @@ +// +// GMFGMapARViewControllerUtilities.swift +// MVM5G +// +// Created by Muthulingam, Muthuraj on 01/06/20. +// Copyright © 2020 Kyle. All rights reserved. +// + +import Foundation +import SceneKit + +public extension CLLocation { + func bearingToLocationRadian(_ destinationLocation: CLLocation) -> CGFloat { + + let lat1 = self.coordinate.latitude.degreesToRadians + let lon1 = self.coordinate.longitude.degreesToRadians + + let lat2 = destinationLocation.coordinate.latitude.degreesToRadians + let lon2 = destinationLocation.coordinate.longitude.degreesToRadians + + let dLon = lon2 - lon1 + + let y = sin(dLon) * cos(lat2) + let x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLon) + let radiansBearing = atan2(y, x) + + return CGFloat(radiansBearing) + } + + func bearingToLocationDegrees(destinationLocation: CLLocation) -> CGFloat { + return bearingToLocationRadian(destinationLocation).radiansToDegrees + } + + func bearingAngle(to location: CLLocation) -> Double { + + let latA = coordinate.latitude.degreesToRadians + let lonA = coordinate.longitude.degreesToRadians + + let latB = location.coordinate.latitude.degreesToRadians + let lonB = location.coordinate.longitude.degreesToRadians + + let longitudeDiff = lonB - lonA + + let y = sin(longitudeDiff) * cos(latB) + let x = cos(latA) * sin(latB) - sin(latA) * cos(latB) * cos(longitudeDiff) + + return atan2(y, x) + } +} + +public extension CGFloat { + var degreesToRadians: CGFloat { return self * .pi / 180 } + var radiansToDegrees: CGFloat { return self * 180 / .pi } +} + +private extension Double { + var degreesToRadians: Double { return Double(CGFloat(self).degreesToRadians) } + var radiansToDegrees: Double { return Double(CGFloat(self).radiansToDegrees) } +} + +public final class GMFGMapARViewControllerUtilities { + + private init() {} + + static func positionFromTransform(_ transform: matrix_float4x4, reversed: Bool = false) -> SCNVector3 { + + if reversed { + return SCNVector3Make(-transform.columns.3.x, -transform.columns.3.y, -transform.columns.3.z) + } + + return SCNVector3Make(transform.columns.3.x, transform.columns.3.y, transform.columns.3.z) + } + + static func position(model modelNode: SCNNode, byDistance distance: Float, fromUserLocation userLocation: CLLocation, toLocation location: CLLocation) { + modelNode.position = translateNodeFrom(userLocation: userLocation, toLocation: location, distance: distance) + modelNode.scale = scaleNode(distance, location: location) + } + + private static func translateNodeFrom(userLocation: CLLocation, toLocation location: CLLocation, distance: Float) -> SCNVector3 { + let locationTransform = transformMatrix(matrix: matrix_identity_float4x4, originLocation: userLocation, targetLocation: location, distance: distance) + return positionFromTransform(locationTransform) + } + + private static func scaleNode(_ distance: Float, location: CLLocation) -> SCNVector3 { + let scale = max( min( Float(1000/distance), 1.5 ), 3 ) + return SCNVector3(x: scale, y: scale, z: scale) + } + + private static func transformMatrix(matrix: simd_float4x4, originLocation: CLLocation, targetLocation: CLLocation, distance: Float) -> simd_float4x4 { + let bearingAngle = originLocation.bearingAngle(to: targetLocation) + let rotationMatrix = rotateAroundY(matrix_identity_float4x4, Float(bearingAngle)) + + var position = vector_float4(0.0, 0.0, -distance, 0.0) + + if distance > 1000.0 { + position = vector_float4(0.0, 0.0, -25.0, 0.0) + } + + let translationMatrix = getTranslationMatrix(matrix_identity_float4x4, position) + + let transformMatrix = simd_mul(rotationMatrix, translationMatrix) + + return simd_mul(matrix, transformMatrix) + } + + private static func getTranslationMatrix(_ matrix: simd_float4x4, _ translation : vector_float4) -> simd_float4x4 { + var matrix = matrix + matrix.columns.3 = translation + return matrix + } + + private static func rotateAroundY(_ matrix: simd_float4x4, _ degrees: Float) -> simd_float4x4 { + var matrix = matrix + + matrix.columns.0.x = cos(degrees) + matrix.columns.0.z = -sin(degrees) + + matrix.columns.2.x = sin(degrees) + matrix.columns.2.z = cos(degrees) + + return matrix.inverse + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGSelfInstallTimeTracker.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGSelfInstallTimeTracker.swift new file mode 100644 index 0000000..19eefd2 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGSelfInstallTimeTracker.swift @@ -0,0 +1,179 @@ +// +// GMFGSelfInstallTimeTracker.swift +// MVM5G +// +// Created by Gujuluva Santharam, Ajai Prabhu on 05/02/21. +// Copyright © 2021 Kyle. All rights reserved. +// + +import Foundation + +enum MFFGHSPageDisplayEvent { + case show + case hide(_ elapse: TimeDuration) +} + +final class GMFGSelfInstallTimeTracker { + // MARK: - Private Propeties + /* Elapsed will give us the actual time spent by user in the self setup */ + private(set) var elapsed: Double = GMFGStorageManager.retrieve(key: GMFGConstant.TimeTracker.details)?["elapsedTime"] as? Double ?? 0 + private var sessionStartTime: Double = Date().timeIntervalSince1970 // this will make sure not to calculate with zero + private var sessionEndTime: Double = 0 + private var pageStartTime: Double = 0 + private var didWentBgState: Bool = false // to track transion of bg/fg states to send analytics + private var foregroundObserver: NSObjectProtocol? + private var currentSessionInterval: Double = 0 + // MARK: - Public Properties + static var shared: GMFGSelfInstallTimeTracker = GMFGSelfInstallTimeTracker() + + // MARK: - Initialisers + private init() { + if #available(iOS 13.0, *) { + NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: UIScene.willDeactivateNotification, object: nil) + } else { + NotificationCenter.default.addObserver(self, selector: #selector(willResignActive), name: UIApplication.willResignActiveNotification, object: nil) + } + + foregroundObserver = NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main) { [weak self] notification in + self?.beginSession() + } + } + + deinit { + if #available(iOS 13.0, *) { + NotificationCenter.default.removeObserver(self, name: UIScene.willDeactivateNotification, object: nil) + } else { + NotificationCenter.default.removeObserver(self, name: UIApplication.willResignActiveNotification, object: nil) + } + NotificationCenter.default.removeObserver(foregroundObserver as Any) + } + + // MARK: - Public Helpers + + func beginSession() { + sessionStartTime = currentTime() + } + + func endSession() { + let newSessionEndTime = currentTime() + let previousSessionTime = elapsed + if sessionEndTime != 0 { + elapsed += newSessionEndTime - sessionEndTime + } else { + elapsed += newSessionEndTime - sessionStartTime + } + currentSessionInterval = elapsed - previousSessionTime + sessionEndTime = newSessionEndTime + + func handleLargeTiming() { + // NOTE: Fallback approach to avoid sending unimaginable time spent all sessions to server & Reset time + let currentAllSpendSessionTimeInMonths = elapsed.month + if currentAllSpendSessionTimeInMonths > 1 { + trackGemini(value: ["LASTHappen": true, + "LASTSReceived" : elapsed.millisecond, + "LASTSStartTime": sessionStartTime, + "LASTSEndTime": sessionEndTime, + "LASTSCurrentTime": newSessionEndTime], + logType: .UI) + elapsed = previousSessionTime + sessionStartTime = currentTime() + sessionEndTime = currentTime() + } + } + + handleLargeTiming() + GMFGStorageManager.addEntries(dictionary: ["elapsedTime": elapsed], key: GMFGConstant.TimeTracker.details) + } + + // MARK:- Page Tracking + + func trackPageAppear(with pageJSON: [String: Any]?) { + pageStartTime = currentTime() + } + + func trackPageDisappear(with pageJSON: [String: Any]?) { + let timeDuration = TimeDuration(start: pageStartTime, end: currentTime()) + endSession() + logPageDisplayEvent(of: .hide(timeDuration), with: pageJSON) + if didWentBgState { didWentBgState = false } // reset back to default state + } +} + +struct TimeDuration { + let start: TimeInterval + let end: TimeInterval +} + +extension TimeDuration { + var duration: TimeInterval { + return end - start + } +} + +// MARK: - Private Helpers +private extension GMFGSelfInstallTimeTracker { + + // MARK: - Private Helpers + func currentTime() -> Double { + return Date().timeIntervalSince1970 + } + + @objc + func willResignActive(_ notification: Notification) { + endSession() + didWentBgState = true // reset once logged + } + + func preparePageDisappear(with pageDuration: TimeDuration) -> [String: Any] { + var params: [String: Any] = fetchDefaultAdditionalInfo() + params["eventName"] = "stop" + params["startTime"] = Int(pageDuration.start.millisecond) + params["endTime"] = Int(pageDuration.end.millisecond) + params["timeSpendPage"] = Int(pageDuration.duration.millisecond) + params["didEnterBg"] = didWentBgState + return params + } + + func preparePageAppear() -> [String: Any] { + var params: [String: Any] = fetchDefaultAdditionalInfo() + params["eventName"] = "start" + params["timeSpendPage"] = 0 + return params + } + + func fetchDefaultAdditionalInfo() -> [String: Any] { + return [ "eventType": "pageDisplay", + "timeSpentAllSessions": Int(GMFGSelfInstallTimeTracker.shared.elapsed.millisecond), // to convert seconds to milliseconds + "timeSpentSession": Int(currentSessionInterval.millisecond) + ] as [String : Any] + } +} + +extension TimeInterval { + var hour: Int { + Int(self/3600) + } + var millisecond: Int { + Int(self*1000) + } + var day: Int { + hour/24 + } + var month: Int { + day/30 + } +} + +// MARK: - Analytics Protocol Conformance +extension GMFGSelfInstallTimeTracker: MFFGHSAnalyticsProtocol { + func logPageDisplayEvent(of type: MFFGHSPageDisplayEvent, with pageJson: [String: Any]?) { + let additionalData: [String: Any] + switch type { + case .show: + additionalData = preparePageAppear() + case let .hide(elapsed): + additionalData = preparePageDisappear(with: elapsed) + } + trackPage(data: pageJson, additionalData: additionalData) + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGStorageManager.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGStorageManager.swift new file mode 100644 index 0000000..e71946d --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGStorageManager.swift @@ -0,0 +1,50 @@ +// +// GMFGRequestHandler.swift +// MVM5G +// +// Created by Gujuluva Santharam, Ajai Prabhu on 07/07/20. +// Copyright © 2020 Kyle. All rights reserved. +// + +import UIKit + +class GMFGStorageManager { + + //MARK - user default base methods + class func store(dictionary: [AnyHashable : Any],for key:String) { + // MFUtility.save(dictionary, toUserDefaultsForKey: key) + } + + class func retrieve(key: String) -> [String: Any]? { + return UserDefaults.standard.dictionary(forKey: key) + } + + //Use this to append entries from dictionary to existing one + class func addEntries(dictionary: [String : Any], key: String = GMFGConstant.fiveGParams) { + let storedDict = GMFGStorageManager.retrieve(key: key) ?? [:] + let appendedDict = storedDict.merging(dictionary) { $1 } + GMFGStorageManager.store(dictionary: appendedDict, for: key) + } + + class func extraRequestParams() -> [String: Any] { + var storedDict = GMFGStorageManager.retrieve(key: GMFGConstant.fiveGParams) ?? [:] + if let currentLocation = GMFGLocationManager.shared.currentLocation { + storedDict[GMFGConstant.AR.deviceLocation] = currentLocation.array + } + return GMFGTestScreenData.shared.isHardcodedValues ? GMFGTestScreenData.shared.hardcodeDict : storedDict + } + + //MARK: - methods to remove from user defaults + class func remove(key: String) { + if var storedDict = GMFGStorageManager.retrieve(key: GMFGConstant.fiveGParams) { + storedDict.removeValue(forKey: key) + GMFGStorageManager.store(dictionary: storedDict, for: GMFGConstant.fiveGParams) + } + } + + class func removeAll() { + UserDefaults.standard.removeObject(forKey: GMFGConstant.fiveGParams) + UserDefaults.standard.removeObject(forKey: "selfInstallNextStep") + } +} + diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGTestScreen.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGTestScreen.swift new file mode 100644 index 0000000..c7028f2 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGTestScreen.swift @@ -0,0 +1,228 @@ +// +// GMFGTestScreen.swift +// MVM5G +// +// Created by Shohrab Chowdhury on 7/15/20. +// Copyright © 2020 Kyle. All rights reserved. +// + +import Foundation + +class GMFGTestScreen: TopLabelsAndBottomButtonsViewController { + + private var deviceLocationLatTextField, deviceLocationLongTextField, advertisedDataTextField, pinTextField, orderNumberTextField, imeiTextField, currentRssiTextField, highRssiTextField, lowRssiTextField, cpeVersionNumberTextField: MFTextField? + + private var isARCapableToggle, isCPESupportedToggle, isHardcodeToggle, isEnable5GSignalToggle, isBCSForce: UISwitch? + + var toggleval = [String: Bool]() + + + override public func newDataBuildScreen() { + super.newDataBuildScreen() + topLabelsView?.setHeadlineString("Gemini 5G Test Screen", messageString: "Click on load values button to load default values") + topLabelsView?.separatorView?.isHidden = false + } + + public override func buildViewsBetweenLabelsAndButtons() -> [UIView]? { + + var views:[UIView] = [] + + let locationText = GMFGTestScreenData.shared.deviceLocation + deviceLocationLatTextField = createTextField("Device Location Latitude", locationText.count > 0 && locationText[0] != GMFGConstant.empty ? locationText[0] : "29.72955022663853") + views.append(deviceLocationLatTextField ?? MFTextField()) + + deviceLocationLongTextField = createTextField("Device Location Longitude", locationText.count > 0 && locationText[0] != GMFGConstant.empty ? locationText[1] : "-95.370197614801185") + views.append(deviceLocationLongTextField ?? MFTextField()) + + let advertisedDataText = GMFGTestScreenData.shared.hardcodeDict.stringForkey(GMFGConstant.BLE.advertisedData) + advertisedDataTextField = createTextField("Advertised Data", advertisedDataText != GMFGConstant.empty ? advertisedDataText : "VZ5G_RECEIVER_203975") + views.append(advertisedDataTextField ?? MFTextField()) + + let pinText = GMFGTestScreenData.shared.hardcodeDict.stringForkey(GMFGConstant.BLE.pin) + pinTextField = createTextField("Pin", pinText != GMFGConstant.empty ? pinText : "00000") + views.append(pinTextField ?? MFTextField()) + + let orderNumberText = GMFGTestScreenData.shared.hardcodeDict.stringForkey(GMFGConstant.orderNumber) + orderNumberTextField = createTextField("Order Number", orderNumberText != GMFGConstant.empty ? orderNumberText : "3110116") + views.append(orderNumberTextField ?? MFTextField()) + + let imeiText = GMFGTestScreenData.shared.hardcodeDict.stringForkey(GMFGConstant.imei) + imeiTextField = createTextField("IMEI Number", imeiText != GMFGConstant.empty ? imeiText : "353450100203975") + views.append(imeiTextField ?? MFTextField()) + + let currentRSSIText = GMFGTestScreenData.shared.hardcodeDict.stringForkey(GMFGConstant.BLE.fiveGCurrentSignal) + currentRssiTextField = createTextField("Current Rssi", currentRSSIText != GMFGConstant.empty ? currentRSSIText : "-95") + views.append(currentRssiTextField ?? MFTextField()) + + let highRSSIText = GMFGTestScreenData.shared.hardcodeDict.stringForkey(GMFGConstant.BLE.fiveGHighSignal) + highRssiTextField = createTextField("High Rssi", highRSSIText != GMFGConstant.empty ? highRSSIText : "-98") + views.append(highRssiTextField ?? MFTextField()) + + let lowRSSIText = GMFGTestScreenData.shared.hardcodeDict.stringForkey(GMFGConstant.BLE.fiveGLowSignal) + lowRssiTextField = createTextField("Low Rssi", lowRSSIText != GMFGConstant.empty ? lowRSSIText : "-92") + views.append(lowRssiTextField ?? MFTextField()) + + let cpeVersionNumberText = GMFGTestScreenData.shared.cpeVersionNumber + cpeVersionNumberTextField = createTextField("CPE Version Number", cpeVersionNumberText != GMFGConstant.empty ? cpeVersionNumberText : "2.29.49.1") + views.append(cpeVersionNumberTextField ?? MFTextField()) + + let hardcodeToggleView = getSwitchView("Hardcoded Request", 1, GMFGConstant.Test.hardcodeToggle) + hardcodeToggleView.uiSwitch.isOn = GMFGTestScreenData.shared.isHardcodedValues + views.append(hardcodeToggleView.container) + + let arToggleView = getSwitchView("AR Capable", 2, GMFGConstant.AR.capable) + arToggleView.uiSwitch.isOn = GMFGTestScreenData.shared.isARCapable + views.append(arToggleView.container) + + let cpeToggleView = getSwitchView("CPE Simulation", 3, GMFGConstant.Test.CPEToggle) + cpeToggleView.uiSwitch.isOn = GMFGTestScreenData.shared.isCPESimulated + views.append(cpeToggleView.container) + + let fiveGSignalToggle = getSwitchView("Enable 5G Signal", 4, GMFGConstant.Test.fiveGSignalToggle) + fiveGSignalToggle.uiSwitch.isOn = GMFGTestScreenData.shared.isEnable5GSignal + views.append(fiveGSignalToggle.container) + + let fiveGBCSToggle = getSwitchView("Force BCS Wifi", 5, GMFGConstant.Test.forceBCS) + fiveGBCSToggle.uiSwitch.isOn = GMFGTestScreenData.shared.forceBCS + views.append(fiveGBCSToggle.container) + + let visualDebuggerToggle = getSwitchView("Visual Debugger", 6, GMFGConstant.Test.visualDebugger) + visualDebuggerToggle.uiSwitch.isOn = GMFGTestScreenData.shared.visualDebugger + views.append(visualDebuggerToggle.container) + + + /// Added Empty label to add extra space above button + let emptyLabel = Label() + emptyLabel.text = " " + views.append(emptyLabel) + + let doneButton = PrimaryButtonView(frame: .zero) + doneButton.primaryButton?.setTitle("Load Values", for: .normal) + doneButton.primaryButton?.addTarget(self, action: #selector(donePressed(sender:)), for: .touchUpInside) + doneButton.primaryButton?.isEnabled = true + views.append(doneButton) + + return views + } + + @objc func donePressed(sender: UIButton) { + setHardcodeValue() + dismiss(animated: false, completion: nil) + } + + @objc func switchValueDidChange(_ sender: UISwitch!) { + + switch sender.tag { + case 1: + toggleval[GMFGConstant.Test.hardcodeToggle] = sender.isOn + GMFGTestScreenData.shared.isHardcodedValues = toggleval.boolForKey(GMFGConstant.Test.hardcodeToggle) + case 2: + toggleval.updateValue(sender.isOn, forKey: GMFGConstant.AR.capable) + GMFGTestScreenData.shared.isARCapable = toggleval.boolForKey(GMFGConstant.AR.capable) + case 3: + toggleval.updateValue(sender.isOn, forKey: GMFGConstant.Test.CPEToggle) + GMFGTestScreenData.shared.isCPESimulated = toggleval.boolForKey(GMFGConstant.Test.CPEToggle) + case 4: + toggleval.updateValue(sender.isOn, forKey: GMFGConstant.Test.fiveGSignalToggle) + GMFGTestScreenData.shared.isEnable5GSignal = toggleval.boolForKey(GMFGConstant.Test.fiveGSignalToggle) + case 5: + toggleval.updateValue(sender.isOn, forKey: GMFGConstant.Test.forceBCS) + GMFGTestScreenData.shared.forceBCS = toggleval.boolForKey(GMFGConstant.Test.forceBCS) + case 6: + toggleval.updateValue(sender.isOn, forKey: GMFGConstant.Test.visualDebugger) + GMFGTestScreenData.shared.visualDebugger = toggleval.boolForKey(GMFGConstant.Test.visualDebugger) + default: + break + } + } + + override public func spaceAboveBetweenView() -> NSNumber? { + return 30 + } + + override func spaceAroundUIObject(_ object: Any?) -> UIEdgeInsets { + var insets = super.spaceAroundUIObject(object) + insets.top = PaddingOne + return insets + } + + private func createTextField(_ formLabel: String, _ text: String) -> MFTextField? { + let textField = MFTextField(bothDelegates: self) + textField?.formLabel?.text = formLabel + textField?.text = text as NSString? + return textField + } + + func getSwitchView(_ labelText: String, _ tag: Int, _ key: String) -> (container: UIView, uiSwitch: UISwitch, label: Label) { + let toggleView = MFCommonViewsUtility.commonView() + + let toggle = UISwitch(frame: .zero) + toggleView.addSubview(toggle) + toggle.tag = tag + toggle.addTarget(self, action: #selector(switchValueDidChange(_:)), for: .valueChanged) + + let arToggleLabel = Label() + arToggleLabel.text = labelText + toggleView.addSubview(arToggleLabel) + arToggleLabel.font = MFStyler.fontRegularBodyLarge() + + NSLayoutConstraint.constraintPinSubview(toggle, pinTop: true, pinBottom: true, pinLeft: true, pinRight: false) + NSLayoutConstraint.constraintPinSubview(arToggleLabel, pinTop: true, pinBottom: true, pinLeft: false, pinRight: true) + NSLayoutConstraint(item: toggle as Any, attribute: .width, relatedBy: .equal, toItem: toggleView, attribute: .width, multiplier: 0, constant: 0).isActive = true + NSLayoutConstraint(item: arToggleLabel as Any, attribute: .left, relatedBy: .equal, toItem: toggle, attribute: .right, multiplier: 1.0, constant: PaddingThree).isActive = true + + return (toggleView, toggle, arToggleLabel) + } + + func setHardcodeValue() { + let hardCodeValue: [String : Any] = [ + GMFGConstant.AR.deviceLocation : [ + deviceLocationLatTextField?.text?.doubleValue, + deviceLocationLongTextField?.text?.doubleValue + ], + GMFGConstant.AR.capable : GMFGTestScreenData.shared.isARCapable, + GMFGConstant.BLE.advertisedData : advertisedDataTextField?.text as Any, + GMFGConstant.BLE.firmwareVersion : (cpeVersionNumberTextField?.text)! as String, + GMFGConstant.BLE.pin : pinTextField?.text as Any, + GMFGConstant.orderNumber : orderNumberTextField?.text as Any, + GMFGConstant.imei : imeiTextField?.text as Any, + GMFGConstant.BLE.fiveGCurrentSignal : currentRssiTextField?.text as Any, + GMFGConstant.BLE.fiveGHighSignal : highRssiTextField?.text as Any, + GMFGConstant.BLE.fiveGLowSignal : lowRssiTextField?.text as Any + ] + + /// We can append to above dict but make sure whether backend is expecting below parameters as well when a call is made + let hardcodeToggleVal: [String: Bool] = [ + GMFGConstant.Test.hardcodeToggle : toggleval.boolForKey(GMFGConstant.Test.hardcodeToggle), + GMFGConstant.Test.CPEToggle : toggleval.boolForKey(GMFGConstant.Test.CPEToggle), + GMFGConstant.Test.fiveGSignalToggle : toggleval.boolForKey(GMFGConstant.Test.fiveGSignalToggle), + GMFGConstant.Test.forceBCS : toggleval.boolForKey(GMFGConstant.Test.forceBCS), + GMFGConstant.Test.visualDebugger : toggleval.boolForKey(GMFGConstant.Test.visualDebugger) + ] + + GMFGTestScreenData.shared.hardcodeDict = hardCodeValue + GMFGTestScreenData.shared.toggleDict = hardcodeToggleVal + GMFGTestScreenData.shared.deviceLocation = [(deviceLocationLatTextField?.text as String? ?? GMFGConstant.empty), deviceLocationLongTextField?.text as String? ?? GMFGConstant.empty] + GMFGTestScreenData.shared.cpeVersionNumber = (cpeVersionNumberTextField?.text)! as String + } +} + +class GMFGTestScreenData: NSObject { + var geminiHardCode = MFSessionSingleton.sharedGlobal()?.testObject?.is5GGoodSignalActive ?? false + var isCPESimulated = true && (MFSessionSingleton.sharedGlobal()?.testObject?.is5GGoodSignalActive ?? false) + var isHardcodedValues = false && (MFSessionSingleton.sharedGlobal()?.testObject?.is5GGoodSignalActive ?? false) + var isARCapable = true + var cpeVersionNumber = GMFGConstant.empty + var isEnable5GSignal = true && (MFSessionSingleton.sharedGlobal()?.testObject?.is5GGoodSignalActive ?? false) + var forceBCS = false + var visualDebugger = false + var deviceLocation: [String] = [] + var hardcodeDict: [String: Any] = [:] + var toggleDict: [String : Bool] = [:] + static let shared = GMFGTestScreenData() + + /// to avoid called to create instance + private override init() { + super.init() + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGTestScreenData.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGTestScreenData.swift new file mode 100644 index 0000000..b8ee312 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/GMFGTestScreenData.swift @@ -0,0 +1,30 @@ +// +// GMFGTestScreenData.swift +// JSONCreator +// +// Created by Matt Bruce on 5/11/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import UIKit + +class GMFGTestScreenData: NSObject { + var geminiHardCode = false + var isCPESimulated = true + var isHardcodedValues = false + var isARCapable = true + var cpeVersionNumber = GMFGConstant.empty + var isEnable5GSignal = true + var forceBCS = false + var visualDebugger = false + var deviceLocation: [String] = [] + var hardcodeDict: [String: Any] = [:] + var toggleDict: [String : Bool] = [:] + static let shared = GMFGTestScreenData() + + /// to avoid called to create instance + private override init() { + super.init() + } +} diff --git a/JSONCreator_iOS/JSONCreator/5G/Utility/Protocols/GMFGBLEHandlerProtocol.swift b/JSONCreator_iOS/JSONCreator/5G/Utility/Protocols/GMFGBLEHandlerProtocol.swift new file mode 100644 index 0000000..cb025ff --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/5G/Utility/Protocols/GMFGBLEHandlerProtocol.swift @@ -0,0 +1,21 @@ +// +// GMFGBLEHandlerProtocol.swift +// MVM5G +// +// Created by Jason Beck on 7/10/20. +// Copyright © 2020 Kyle. All rights reserved. +// +import Foundation + +protocol GMFGBLEHandlerProtocol { + func convertToData(_ dictionary: [String: Any]) -> Data? + func response(response: [String : Any]) + func deviceNotReady() +} + +extension GMFGBLEHandlerProtocol { + func convertToData(_ dictionary: [String: Any]) -> Data? { + do { return try JSONSerialization.data(withJSONObject: dictionary, options: []) } + catch { return nil } + } +} diff --git a/JSONCreator_iOS/JSONCreator/AppDelegate.swift b/JSONCreator_iOS/JSONCreator/AppDelegate.swift index 2ee61fb..042ac17 100644 --- a/JSONCreator_iOS/JSONCreator/AppDelegate.swift +++ b/JSONCreator_iOS/JSONCreator/AppDelegate.swift @@ -8,6 +8,7 @@ import UIKit import MVMCoreUI +import VDS @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate { @@ -129,7 +130,12 @@ extension AppDelegate: MVMCoreGlobalTopAlertDelegateProtocol { extension AppDelegate { func register(){ + ModelRegistry.register(handler: TestLabelToggle.self, for: TestLabelToggleModel.self) + ModelRegistry.register(handler: TestToggle.self, for: TestToggleModel.self) + ModelRegistry.register(handler: TextEntryField.self, for: TextEntryField64Model.self) ModelRegistry.register(handler: EmailVerifyField.self, for: EmailVerifyModel.self) + ModelRegistry.register(handler: WifiWidget.self, for: WifiWidgetModel.self) + ModelRegistry.register(handler: ToggleWifiActionHandler.self, for: ToggleWifiActionModel.self) } } @@ -140,7 +146,7 @@ extension AppDelegate { public var emailField: TextEntryFieldModel public var bottomMolecule: MoleculeModelProtocol public var email: String - public var backgroundColor: Color? + public var backgroundColor: MVMCoreUI.Color? public init(emailField: TextEntryFieldModel, email: String, bottomMolecule: MoleculeModelProtocol) { self.emailField = emailField @@ -235,3 +241,52 @@ extension AppDelegate { bottomView.isHidden = isBottomViewHidden } } + +class ToggleWifiActionModel: ActionModelProtocol { + static var identifier: String = "toggleWifi" + var extraParameters: JSONValueDictionary? + var analyticsData: JSONValueDictionary? + var wifiId: String + var actionType: String = ToggleWifiActionModel.identifier + + init(wifiId: String) { + self.wifiId = wifiId + } +} + +class ToggleWifiActionHandler: MVMCoreActionHandlerProtocol { + required public init() {} + + func handleAction(_ model: ActionModelProtocol, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) { + guard let action = model as? ToggleWifiActionModel else { return } + + print("Wi-Fi Id: \(action.wifiId)") + + } +} + +class TextEntryField64Model: TextEntryFieldModel { + open override class var identifier: String { "textFieldBase64" } + + open override func formFieldServerValue() -> AnyHashable? { + guard let value = super.formFieldServerValue() as? String else { return nil } + return value.base64Encoded() + } + + required init(from decoder: Decoder) throws { + try super.init(from: decoder) + text = text?.base64Decoded() + } +} + +extension String { + + func base64Encoded() -> String? { + data(using: .utf8)?.base64EncodedString() + } + + func base64Decoded() -> String? { + guard let data = Data(base64Encoded: self) else { return nil } + return String(data: data, encoding: .utf8) + } +} diff --git a/JSONCreator_iOS/JSONCreator/DecodableDefaults+VDS.swift b/JSONCreator_iOS/JSONCreator/DecodableDefaults+VDS.swift new file mode 100644 index 0000000..bb23af3 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/DecodableDefaults+VDS.swift @@ -0,0 +1,117 @@ +// +// DecodableDefaults+VDS.swift +// JSONCreator +// +// Created by Matt Bruce on 7/28/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import VDS +import MVMCore + +//MARK: - Decodable Defaults +extension VDSFontCategory { + public enum DefaultFeature: DecodableDefault.Source { + public static var defaultValue: VDSFontCategory { .feature } + } + public enum DefaultTitle: DecodableDefault.Source { + public static var defaultValue: VDSFontCategory { .title } + } + public enum DefaultBody: DecodableDefault.Source { + public static var defaultValue: VDSFontCategory { .body } + } + public enum DefaultMicro: DecodableDefault.Source { + public static var defaultValue: VDSFontCategory { .micro } + } +} + +extension VDSFontSize { + public enum Default2XLarge: DecodableDefault.Source { + public static var defaultValue: VDSFontSize { .xxlarge } + } + public enum DefaultXLarge: DecodableDefault.Source { + public static var defaultValue: VDSFontSize { .xlarge } + } + public enum DefaultLarge: DecodableDefault.Source { + public static var defaultValue: VDSFontSize { .large } + } + public enum DefaultMedium: DecodableDefault.Source { + public static var defaultValue: VDSFontSize { .medium } + } + public enum DefaultSmall: DecodableDefault.Source { + public static var defaultValue: VDSFontSize { .small } + } + public enum DefaultXSmall: DecodableDefault.Source { + public static var defaultValue: VDSFontSize { .xsmall } + } +} + +extension VDSTextPosition { + public enum DefaultLeft: DecodableDefault.Source { + public static var defaultValue: VDSTextPosition { .left } + } + public enum DefaultRight: DecodableDefault.Source { + public static var defaultValue: VDSTextPosition { .right } + } + public enum DefaultCenter: DecodableDefault.Source { + public static var defaultValue: VDSTextPosition { .center } + } +} + +extension VDSFontWeight { + public enum DefaultBold: DecodableDefault.Source { + public static var defaultValue: VDSFontWeight { .bold } + } + public enum DefaultRegular: DecodableDefault.Source { + public static var defaultValue: VDSFontWeight { .regular } + } +} + +extension VDSToggle { + public enum DefaultOffText: DecodableDefault.Source { + public static var defaultValue: String { "Off" } + } + public enum DefaultOnText: DecodableDefault.Source { + public static var defaultValue: String { "On" } + } +} + +extension Surface { + public enum DefaultLight: DecodableDefault.Source { + public static var defaultValue: Surface { .light } + } + public enum DefaultDark: DecodableDefault.Source { + public static var defaultValue: Surface { .dark } + } +} + +extension DecodableDefault { + + public struct Surface { + public typealias Light = DecodableDefault.Wrapper + public typealias Dark = DecodableDefault.Wrapper + } + + public struct VDSTypography { + public typealias FontCategoryFeature = DecodableDefault.Wrapper + public typealias FontCategoryTitle = DecodableDefault.Wrapper + public typealias FontCategoryBody = DecodableDefault.Wrapper + public typealias FontCategoryMicro = DecodableDefault.Wrapper + public typealias FontWeightBold = DecodableDefault.Wrapper + public typealias FontWeightRegular = DecodableDefault.Wrapper + public typealias FontSize2XLarge = DecodableDefault.Wrapper + public typealias FontSizeXLarge = DecodableDefault.Wrapper + public typealias FontSizeLarge = DecodableDefault.Wrapper + public typealias FontSizeMedium = DecodableDefault.Wrapper + public typealias FontSizeSmall = DecodableDefault.Wrapper + public typealias FontSizeXSmall = DecodableDefault.Wrapper + public typealias TextPositionLeft = DecodableDefault.Wrapper + public typealias TextPositionRight = DecodableDefault.Wrapper + public typealias TextPositionCenter = DecodableDefault.Wrapper + } +// public struct VDSToggle { +// public typealias OffText = DecodableDefault.Wrapper +// public typealias OnText = DecodableDefault.Wrapper +// } +} diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/5G-AccordianList.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/5G-AccordianList.json new file mode 100644 index 0000000..fe94d12 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/5G-AccordianList.json @@ -0,0 +1,215 @@ +{ + "Page": { + "pageType": "eagleSignalStrengthTips", + "template": "modalList", + "behaviors": [ + { + + } + ], + "header": { + "moleculeName": "header", + "molecule": { + "moleculeName": "headlineBody", + "headline": { + "moleculeName": "label", + "text": "Tips for finding a good 5G signal" + } + } + }, + "footer": { + "moleculeName": "footer", + "molecule": { + "moleculeName": "button", + "title": "OK", + "action": { + "actionType": "back", + } + } + }, + "molecules": [ + { + "moleculeName": "accordionListItem", + "hideLineWhenExpanded": true, + "selected": true, + "topPadding": 14, + "bottomPadding": 14, + "line": { + "moleculeName": "line", + "type":"none" + }, + "molecule": { + "moleculeName": "label", + "fontStyle": "BoldBodyLarge", + "text": "Face the 5G antenna." + }, + "molecules": [ + { + "moleculeName": "list1CTxt", + "topPadding": 0, + "body": { + "moleculeName": "label", + "text": "The words “Verizon Receiver” should face out towards the antenna. Use the 5G AR compass to reference the location of the antenna or find a new one." + }, + "link": { + "moleculeName": "link", + "title": "Open 5G AR compass", + "action": { + "actionType": "openPage", + "pageType": "forgotPasswordPage" + } + } + } + ] + }, + { + "moleculeName": "accordionListItem", + "hideLineWhenExpanded": true, + "selected": true, + "topPadding": 14, + "bottomPadding": 14, + "line": { + "moleculeName": "line", + "type":"none" + }, + "molecule": { + "moleculeName": "label", + "fontStyle": "BoldBodyLarge", + "text": "Find the clearest path to the antenna." + }, + "molecules": [ + { + "moleculeName": "listItem", + "topPadding": 0, + "molecule": { + "moleculeName": "label", + "text": "If indoors, test at a window first. If you don’t have a window that faces an antenna, or if you prefer to mount the Receiver to a wall, make sure it’s exterior-facing. Upstairs is recommended, or as high as possible in your location.\r\rIf outdoors, move away from trees and other obstacles. If at a railing, position the Receiver on the outer side of the railling, so the railing itself doesn’t block the signal." + } + } + ] + }, + { + "moleculeName": "accordionListItem", + "hideLineWhenExpanded": true, + "selected": true, + "topPadding": 14, + "bottomPadding": 14, + "line": { + "moleculeName": "line", + "type":"none" + }, + "molecule": { + "moleculeName": "label", + "fontStyle": "BoldBodyLarge", + "text": "Check the Receiver for a solid white light." + }, + "molecules": [ + { + "moleculeName": "list1CTxt", + "topPadding": 0, + "body": { + "moleculeName": "label", + "text": "When the Receiver finds a good signal, its LED light will be solid white." + }, + "link": { + "moleculeName": "link", + "title": "Receiver status lights", + "action": { + "actionType": "openPage", + "pageType": "forgotPasswordPage" + } + } + } + ] + }, + { + "moleculeName": "accordionListItem", + "hideLineWhenExpanded": true, + "selected": true, + "topPadding": 14, + "bottomPadding": 14, + "line": { + "moleculeName": "line", + "type":"none" + }, + "molecule": { + "moleculeName": "label", + "fontStyle": "BoldBodyLarge", + "text": "Explore more options." + }, + "molecules": [ + { + "moleculeName": "list1CTxt", + "topPadding": 0, + "body": { + "moleculeName": "label", + "text": "You may be able to test for a signal outdoors, or indoors if you haven’t already. Answer a few questions so we can help you determine what to do next." + }, + "link": { + "moleculeName": "link", + "title": "Learn what to do next", + "action": { + "actionType": "openPage", + "pageType": "forgotPasswordPage" + } + } + } + ] + } + ] + }, + "ModuleMap": { + "fivegBleUuid": { + "ResponseInfo": { + "locale": "EN", + "type": "Success", + "appSessionExtended": true, + "code": "00000", + "message": "0", + "userMessage": "0", + "topAlertTime": 0 + }, + "fivegBleUuid": { + "bleAPIVersion": "2.0", + "bleSignalThreshold": "-94", + "bleSignalLowerBound": "-140", + "bleSignalUpperBound": "-1", + "advertisedData": "VZ5G_RECEIVER_203975", + "pin": "000000", + "bleAdv": [ + { + "uuid": "00002a37-0000-1000-8000-00805f9b34fb" + } + ], + "service": [ + { + "uuid": "6FE382F6-7A33-4990-9D7B-F2770A161E68", + "characteristicUUID": { + "Fiveg": "6FE382F6-7A33-4990-9D7B-4C9002F90979", + "Fourg": "6FE382F6-7A33-4990-9D7B-4C9005F90979", + "Radiocontrol": "6FE382F6-7A33-4990-9D7B-4C9003F90979", + "Activation": "6FE382F6-7A33-4990-9D7B-4C9006F90979", + "RepeaterPair": "6FE382F6-7A33-4990-9D7B-4C9010F90979", + "RepeaterMode": "6FE382F6-7A33-4990-9D7B-4C9011F90979", + "RepeaterPairStatus": "6FE382F6-7A33-4990-9D7B-4C9012F90979", + "RepeaterOperations": "6FE382F6-7A33-4990-9D7B-4C9013F90979", + "ReceiverMode": "6FE382F6-7A33-4990-9D7B-4C9070F90979", + "RouterMode": "6FE382F6-7A33-4990-9D7B-4C9071F90979", + "RouterWifi": "6FE382F6-7A33-4990-9D7B-4C9072F90979", + "RouterPublicInternetAccess": "6FE382F6-7A33-4990-9D7B-4C9073F90979", + "RouterSpeedTest": "6FE382F6-7A33-4990-9D7B-4C9074F90979", + "RouterSpeedTestResults": "6FE382F6-7A33-4990-9D7B-4C9075F90979", + "WifiExtendersStatus": "6FE382F6-7A33-4990-9D7B-4C9076F90979", + "AddWifiExtender": "6FE382F6-7A33-4990-9D7B-4C9077F90979", + "PowerCycle": "6FE382F6-7A33-4990-9D7B-4C9078F90979", + "FOTACheck": "6FE382F6-7A33-4990-9D7B-4C9079F90979", + "FOTAStatus": "6FE382F6-7A33-4990-9D7B-4C907AF90979", + "RepeaterInfo": "1C120000-88DD-4E3E-AFC2-DF4300574E43" + } + } + ] + } + } + } +} + diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json index 56ee6c2..6918dfb 100644 --- a/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json @@ -19,6 +19,13 @@ "stack": { "moleculeName": "stack", "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "testToggle", + "fieldKey": "isActive" + } + }, { "moleculeName": "stackItem", "molecule": { @@ -72,11 +79,12 @@ { "moleculeName": "stackItem", "molecule": { - "moleculeName": "textField", + "moleculeName": "textFieldBase64", "fieldKey": "zipcode", "type": "number", "placeholder": "90210", "title": "Zip Code", + "text": "NzUwMzQ=", "errorMessage": "Please enter a valid zip code." } } diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/MVA3.0/RadioButtons.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/MVA3.0/RadioButtons.json index 6e84fb7..25070eb 100644 --- a/JSONCreator_iOS/JSONCreator/JSON/Samples/MVA3.0/RadioButtons.json +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/MVA3.0/RadioButtons.json @@ -6,88 +6,85 @@ "Page": { "pageType":"x", "template":"list", - "header": { - - }, - "footer":{ - - }, + "header": {}, + "footer":{}, "molecules": [{ - "moleculeName":"listLVRBImg", - "radioButton":{ -"moleculeName":"radioButton", -}, -"image": { -"moleculeName":"image", -"image": "https://mobile.vzw.com/hybridClient/is/image/VerizonWireless/iPhoneXr_Black_PureAngles" -}, -"eyebrowHeadlineBodyLink": { -"moleculeName": "eyebrowHeadlineBodyLink", -"eyebrow":{ - "moleculeName": "label", - "text":"Eyebrow" -}, -"headline":{ - "moleculeName": "label", - "text":"Headline" -}, -"body":{ - "moleculeName": "label", - "text":"Body" -}, -"link":{ - "moleculeName": "link", - "title":"TextButton", - "action": { - "actionType": "cancel" - } -} -} + "moleculeName":"listLVRBImg", + "radioButton":{ + "moleculeName":"radioButton" + }, + "image": { + "moleculeName":"image", + "image": "https://mobile.vzw.com/hybridClient/is/image/VerizonWireless/iPhoneXr_Black_PureAngles" + }, + "eyebrowHeadlineBodyLink": { + "moleculeName": "eyebrowHeadlineBodyLink", + "eyebrow":{ + "moleculeName": "label", + "text":"Eyebrow" + }, + "headline":{ + "moleculeName": "label", + "text":"Headline" + }, + "body":{ + "moleculeName": "label", + "text":"Body" + }, + "link":{ + "moleculeName": "link", + "title":"TextButton", + "action": { + "actionType": "cancel" + } + } + } }, -{ - "moleculeName":"listLVRBBdy", - "radioButton":{ -"moleculeName":"radioButton", -}, -"headlineBody": { -"moleculeName": "headlineBody", -"headline":{ - "moleculeName": "label", - "text":"Headline" -}, -"body":{ - "moleculeName": "label", - "text":"Body" -} -} - },{ - "moleculeName":"listLVRBAll", - "radioButton":{ -"moleculeName":"radioButton", -}, -"eyebrowHeadlineBodyLink": { -"moleculeName": "eyebrowHeadlineBodyLink", -"eyebrow":{ - "moleculeName": "label", - "text":"Eyebrow" -}, -"headline":{ - "moleculeName": "label", - "text":"Headline" -}, -"body":{ - "moleculeName": "label", - "text":"Body" -}, -"link":{ - "moleculeName": "link", - "title":"TextButton", - "action": { - "actionType": "cancel" - } -} -} - } -] + { + "moleculeName":"listLVRBBdy", + "radioButton":{ + "moleculeName":"radioButton" + }, + "headlineBody": { + "moleculeName": "headlineBody", + "headline":{ + "moleculeName": "label", + "text":"Headline" + }, + "body":{ + "moleculeName": "label", + "text":"Body" + } + } + },{ + "moleculeName":"listLVRBAll", + "radioButton":{ + "moleculeName":"radioButton" + }, + "eyebrowHeadlineBodyLink": { + "moleculeName": "eyebrowHeadlineBodyLink", + "eyebrow":{ + "moleculeName": "label", + "text":"Eyebrow" + }, + "headline":{ + "moleculeName": "label", + "text":"Headline" + }, + "body":{ + "moleculeName": "label", + "text":"Body" + }, + "link":{ + "moleculeName": "link", + "title":"TextButton", + "action": { + "actionType": "cancel" + } + } + } + } + ] } } + diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/RadioButtonGroup.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/RadioButtonGroup.json new file mode 100644 index 0000000..3b7e8d9 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/RadioButtonGroup.json @@ -0,0 +1,112 @@ +{ + "ResponseInfo": { + "locale": "EN", + "server": "loghost-mf_postpayss01", + "userMessage": "0", + "code": "00000", + "appSessionExtended": true, + "message": "0", + "mdn": "5162733172", + "buildNumber": "1287", + "type": "Success", + "requestId": "288cb5d1-52a1-4a71-aeaf-55e4d0bcc275", + "topAlertTime": 0 + }, + "Page": { + "footer": { + "moleculeName": "footer", + "molecule": { + "moleculeName": "twoButtonView", + "primaryButton": { + "groupName": "default", + "moleculeName": "button", + "title": "Call Us", + "action": { + "actionType": "openPage", + "pageType": "callUsSubmit" + } + } + } + }, + "formRules": [ + { + "groupName": "default", + "rules": [ + { + "type": "anyRequired", + "fields": [ + "rB1" + ] + } + ] + } + ], + "pageType": "list", + "molecules": [ + { + "moleculeName": "listLVRBBdy", + "radioButton": { + "moleculeName": "radioButton", + "fieldKey": "rB1", + "fieldValue": "Radio Button 1" + }, + "headlineBody": { + "moleculeName": "headlineBody", + "headline": { + "moleculeName": "label", + "text": "Lorem ipsum dolor sit amet" + }, + "body": { + "moleculeName": "label", + "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit" + } + } + }, + { + "moleculeName": "listLVRBBdy", + "radioButton": { + "moleculeName": "radioButton", + "fieldKey": "rB1", + "fieldValue": "Radio Button 2" + }, + "headlineBody": { + "moleculeName": "headlineBody", + "headline": { + "moleculeName": "label", + "text": "Lorem ipsum dolor sit amet" + }, + "body": { + "moleculeName": "label", + "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit" + } + } + }, + { + "moleculeName": "listLVRBBdy", + "radioButton": { + "moleculeName": "radioButton", + "fieldKey": "rB1", + "fieldValue": "Radio Button 3" + }, + "headlineBody": { + "moleculeName": "headlineBody", + "headline": { + "moleculeName": "label", + "text": "Lorem ipsum dolor sit amet" + }, + "body": { + "moleculeName": "label", + "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit" + } + } + } + ], + "suppressPostLaunchRequests": false, + "tabBarHidden": false, + "template": "list", + "navigationBar": { + "moleculeName": "navigationBar", + "title": "Call Us" + } + } +} diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/TabsSample.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/TabsSample.json index f9a61ae..92caec3 100644 --- a/JSONCreator_iOS/JSONCreator/JSON/Samples/TabsSample.json +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/TabsSample.json @@ -44,7 +44,8 @@ } }, - "molecules": [{ + "molecules": [ + { "moleculeName": "tabsListItem", "tabs": { "moleculeName": "tabs", diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/Wifi/Wifi-1.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/Wifi/Wifi-1.json new file mode 100644 index 0000000..952922d --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/Wifi/Wifi-1.json @@ -0,0 +1,637 @@ +{ + "Page": { + "template": "list", + "pageType": "wifiSample1", + "screenHeading": "Network Management", + "hideFabOverlay": true, + "suppressPostLaunchRequests": false, + "tabBarHidden": true, + "line": { + "moleculeName": "line", + "type": "none" + }, + "header": { + "moleculeName": "header", + "molecule": { + "moleculeName": "headlineBody", + "headline": { + "moleculeName": "label", + "text": "Wi-Fi networks" + } + }, + "line": { + "moleculeName": "line", + "type": "none" + } + }, + "molecules": [ + { + "moleculeName": "tabsListItem", + "tabs": { + "moleculeName": "tabs", + "tabs": [ + { + "label": { + "moleculeName": "label", + "text": "Primary" + } + }, + { + "label": { + "moleculeName": "label", + "text": "Guest" + } + }, + { + "label": { + "moleculeName": "label", + "text": "IoT" + } + } + ] + }, + "molecules": [ + [ + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "molecule": { + "moleculeName": "container", + "backgroundColor": "#F6F6F6", + "molecule": { + "moleculeName": "stack", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 16, + "useHorizontalMargins": true, + "useVerticalMargins": true, + "spacing": 16, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "stack", + "spacing": 8, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "labelToggle", + "label": { + "moleculeName": "label", + "text": "West Coast Avocado Toast", + "fontStyle": "BoldBodyLarge" + }, + "toggle": { + "moleculeName": "toggle", + "state": true, + "action": { + "actionType": "toggleWifi", + "enabled": true, + "wifiId": "primary-1" + } + } + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "label", + "text": "Primary Wi-Fi (2.4 Ghz)", + "fontStyle": "RegularMicro" + } + } + ] + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textField", + "fieldKey": "preferredLastName", + "type": "password", + "text": "12345", + "readOnly": true + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "stack", + "axis": "horizontal", + "spacing": 8, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "link", + "title": "Edit Wi-Fi details", + "action": { + "actionType": "openPage", + "pageType": "editWifi", + "extraParameters": { + "wifiType": "primary" + } + } + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "link", + "title": "Share Wi-Fi", + "action": { + "actionType": "share", + "sharedText": "123456", + "sharedType": "Text" + } + } + } + ] + } + } + ] + } + } + }, + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "molecule": { + "moleculeName": "container", + "backgroundColor": "#F6F6F6", + "molecule": { + "moleculeName": "stack", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 16, + "useHorizontalMargins": true, + "useVerticalMargins": true, + "spacing": 16, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "stack", + "spacing": 8, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "labelToggle", + "label": { + "moleculeName": "label", + "text": "New England Clam Router", + "fontStyle": "BoldBodyLarge" + }, + "toggle": { + "moleculeName": "toggle", + "state": false, + "action": { + "actionType": "toggleWifi", + "enabled": false, + "wifiId": "primary-2" + } + } + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "label", + "text": "Primary Wi-Fi (5 Ghz)", + "fontStyle": "RegularMicro" + } + } + ] + } + } + ] + } + } + }, + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "line": { + "moleculeName": "line", + "type": "standard" + }, + "molecule": { + "moleculeName": "container", + "backgroundColor": "#F6F6F6", + "molecule": { + "moleculeName": "stack", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 16, + "useHorizontalMargins": true, + "useVerticalMargins": true, + "spacing": 16, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "stack", + "spacing": 8, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "labelToggle", + "label": { + "moleculeName": "label", + "text": "Chicago Pizza", + "fontStyle": "BoldBodyLarge" + }, + "toggle": { + "moleculeName": "toggle", + "state": true, + "action": { + "actionType": "toggleWifi", + "enabled": true, + "wifiId": "primary-3" + } + } + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "label", + "text": "Primary Wi-Fi (5 Ghz 2)", + "fontStyle": "RegularMicro" + } + } + ] + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textField", + "fieldKey": "preferredLastName", + "type": "password", + "text": "12345", + "readOnly": true + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "stack", + "axis": "horizontal", + "spacing": 8, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "link", + "title": "Edit Wi-Fi details", + "action": { + "actionType": "openPage", + "pageType": "editWifi", + "extraParameters": { + "wifiType": "primary" + } + } + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "link", + "title": "Share Wi-Fi", + "action": { + "actionType": "share", + "sharedText": "123456", + "sharedType": "Text" + } + } + } + ] + } + } + ] + } + } + }, + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "molecule": { + "moleculeName": "container", + "backgroundColor": "#F6F6F6", + "molecule": { + "moleculeName": "stack", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 16, + "useHorizontalMargins": true, + "useVerticalMargins": true, + "spacing": 16, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "label", + "backgroundColor": "#000000", + "textColor": "#ffffff", + "text": "Disabled" + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "label", + "fontStyle": "BoldBodyLarge", + "text": "SON (Self-Organizing Network)" + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "label", + "text": "Enable SON to automatically connect your devices to the fastest available Wi-Fi network." + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "link", + "title": "Details about SON", + "action": { + "actionType": "openPage", + "pageType": "sonDetails", + "extraParameters": { + "wifiType": "primary" + } + } + } + } + ] + } + } + } + ], + [ + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "molecule": { + "moleculeName": "label", + "text": "Limits access to devices in your home, recommended for guests" + } + }, + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "molecule": { + "moleculeName": "container", + "backgroundColor": "#F6F6F6", + "molecule": { + "moleculeName": "stack", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 16, + "useHorizontalMargins": true, + "useVerticalMargins": true, + "spacing": 16, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "stack", + "spacing": 8, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "labelToggle", + "label": { + "moleculeName": "label", + "text": "You Don't Belong Here", + "fontStyle": "BoldBodyLarge" + }, + "toggle": { + "moleculeName": "toggle", + "state": true, + "action": { + "actionType": "toggleWifi", + "enabled": true, + "wifiId": "guest-1" + } + } + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "label", + "text": "Guest Wi-Fi (2.4 Ghz)", + "fontStyle": "RegularMicro" + } + } + ] + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textField", + "fieldKey": "preferredLastName", + "type": "password", + "text": "12345", + "readOnly": true + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "stack", + "axis": "horizontal", + "spacing": 8, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "link", + "title": "Edit Wi-Fi details", + "action": { + "actionType": "openPage", + "pageType": "editWifi", + "extraParameters": { + "wifiType": "primary" + } + } + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "link", + "title": "Share Wi-Fi", + "action": { + "actionType": "share", + "sharedText": "123456", + "sharedType": "Text" + } + } + } + ] + } + } + ] + } + } + } + ], + [ + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "molecule": { + "moleculeName": "label", + "text": "Recommended for optimal connectivity to \"Internet of Things\" (IoT) devices, for example smart speakers, plugs, and light switches." + } + }, + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "molecule": { + "moleculeName": "container", + "backgroundColor": "#F6F6F6", + "molecule": { + "moleculeName": "stack", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 16, + "useHorizontalMargins": true, + "useVerticalMargins": true, + "spacing": 16, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "stack", + "spacing": 8, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "labelToggle", + "label": { + "moleculeName": "label", + "text": "Connected Homies", + "fontStyle": "BoldBodyLarge" + }, + "toggle": { + "moleculeName": "toggle", + "state": true, + "action": { + "actionType": "toggleWifi", + "enabled": true, + "wifiId": "iot-1" + } + } + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "label", + "text": "IoT (2.4 Ghz)", + "fontStyle": "RegularMicro" + } + } + ] + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textField", + "fieldKey": "preferredLastName", + "type": "password", + "text": "12345", + "readOnly": true + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "stack", + "axis": "horizontal", + "spacing": 8, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "link", + "title": "Edit Wi-Fi details", + "action": { + "actionType": "openPage", + "pageType": "editWifi", + "extraParameters": { + "wifiType": "primary" + } + } + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "link", + "title": "Share Wi-Fi", + "action": { + "actionType": "share", + "sharedText": "123456", + "sharedType": "Text" + } + } + } + ] + } + } + ] + } + } + } + ] + ] + } + ], + "footer": {} + } +} + diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/Wifi/Wifi-2.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/Wifi/Wifi-2.json new file mode 100644 index 0000000..77b30cb --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/Wifi/Wifi-2.json @@ -0,0 +1,357 @@ +{ + "Page": { + "template": "list", + "pageType": "wifiSample1", + "screenHeading": "Network Management", + "hideFabOverlay": true, + "suppressPostLaunchRequests": false, + "tabBarHidden": true, + "line": { + "moleculeName": "line", + "type": "none" + }, + "header": { + "moleculeName": "header", + "molecule": { + "moleculeName": "headlineBody", + "headline": { + "moleculeName": "label", + "text": "Wi-Fi networks" + } + }, + "line": { + "moleculeName": "line", + "type": "none" + } + }, + "molecules": [ + { + "moleculeName": "tabsListItem", + "tabs": { + "moleculeName": "tabs", + "tabs": [ + { + "label": { + "moleculeName": "label", + "text": "Primary" + } + }, + { + "label": { + "moleculeName": "label", + "text": "Guest" + } + }, + { + "label": { + "moleculeName": "label", + "text": "IoT" + } + } + ] + }, + "molecules": [ + [ + { + "moleculeName": "listItem", + "verticalAlignment": "leading", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "enabled": false, + "molecule": { + "moleculeName": "toggle" + } + }, + { + "moleculeName": "listItem", + "verticalAlignment": "leading", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "disabled": true, + "molecule": { + "moleculeName": "testToggle" + } + }, + { + "moleculeName": "listItem", + "verticalAlignment": "leading", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "molecule": { + "moleculeName": "wifiWidget", + "backgroundColor": "#F6F6F6", + "wifiId": "primary-1", + "title": "West Coast Avocado Toast", + "enabled": true, + "enabledAction": { + "actionType": "toggleWifi", + "enabled": true, + "wifiId": "primary-1", + }, + "description": "Primary Wi-Fi (2.4 Ghz)", + "password": "1234567", + "editTitle": "Edit Wi-Fi details", + "editAction": { + "actionType": "openPage", + "pageType": "editWifi", + "extraParameters": { + "wifiId": "primary-1" + } + }, + "shareTitle": "Share Wi-Fi", + "shareAction": { + "actionType": "share", + "sharedText": "ssid: primary-1 password: 123456", + "sharedType": "Text" + } + } + }, + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "molecule": { + "moleculeName": "wifiWidget", + "backgroundColor": "#F6F6F6", + "wifiId": "primary-2", + "title": "New England Clam Router", + "enabled": false, + "enabledAction": { + "actionType": "toggleWifi", + "enabled": false, + "wifiId": "primary-2" + }, + "description": "Primary Wi-Fi (5.0 Ghz)", + "password": "1234567", + "editTitle": "Edit Wi-Fi details", + "editAction": { + "actionType": "openPage", + "pageType": "editWifi", + "extraParameters": { + "wifiId": "primary-2" + } + }, + "shareTitle": "Share Wi-Fi", + "shareAction": { + "actionType": "share", + "sharedText": "ssid: primary-2 password: 123456", + "sharedType": "Text" + } + } + }, + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "line": { + "moleculeName": "line", + "type": "standard" + }, + "molecule": { + "moleculeName": "wifiWidget", + "backgroundColor": "#F6F6F6", + "wifiId": "primary-3", + "title": "Chicago Pizza", + "enabled": true, + "enabledAction": { + "actionType": "toggleWifi", + "enabled": true, + "wifiId": "primary-3" + }, + "description": "Primary Wi-Fi (5.0 Ghz 2)", + "password": "1234567", + "editTitle": "Edit Wi-Fi details", + "editAction": { + "actionType": "openPage", + "pageType": "editWifi", + "extraParameters": { + "wifiId": "primary-3" + } + }, + "shareTitle": "Share Wi-Fi", + "shareAction": { + "actionType": "share", + "sharedText": "ssid: primary-3 password: 123456", + "sharedType": "Text" + } + } + }, + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "molecule": { + "moleculeName": "container", + "backgroundColor": "#F6F6F6", + "molecule": { + "moleculeName": "stack", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 16, + "useHorizontalMargins": true, + "useVerticalMargins": true, + "spacing": 16, + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "label", + "leftPadding": 5, + "rightPadding": 5, + "topPadding": 2, + "bottomPadding": 2, + "backgroundColor": "#000000", + "textColor": "#ffffff", + "text": "Disabled" + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName":"headlineBodyLink", + "headlineBody":{ + "moleculeName": "headlineBody", + "headline":{ + "moleculeName": "label", + "fontStyle": "BoldBodyLarge", + "text": "SON (Self-Organizing Network)" + }, + "body":{ + "moleculeName": "label", + "text": "Enable SON to automatically connect your devices to the fastest available Wi-Fi network." + } + }, + "link":{ + "moleculeName": "link", + "title": "Details about SON", + "action": { + "actionType": "openPage", + "pageType": "sonDetails", + "extraParameters": { + "wifiType": "primary" + } + } + } + } + } + ] + } + } + } + ], + [ + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "molecule": { + "moleculeName": "label", + "text": "Limits access to devices in your home, recommended for guests" + } + }, + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "molecule": { + "moleculeName": "wifiWidget", + "backgroundColor": "#F6F6F6", + "wifiId": "guest-1", + "title": "You Don't Belong Here", + "enabled": true, + "enabledAction": { + "actionType": "toggleWifi", + "enabled": true, + "wifiId": "guest-1" + }, + "description": "Guest Wi-Fi (2.4 Ghz)", + "password": "1234567", + "editTitle": "Edit Wi-Fi details", + "editAction": { + "actionType": "openPage", + "pageType": "editWifi", + "extraParameters": { + "wifiId": "guest-1" + } + }, + "shareTitle": "Share Wi-Fi", + "shareAction": { + "actionType": "share", + "sharedText": "ssid: guest-1 password: 123456", + "sharedType": "Text" + } + } + } + ], + [ + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "molecule": { + "moleculeName": "label", + "text": "Recommended for optimal connectivity to \"Internet of Things\" (IoT) devices, for example smart speakers, plugs, and light switches." + } + }, + { + "moleculeName": "listItem", + "leftPadding": 16, + "rightPadding": 16, + "topPadding": 16, + "bottomPadding": 8, + "molecule": { + "moleculeName": "wifiWidget", + "backgroundColor": "#F6F6F6", + "wifiId": "iot-2", + "title": "Connected Homies", + "enabled": true, + "enabledAction": { + "actionType": "toggleWifi", + "enabled": true, + "wifiId": "iot-1" + }, + "description": "IoT (2.4 Ghz)", + "password": "1234567", + "editTitle": "Edit Wi-Fi details", + "editAction": { + "actionType": "openPage", + "pageType": "editWifi", + "extraParameters": { + "wifiId": "iot-1" + } + }, + "shareTitle": "Share Wi-Fi", + "shareAction": { + "actionType": "share", + "sharedText": "ssid: iot-1 password: 123456", + "sharedType": "Text" + } + } + } + ] + ] + } + ], + "footer": {} + } +} + diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/accordionViews.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/accordionViews.json new file mode 100644 index 0000000..5d4b6b3 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/accordionViews.json @@ -0,0 +1,67 @@ + +{ + "ResponseInfo" : { + "code" : "00000", + "type" : "Success" + }, + "Page": { + "pageType":"x", + "template":"list", + "header": { + "moleculeName":"header", + "molecule": { + "moleculeName": "headlineBody", + "headline":{ + "moleculeName": "label", + "text":"Your lines are on Unlimited plans." + }, + "body":{ + "moleculeName": "label", + "text":"Need something different? Take a minute to explore other plan options." + } + } + + }, + "footer":{ + "moleculeName":"footer", + "molecule": { + "moleculeName":"twoButtonView", + "primaryButton":{ + "moleculeName": "button", + "title":"Explore", + "action": { + "actionType": "openPage", + "pageType": "explore" + } + }, + "secondaryButton":{ + "moleculeName": "button", + "title":"Recommend", + "action": { + "actionType": "openPage", + "pageType": "recommend" + } + } + } + + }, + "molecules": [ + { + "moleculeName": "accordionListItem", + "molecule": { + "moleculeName": "label", + "text": "Testing" + }, + "molecules": [ + { + "moleculeName":"listItem", + "molecule": { + "moleculeName":"image", + "image": "https://mobile.vzw.com/hybridClient/is/image/VerizonWireless/iPhoneXr_Black_PureAngles", + "contentMode": "scaleAspectFit" + } + } + ] + }] + } +} diff --git a/JSONCreator_iOS/JSONCreator/MF/JSONCreatorActionHandler.swift b/JSONCreator_iOS/JSONCreator/MF/JSONCreatorActionHandler.swift index ec00c22..77e25b0 100644 --- a/JSONCreator_iOS/JSONCreator/MF/JSONCreatorActionHandler.swift +++ b/JSONCreator_iOS/JSONCreator/MF/JSONCreatorActionHandler.swift @@ -15,7 +15,7 @@ import MVMCoreUI //-------------------------------------------------- public static var identifier: String = "print" public var actionType: String = ActionPrintModel.identifier - public var text: String + public var text: String = "" public var delay = false public var extraParameters: JSONValueDictionary? public var analyticsData: JSONValueDictionary? diff --git a/JSONCreator_iOS/JSONCreator/TestToggle.swift b/JSONCreator_iOS/JSONCreator/TestToggle.swift new file mode 100644 index 0000000..c9055ca --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/TestToggle.swift @@ -0,0 +1,101 @@ +// +// Toggle.swift +// MVMCoreUI +// +// Created by Kevin Christiano on 12/4/19. +// Copyright © 2019 Verizon Wireless. All rights reserved. +// + +import MVMCore +import MVMCoreUI +import UIKit +import VDS + +extension MoleculeViewProtocol { + public func onModelChange(model: MoleculeModelProtocol) { + if let backgroundColor = model.backgroundColor { + self.backgroundColor = backgroundColor.uiColor + } + + if let accessibilityIdentifier = model.accessibilityIdentifier { + self.accessibilityIdentifier = accessibilityIdentifier + } + } +} + +extension Modelable where Self: MoleculeViewProtocol { + public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { + self.init(with: model as! ModelType) + self.set(with: model, delegateObject, additionalData) + } +} + +open class TestToggle: VDSToggleBase, MoleculeViewProtocol, MVMCoreViewProtocol { + + /// The state on the toggle. Default value: false. + open override var isOn: Bool { + didSet { + _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) + } + } + + open override var isEnabled: Bool { + didSet { + model.enabled = isEnabled && !model.readOnly + } + } + + //-------------------------------------------------- + // MARK: - Delegate + //-------------------------------------------------- + + private var delegateObject: MVMCoreUIDelegateObject? + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + open override func onStateChange(viewModel: ModelType) { + super.onStateChange(viewModel: viewModel) + onModelChange(model: viewModel) + } + + // MARK:- MoleculeViewProtocol + public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + + guard let castedModel = model as? ModelType else { return } + set(with: castedModel) + + self.delegateObject = delegateObject + + guard let formFieldProtocol = model as? FormFieldProtocol else { return } + FormValidator.setupValidation(for: formFieldProtocol, delegate: delegateObject?.formHolderDelegate) + + let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: model) + if castedModel.action != nil || castedModel.alternateAction != nil { + onChange = { [weak self] in + guard let self = self else { return } + if self.isOn { + if let action = castedModel.action { + MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: additionalDataWithSource, delegateObject: delegateObject) + } + } else { + if let action = castedModel.alternateAction ?? castedModel.action { + MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: additionalDataWithSource, delegateObject: delegateObject) + } + } + } + } + } + + public static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 24 + } +} + +// MARK: - MVMCoreUIViewConstrainingProtocol +extension TestToggle: MVMCoreUIViewConstrainingProtocol { + + public func needsToBeConstrained() -> Bool { true } + + public func horizontalAlignment() -> UIStackView.Alignment { .trailing } +} diff --git a/JSONCreator_iOS/JSONCreator/TestToggleModel.swift b/JSONCreator_iOS/JSONCreator/TestToggleModel.swift new file mode 100644 index 0000000..bf01592 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/TestToggleModel.swift @@ -0,0 +1,202 @@ +// +// TestToggleModel.swift +// JSONCreator +// +// Created by Matt Bruce on 7/27/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore +import MVMCoreUI +import VDS + +//MARK: - Model +public class TestToggleModel: MoleculeModelProtocol, FormFieldProtocol, VDSToggleModel { + + + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static var identifier: String = "testToggle" + public var moleculeName: String = TestToggleModel.identifier + + public var action: ActionModelProtocol? + public var alternateAction: ActionModelProtocol? + public var accessibilityIdentifier: String? + public var backgroundColor: MVMCoreUI.Color? + + //FormFieldProtocol + public var fieldKey: String? + public var groupName: String = FormValidator.defaultGroupName + public var baseValue: AnyHashable? + public var readOnly: Bool = false + public var enabled: Bool = true + + //ToggleModelProtocol + public var id: String? + public var showText: Bool = false + public var on: Bool = false + public var surface: Surface = .light + public var inputId: String? + public var value: AnyHashable? + public var dataAnalyticsTrack: String? + public var dataClickStream: String? + public var dataTrack: String? + public var disabled: Bool = false + public var accessibilityHintEnabled: String? + public var accessibilityHintDisabled: String? + public var accessibilityValueEnabled: String? + public var accessibilityValueDisabled: String? + public var accessibilityLabelEnabled: String? + public var accessibilityLabelDisabled: String? + public var fontSize: VDSFontSize = .small + public var textPosition: VDSTextPosition = .left + public var fontWeight: VDSFontWeight = .regular + public var offText: String = "Off" + public var onText: String = "On" + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case moleculeName + case action + case alternateAction + case accessibilityIdentifier + case fieldKey + case groupName + case readOnly + case enabled //which to use + + case id + case showText + case on + case surface + case inputId + case value + case dataAnalyticsTrack + case dataClickStream + case dataTrack + case disabled //which to use + case fontSize + case textPosition + case fontWeight + case offText + case onText + } + + //-------------------------------------------------- + // MARK: - Form Valdiation + //-------------------------------------------------- + + public func formFieldValue() -> AnyHashable? { + guard enabled else { return nil } + if let value = value { + return value + } else { + return on + } + } + + //-------------------------------------------------- + // MARK: - Server Value + //-------------------------------------------------- + open func formFieldServerValue() -> AnyHashable? { + return formFieldValue() + } + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + required public convenience init(){ + self.init(false) + } + + public init(_ state: Bool) { + self.on = state + baseValue = state + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + + //molecule + action = try typeContainer.decodeModelIfPresent(codingKey: .action) + alternateAction = try typeContainer.decodeModelIfPresent(codingKey: .alternateAction) + accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) + + //formField + fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) + readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) ?? false + if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { + self.groupName = groupName + } + + //vds toggle + id = try typeContainer.decodeIfPresent(String.self, forKey: .id) + showText = try typeContainer.decodeIfPresent(Bool.self, forKey: .showText) ?? false + on = try typeContainer.decodeIfPresent(Bool.self, forKey: .on) ?? false + + surface = try typeContainer.decodeIfPresent(Surface.self, forKey: .surface) ?? .light + dataAnalyticsTrack = try typeContainer.decodeIfPresent(String.self, forKey: .dataAnalyticsTrack) + dataClickStream = try typeContainer.decodeIfPresent(String.self, forKey: .dataClickStream) + dataTrack = try typeContainer.decodeIfPresent(String.self, forKey: .dataTrack) + fontSize = try typeContainer.decodeIfPresent(VDSFontSize.self, forKey: .fontSize) ?? .small + textPosition = try typeContainer.decodeIfPresent(VDSTextPosition.self, forKey: .textPosition) ?? .left + fontWeight = try typeContainer.decodeIfPresent(VDSFontWeight.self, forKey: .fontWeight) ?? .regular + offText = try typeContainer.decodeIfPresent(String.self, forKey: .offText) ?? "Off" + onText = try typeContainer.decodeIfPresent(String.self, forKey: .onText) ?? "On" + + accessibilityHintEnabled = MVMCoreUIUtility.hardcodedString(withKey: "AccToggleHint") + accessibilityHintDisabled = MVMCoreUIUtility.hardcodedString(withKey: "AccDisabled") + accessibilityValueEnabled = MVMCoreUIUtility.hardcodedString(withKey: "AccOn") + accessibilityValueDisabled = MVMCoreUIUtility.hardcodedString(withKey: "AccOff") + accessibilityLabelEnabled = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") + accessibilityLabelDisabled = accessibilityLabelEnabled + + if let _enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { + enabled = _enabled && !readOnly + disabled = !_enabled && readOnly + } else if let _disabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .disabled) { + enabled = !_disabled && !readOnly + disabled = _disabled && readOnly + } + + baseValue = on + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + //molecule + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeModelIfPresent(action, forKey: .action) + try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) + //formField + try container.encodeIfPresent(fieldKey, forKey: .fieldKey) + try container.encodeIfPresent(groupName, forKey: .groupName) + try container.encode(readOnly, forKey: .readOnly) + try container.encode(enabled, forKey: .enabled) + //vds toggle + try container.encodeIfPresent(id, forKey: .id) + try container.encode(showText, forKey: .showText) + try container.encode(on, forKey: .on) + try container.encodeIfPresent(surface, forKey: .surface) + try container.encodeIfPresent(inputId, forKey: .inputId) + try container.encode(dataAnalyticsTrack, forKey: .dataAnalyticsTrack) + try container.encode(dataClickStream, forKey: .dataClickStream) + try container.encode(dataTrack, forKey: .dataTrack) + try container.encode(disabled, forKey: .disabled) + try container.encodeIfPresent(fontSize, forKey: .fontSize) + try container.encodeIfPresent(textPosition, forKey: .textPosition) + try container.encodeIfPresent(fontWeight, forKey: .fontWeight) + try container.encodeIfPresent(offText, forKey: .offText) + try container.encodeIfPresent(onText, forKey: .onText) + } +} diff --git a/JSONCreator_iOS/JSONCreator/WifiViewController.swift b/JSONCreator_iOS/JSONCreator/WifiViewController.swift new file mode 100644 index 0000000..b617939 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/WifiViewController.swift @@ -0,0 +1,112 @@ +// +// WifiViewController.swift +// JSONCreator +// +// Created by Matt Bruce on 6/27/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCoreUI + +class WifiViewController: UIViewController { +// MFSTLandingViewController *vc=[MFSTLandingViewController new]; +// vc.isForFiveG = true; +// vc.ssIDs = ssIDs; +// MFSTHistoryViewController *vc2=[MFSTHistoryViewController new]; +// vc2.isForFiveG = true; +// NSArray *tabInfo = @[ +// @{ +// @"presentationStyle" : @"push", +// @"itemName" : kST_OVERVIEW, +// @"title" : @"kST_OVERVIEW", +// @"actionType" :@"openPage", +// @"appContext" : @"mobileFirstSS", +// @"pageType" : DHCRegistry.PageTypeDHCLanding +// }, +// @{ +// @"presentationStyle" : @"push", +// @"itemName" : kST_HISTORY, +// @"title" : kST_HISTORY, +// @"actionType" : @"openPage", +// @"appContext" : @"mobileFirstSS", +// @"pageType" : DHCRegistry.PageTypeDHCHistoryLanding +// } +// ]; +// NSError *error = nil; +// MFSubNavManagerController *viewControllerToLoad = [[MFSubNavManagerController alloc] initWithViewControllers:@[vc,vc2] loadObject:vc.loadObject tabsInfo:tabInfo selectedIndex: 0 shouldEnableSwipeGestures:YES error:&error]; +// [[MVMCoreNavigationHandler sharedNavigationHandler] pushViewController:viewControllerToLoad animated:YES]; + +} + +class PrimaryWifiViewController: UIViewController { + let inset = 16.0 + + private lazy var stackView: UIStackView = { + let stack = UIStackView(frame: .zero) + stack.translatesAutoresizingMaskIntoConstraints = false + stack.spacing = 16 + stack.axis = .vertical + stack.alignment = .leading + stack.distribution = .fillProportionally + return stack + }() + + override func viewDidLoad() { + super.viewDidLoad() + + view.addSubview(stackView) + NSLayoutConstraint.constraintPinSubview(stackView, + pinTop: true, topConstant: inset, + pinBottom: true, bottomConstant: inset, + pinLeft: true, leftConstant: inset, + pinRight: true, rightConstant: inset) + + let primary1 = wifiWidget(for: "Primary-1", title: "", description: "", password: "123456") + let primary2 = wifiWidget(for: "Primary-2", title: "", description: "", password: "123456") + let secondary1 = wifiWidget(for: "Secondary-1", title: "", description: "", password: "123456") + let secondary2 = wifiWidget(for: "Secondary-2", title: "", description: "", password: "123456") + + stackView.addArrangedSubview(primary1) + stackView.addArrangedSubview(primary2) + stackView.addArrangedSubview(secondary1) + stackView.addArrangedSubview(secondary2) + } + + private func wifiWidget(for wifiId: String, title: String, description: String, password: String, enabled: Bool = true) -> WifiWidget { + let extraParameters: JSONValueDictionary = ["wifiId": JSONValue.init(stringLiteral: wifiId)] + + let editAction = ActionOpenPageModel(pageType: "editWifi", + presentationStyle: "push", + extraParameters: extraParameters) + + let enabledAction = ActionOpenPageModel(pageType: "enabledWifi", + presentationStyle: "push", + extraParameters: extraParameters) + + let shareAction = ActionShareModel(sharedText: "ssid: \(wifiId) password: \(password)", + sharedType: "Text") + + let wifiWidgetModel = WifiWidgetModel(wifiId: wifiId, + title: title, + description: description, + password: password, + enabled: enabled, + editTitle: "Edit Wi-Fi details", + shareTitle: "Share Wi-Fi", + enabledAction: enabledAction, + editAction: editAction, + shareAction: shareAction, + useHorizontalMargins: true, leftPadding: inset, rightPadding: inset, + useVerticalMargins: true, topPadding: inset, bottomPadding: inset) + + let wifiWidget = WifiWidget(frame: .zero) + wifiWidget.translatesAutoresizingMaskIntoConstraints = false + wifiWidget.set(with: wifiWidgetModel, nil, nil) + + return wifiWidget + } + +} + + diff --git a/JSONCreator_iOS/JSONCreator/WifiWidget.swift b/JSONCreator_iOS/JSONCreator/WifiWidget.swift new file mode 100644 index 0000000..02a889e --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/WifiWidget.swift @@ -0,0 +1,207 @@ +// +// WifiWidgetMolecule.swift +// MVMFrameworksSample +// +// Created by Matt Bruce on 6/17/22. +// + +import Foundation +import UIKit +import MVMCore +import MVMCoreUI + +@objcMembers open class TestLabelToggle: View { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public let label = Label(fontStyle: .BoldBodySmall) + public let toggle = TestToggle() + + //-------------------------------------------------- + // MARK: - MVMCoreViewProtocol + //-------------------------------------------------- + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + label.updateView(size) + toggle.updateView(size) + } + + open override func setupView() { + super.setupView() + + addSubview(label) + addSubview(toggle) + label.setContentHuggingPriority(.required, for: .vertical) + NSLayoutConstraint.pinViews(leftView: label, rightView: toggle, alignTop: false) + } + + open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + guard let model = model as? TestLabelToggleModel, + let toggleHeight = Toggle.estimatedHeight(with: model.toggle, delegateObject), + let labelHeight = Label.estimatedHeight(with: model.label, delegateObject) + else { return nil } + + return max(toggleHeight, labelHeight) + } + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + + guard let labelToggleModel = model as? TestLabelToggleModel else { return } + + label.set(with: labelToggleModel.label, delegateObject, additionalData) + toggle.set(with: labelToggleModel.toggle, delegateObject, additionalData) + } + + // MARK: - MoleculeViewProtocol + open override func reset() { + super.reset() + label.reset() + toggle.reset() + label.setFontStyle(.BoldBodySmall) + } +} + +public class TestLabelToggleModel: MoleculeModelProtocol { + public static var identifier: String = "testLabelToggle" + public var moleculeName: String = TestLabelToggleModel.identifier + public var backgroundColor: Color? + public var label: LabelModel + public var toggle: TestToggleModel + + public init(_ label: LabelModel, _ toggle: TestToggleModel) { + self.label = label + self.toggle = toggle + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case label + case toggle + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey:.backgroundColor) + label = try typeContainer.decode(LabelModel.self, forKey:.label) + toggle = try typeContainer.decode(TestToggleModel.self, forKey:.toggle) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encode(label, forKey: .label) + try container.encode(toggle, forKey: .toggle) + } +} + + +public class WifiWidget: View { + //-------------------------------------------------- + // MARK: - Outlets + //-------------------------------------------------- + public private(set) var titleLabelToggle: TestLabelToggle = { + let tlt = TestLabelToggle() + tlt.label.setFontStyle(.BoldBodyLarge) + return tlt + }() + public let descriptionLabel = Label(fontStyle: .RegularMicro) + public let passwordTextField = TextEntryField() + public let editTitleLink = Link() + public let shareTitleLink = Link() + + private lazy var stack1: Stack = { + return Stack.createStack(with: [titleLabelToggle, descriptionLabel], + axis: .vertical, spacing: Padding.Two) + }() + + private lazy var stack2: Stack = { + return Stack.createStack(with: [ + (view: editTitleLink, model: StackItemModel()), + (view: shareTitleLink, model: StackItemModel(horizontalAlignment: .trailing)) + ], + axis: .horizontal, + spacing: Padding.Two) + }() + + private var row2 = StackItemModel() + private var row3 = StackItemModel() + + private lazy var stack: Stack = { + let model = StackModel(molecules: [StackItemModel(), row2, row3], + axis: .vertical, + spacing: Padding.Four) + let stackItems = [StackItem(andContain:stack1), StackItem(andContain: passwordTextField), StackItem(andContain:stack2)] + return Stack(with: model, stackItems: stackItems) + }() + + //-------------------------------------------------- + // MARK: - Lifecycle + //-------------------------------------------------- + + /// Initial configuration of class and view. + @objc final public override func setupView() { + super.setupView() + + isAccessibilityElement = false + setContentCompressionResistancePriority(.required, for: .vertical) + accessibilityElements = [titleLabelToggle.label, descriptionLabel, editTitleLink, shareTitleLink] + + stack1.restack() + stack2.restack() + stack.restack() + + stack.backgroundColor = .clear + addSubview(stack) + + NSLayoutConstraint.constraintPinSubview(stack, + pinTop: true, topConstant: 16, + pinBottom: true, bottomConstant: 16, + pinLeft: true, leftConstant: 16, + pinRight: true, rightConstant: 16) + } + + open override func reset() { + super.reset() + stack.reset() + } + + open func restack(){ + stack1.restack() + stack2.restack() + stack.restack() + } + + open override func updateView(_ size: CGFloat) { + super.updateView(size) + stack.updateView(size) + } + + //------------------------------------------------------ + // MARK: - Atomization + //------------------------------------------------------ + + open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { + super.set(with: model, delegateObject, additionalData) + + guard let model = model as? WifiWidgetModel else { return } + backgroundColor = model.backgroundColor?.uiColor ?? .mvmCoolGray1 + + //Password Text Field Model + passwordTextField.set(with: model.passwordModel, delegateObject, additionalData) + + //Update the Stacks with the models + stack1.updateContainedMolecules(with: [model.titleModel, model.descriptionModel], delegateObject, additionalData) + stack2.updateContainedMolecules(with: [model.editModel, model.shareModel], delegateObject, additionalData) + + //show/hide 2 and 3 rows + row2.gone = !model.enabled + row3.gone = !model.enabled + stack.restack() + } +} + + diff --git a/JSONCreator_iOS/JSONCreator/WifiWidgetModel.swift b/JSONCreator_iOS/JSONCreator/WifiWidgetModel.swift new file mode 100644 index 0000000..fe739d0 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/WifiWidgetModel.swift @@ -0,0 +1,115 @@ +// +// WifiWidgetModel.swift +// MVMFrameworksSample +// +// Created by Matt Bruce on 6/17/22. +// + +import Foundation +import MVMCore +import MVMCoreUI + +public class WifiWidgetModel: ContainerModel, MoleculeModelProtocol { + //MoleculeModelProtocol + public var backgroundColor: Color? + public static var identifier: String { "wifiWidget" } + public var moleculeName: String = WifiWidgetModel.identifier + + //UI Bound Properties + public var wifiId: String + public var title: String + public var description: String + public var password: String + public var enabled: Bool = false + public var editTitle: String + public var shareTitle: String + + //Click Actions + public var enabledAction: ActionModelProtocol + public var editAction: ActionModelProtocol + public var shareAction: ActionModelProtocol + + public init(wifiId: String, title: String, description: String, password: String, enabled: Bool, editTitle: String, shareTitle: String, enabledAction: ActionModelProtocol, editAction: ActionModelProtocol, shareAction: ActionModelProtocol, + horizontalAlignment: UIStackView.Alignment? = nil, verticalAlignment: UIStackView.Alignment? = nil, useHorizontalMargins: Bool? = nil, leftPadding: CGFloat? = nil, rightPadding: CGFloat? = nil, useVerticalMargins: Bool? = nil, topPadding: CGFloat? = nil, bottomPadding: CGFloat? = nil) { + + self.wifiId = wifiId + self.title = title + self.description = description + self.password = password + self.enabled = enabled + self.editTitle = editTitle + self.shareTitle = shareTitle + self.enabledAction = enabledAction + self.editAction = editAction + self.shareAction = shareAction + super.init(horizontalAlignment: horizontalAlignment, verticalAlignment: verticalAlignment, useHorizontalMargins: useHorizontalMargins, leftPadding: leftPadding, rightPadding: rightPadding, useVerticalMargins: useVerticalMargins, topPadding: topPadding, bottomPadding: bottomPadding) + } + + private enum CodingKeys: String, CodingKey { + case moleculeName + case backgroundColor + case wifiId, title, description, password, enabled, editTitle, shareTitle + case enabledAction, editAction, shareAction + } + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) + wifiId = try typeContainer.decode(String.self, forKey: .wifiId) + title = try typeContainer.decode(String.self, forKey: .title) + description = try typeContainer.decode(String.self, forKey: .description) + password = try typeContainer.decode(String.self, forKey: .password) + enabled = try typeContainer.decode(Bool.self, forKey: .enabled) + editTitle = try typeContainer.decode(String.self, forKey: .editTitle) + shareTitle = try typeContainer.decode(String.self, forKey: .shareTitle) + enabledAction = try typeContainer.decodeModel(codingKey: .enabledAction) + editAction = try typeContainer.decodeModel(codingKey: .editAction) + shareAction = try typeContainer.decodeModel(codingKey: .shareAction) + try super.init(from: decoder) + } + + public override func encode(to encoder: Encoder) throws { + try super.encode(to: encoder) + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(wifiId, forKey: .wifiId) + try container.encode(title, forKey: .title) + try container.encode(description, forKey: .description) + try container.encode(password, forKey: .password) + try container.encode(enabled, forKey: .enabled) + try container.encode(editTitle, forKey: .editTitle) + try container.encode(shareTitle, forKey: .shareTitle) + try container.encodeModel(enabledAction, forKey: .enabledAction) + try container.encodeModel(editAction, forKey: .editAction) + try container.encodeModel(shareAction, forKey: .shareAction) + } + +} + +extension WifiWidgetModel { + + public var titleModel: TestLabelToggleModel { + let labelModel = LabelModel(text: title) + let toggleModel = TestToggleModel(enabled) + toggleModel.action = enabledAction + toggleModel.showText = true + return TestLabelToggleModel(labelModel, toggleModel) + } + + public var passwordModel: TextEntryFieldModel { + let model = TextEntryFieldModel(with: password) + model.fieldKey = "preferredLastName" + model.type = .password + model.readOnly = true + return model + } + + public var descriptionModel: LabelModel { LabelModel(text: description) } + public var editModel: LinkModel { LinkModel(title: editTitle, action: editAction) } + public var shareModel: LinkModel { LinkModel(title: shareTitle, action: shareAction) } + +} + + + From 5a54eab41e5497612b0a9ebecde62c298b530f98 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 21 Oct 2022 08:18:24 -0500 Subject: [PATCH 06/15] update vds Signed-off-by: Matt Bruce --- .gitignore | 5 + .../contents.xcworkspacedata | 5 +- .../JSONCreator.xcodeproj/project.pbxproj | 24 -- .../xcschemes/JSONCreator.xcscheme | 78 +++++++ JSONCreator_iOS/JSONCreator/AppDelegate.swift | 14 -- .../JSONCreator/DecodableDefaults+VDS.swift | 117 ---------- JSONCreator_iOS/JSONCreator/TestToggle.swift | 101 --------- .../JSONCreator/TestToggleModel.swift | 202 ----------------- .../JSONCreator/WifiViewController.swift | 112 ---------- JSONCreator_iOS/JSONCreator/WifiWidget.swift | 207 ------------------ .../JSONCreator/WifiWidgetModel.swift | 115 ---------- 11 files changed, 84 insertions(+), 896 deletions(-) create mode 100644 JSONCreator_iOS/JSONCreator.xcodeproj/xcshareddata/xcschemes/JSONCreator.xcscheme delete mode 100644 JSONCreator_iOS/JSONCreator/DecodableDefaults+VDS.swift delete mode 100644 JSONCreator_iOS/JSONCreator/TestToggle.swift delete mode 100644 JSONCreator_iOS/JSONCreator/TestToggleModel.swift delete mode 100644 JSONCreator_iOS/JSONCreator/WifiViewController.swift delete mode 100644 JSONCreator_iOS/JSONCreator/WifiWidget.swift delete mode 100644 JSONCreator_iOS/JSONCreator/WifiWidgetModel.swift diff --git a/.gitignore b/.gitignore index ef6af7e..6cbbf79 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ mvm_core mfprepayshop_ios mvm_core_ui mvmreactnative +vds_ios # frameworks contentTransferFramework.framework @@ -24,6 +25,10 @@ VZWAuthentication.framework CardinalMobile.framework MVDRetailFramework.framework +VDSTypographyTokens.framework +VDSFormControlsTokens.framework +VDSColorTokens.framework + __MACOSX # Xcode diff --git a/JSONCreator.xcworkspace/contents.xcworkspacedata b/JSONCreator.xcworkspace/contents.xcworkspacedata index a714f9d..40a48cc 100644 --- a/JSONCreator.xcworkspace/contents.xcworkspacedata +++ b/JSONCreator.xcworkspace/contents.xcworkspacedata @@ -2,10 +2,7 @@ - - + location = "group:vds_ios/VDS.xcodeproj"> diff --git a/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj b/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj index 2f12d6d..88447c8 100644 --- a/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj +++ b/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj @@ -69,20 +69,14 @@ EA09CE01282C43E800A7835F /* KeyedDecodingContainer+Decode.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CE00282C43E800A7835F /* KeyedDecodingContainer+Decode.swift */; }; EA09CE03282C44A100A7835F /* MFFGHSUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CE02282C44A100A7835F /* MFFGHSUtility.swift */; }; EA09CE05282C45C200A7835F /* BluetoothPairBehavior.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA09CE04282C45C200A7835F /* BluetoothPairBehavior.swift */; }; - EA1B7BBD2893459E006AF0BC /* DecodableDefaults+VDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1B7BBC2893459E006AF0BC /* DecodableDefaults+VDS.swift */; }; EA33618B288B1B630071C351 /* VDS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA33618A288B1B630071C351 /* VDS.framework */; }; EA33618C288B1B630071C351 /* VDS.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EA33618A288B1B630071C351 /* VDS.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - EA3361C1288B37FB0071C351 /* TestToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361C0288B37FB0071C351 /* TestToggle.swift */; }; EA3361FB2891D54A0071C351 /* VDSTypographyTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA3361FA2891D54A0071C351 /* VDSTypographyTokens.xcframework */; }; EA3361FC2891D54A0071C351 /* VDSTypographyTokens.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EA3361FA2891D54A0071C351 /* VDSTypographyTokens.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - EA3362342891F5AB0071C351 /* TestToggleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3362332891F5AB0071C351 /* TestToggleModel.swift */; }; - EA3E48A62860BB4D00B524AB /* WifiWidgetModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3E48A52860BB4D00B524AB /* WifiWidgetModel.swift */; }; - EA3E48A82860BB9800B524AB /* WifiWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3E48A72860BB9800B524AB /* WifiWidget.swift */; }; EA5B696E2866BC1000B17D2E /* MVMCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA5B696C2866BC1000B17D2E /* MVMCore.framework */; }; EA5B696F2866BC1000B17D2E /* MVMCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EA5B696C2866BC1000B17D2E /* MVMCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; EA5B69702866BC1000B17D2E /* MVMCoreUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA5B696D2866BC1000B17D2E /* MVMCoreUI.framework */; }; EA5B69712866BC1000B17D2E /* MVMCoreUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EA5B696D2866BC1000B17D2E /* MVMCoreUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - EAA54A92286A47ED00B9136B /* WifiViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA54A91286A47ED00B9136B /* WifiViewController.swift */; }; EAA658152875FA5E00484A7D /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA658142875FA5E00484A7D /* VDSFormControlsTokens.xcframework */; }; EAA658162875FA5E00484A7D /* VDSFormControlsTokens.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EAA658142875FA5E00484A7D /* VDSFormControlsTokens.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; EACA5E5E2853DBC900CBA65B /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EACA5E5D2853DBC900CBA65B /* VDSColorTokens.xcframework */; }; @@ -162,22 +156,16 @@ EA09CE00282C43E800A7835F /* KeyedDecodingContainer+Decode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "KeyedDecodingContainer+Decode.swift"; sourceTree = ""; }; EA09CE02282C44A100A7835F /* MFFGHSUtility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MFFGHSUtility.swift; sourceTree = ""; }; EA09CE04282C45C200A7835F /* BluetoothPairBehavior.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothPairBehavior.swift; sourceTree = ""; }; - EA1B7BBC2893459E006AF0BC /* DecodableDefaults+VDS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DecodableDefaults+VDS.swift"; sourceTree = ""; }; EA2ED278285BB3F400781478 /* MVMCoreUI.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MVMCoreUI.xcframework; path = ../SharedFrameworks/MVMCoreUI.xcframework; sourceTree = ""; }; EA2ED279285BB3F400781478 /* MVMCore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = MVMCore.xcframework; path = ../SharedFrameworks/MVMCore.xcframework; sourceTree = ""; }; EA2ED27E285BD00D00781478 /* MVMCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA2ED27F285BD00D00781478 /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA33618A288B1B630071C351 /* VDS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = VDS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - EA3361C0288B37FB0071C351 /* TestToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestToggle.swift; sourceTree = ""; }; EA3361FA2891D54A0071C351 /* VDSTypographyTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSTypographyTokens.xcframework; path = ../SharedFrameworks/VDSTypographyTokens.xcframework; sourceTree = ""; }; - EA3362332891F5AB0071C351 /* TestToggleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestToggleModel.swift; sourceTree = ""; }; - EA3E48A52860BB4D00B524AB /* WifiWidgetModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiWidgetModel.swift; sourceTree = ""; }; - EA3E48A72860BB9800B524AB /* WifiWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiWidget.swift; sourceTree = ""; }; EA5B696C2866BC1000B17D2E /* MVMCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA5B696D2866BC1000B17D2E /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA7E676927582F2200ABF773 /* MVMCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA7E676A27582F2200ABF773 /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - EAA54A91286A47ED00B9136B /* WifiViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiViewController.swift; sourceTree = ""; }; EAA658142875FA5E00484A7D /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = ""; }; EACA5E5D2853DBC900CBA65B /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = ""; }; /* End PBXFileReference section */ @@ -240,12 +228,6 @@ D2B1E3FB22F4A6930065F95C /* Assets.xcassets */, D2B1E3FD22F4A6930065F95C /* LaunchScreen.storyboard */, D2B1E40022F4A6930065F95C /* Info.plist */, - EA3E48A52860BB4D00B524AB /* WifiWidgetModel.swift */, - EA3E48A72860BB9800B524AB /* WifiWidget.swift */, - EAA54A91286A47ED00B9136B /* WifiViewController.swift */, - EA3362332891F5AB0071C351 /* TestToggleModel.swift */, - EA3361C0288B37FB0071C351 /* TestToggle.swift */, - EA1B7BBC2893459E006AF0BC /* DecodableDefaults+VDS.swift */, ); path = JSONCreator; sourceTree = ""; @@ -473,7 +455,6 @@ buildActionMask = 2147483647; files = ( EA09CDFC282C430400A7835F /* CharacteristicModel.swift in Sources */, - EA3E48A82860BB9800B524AB /* WifiWidget.swift in Sources */, D2B1E3F722F4A68F0065F95C /* DetailViewController.swift in Sources */, EA09CDDD282C40CC00A7835F /* GMFGSpeedTestHandler.swift in Sources */, EA09CDDF282C40CC00A7835F /* GMFGRouterWifiHandler.swift in Sources */, @@ -482,9 +463,7 @@ EA09CDDE282C40CC00A7835F /* GMFG5GSignalHandler.swift in Sources */, EA09CDD1282C40CC00A7835F /* MFFGHSBluetoothPair.swift in Sources */, EA09CDEB282C422900A7835F /* GMFGStorageManager.swift in Sources */, - EA1B7BBD2893459E006AF0BC /* DecodableDefaults+VDS.swift in Sources */, EA09CE05282C45C200A7835F /* BluetoothPairBehavior.swift in Sources */, - EAA54A92286A47ED00B9136B /* WifiViewController.swift in Sources */, EA09CDFA282C430400A7835F /* BluetoothConfigModel.swift in Sources */, D27564CA25939E91003CA713 /* LinksModel.swift in Sources */, D2B1E3F522F4A68F0065F95C /* MasterViewController.swift in Sources */, @@ -495,7 +474,6 @@ EA09CDF9282C430400A7835F /* BluetoothPairableProtocol.swift in Sources */, D27564B72590FADB003CA713 /* ListDeviceRightVariableCaret.swift in Sources */, D2431DEB25E93A4F001C7AAC /* buttimag.swift in Sources */, - EA3361C1288B37FB0071C351 /* TestToggle.swift in Sources */, D27564C825939E91003CA713 /* LinkCollectionViewCell.swift in Sources */, EA09CDFB282C430400A7835F /* PeripheralModel.swift in Sources */, D2FC4FB025897ACB00061EA4 /* OrderTracker.swift in Sources */, @@ -504,11 +482,9 @@ EA09CDD6282C40CC00A7835F /* GMFGConstant.swift in Sources */, D27564C925939E91003CA713 /* Links.swift in Sources */, EA09CDE6282C416C00A7835F /* BluetoothDebuggableProtocol.swift in Sources */, - EA3E48A62860BB4D00B524AB /* WifiWidgetModel.swift in Sources */, EA09CDD2282C40CC00A7835F /* GMFGBluetoothPair.swift in Sources */, EA09CDE9282C416C00A7835F /* BluetoothDebuggerView.swift in Sources */, D2FC4FAE25897ACB00061EA4 /* OrderTrackerModel.swift in Sources */, - EA3362342891F5AB0071C351 /* TestToggleModel.swift in Sources */, D21B3A27259B93ED001483DC /* SelfSizingCollectionView.swift in Sources */, EA09CDD9282C40CC00A7835F /* GMFG5GCBandSignalHandler.swift in Sources */, EA09CDF8282C430400A7835F /* BluetoothPairingProtocol.swift in Sources */, diff --git a/JSONCreator_iOS/JSONCreator.xcodeproj/xcshareddata/xcschemes/JSONCreator.xcscheme b/JSONCreator_iOS/JSONCreator.xcodeproj/xcshareddata/xcschemes/JSONCreator.xcscheme new file mode 100644 index 0000000..39d34d2 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator.xcodeproj/xcshareddata/xcschemes/JSONCreator.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/JSONCreator_iOS/JSONCreator/AppDelegate.swift b/JSONCreator_iOS/JSONCreator/AppDelegate.swift index 042ac17..8becbf4 100644 --- a/JSONCreator_iOS/JSONCreator/AppDelegate.swift +++ b/JSONCreator_iOS/JSONCreator/AppDelegate.swift @@ -130,11 +130,8 @@ extension AppDelegate: MVMCoreGlobalTopAlertDelegateProtocol { extension AppDelegate { func register(){ - ModelRegistry.register(handler: TestLabelToggle.self, for: TestLabelToggleModel.self) - ModelRegistry.register(handler: TestToggle.self, for: TestToggleModel.self) ModelRegistry.register(handler: TextEntryField.self, for: TextEntryField64Model.self) ModelRegistry.register(handler: EmailVerifyField.self, for: EmailVerifyModel.self) - ModelRegistry.register(handler: WifiWidget.self, for: WifiWidgetModel.self) ModelRegistry.register(handler: ToggleWifiActionHandler.self, for: ToggleWifiActionModel.self) } } @@ -254,17 +251,6 @@ class ToggleWifiActionModel: ActionModelProtocol { } } -class ToggleWifiActionHandler: MVMCoreActionHandlerProtocol { - required public init() {} - - func handleAction(_ model: ActionModelProtocol, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) { - guard let action = model as? ToggleWifiActionModel else { return } - - print("Wi-Fi Id: \(action.wifiId)") - - } -} - class TextEntryField64Model: TextEntryFieldModel { open override class var identifier: String { "textFieldBase64" } diff --git a/JSONCreator_iOS/JSONCreator/DecodableDefaults+VDS.swift b/JSONCreator_iOS/JSONCreator/DecodableDefaults+VDS.swift deleted file mode 100644 index bb23af3..0000000 --- a/JSONCreator_iOS/JSONCreator/DecodableDefaults+VDS.swift +++ /dev/null @@ -1,117 +0,0 @@ -// -// DecodableDefaults+VDS.swift -// JSONCreator -// -// Created by Matt Bruce on 7/28/22. -// Copyright © 2022 Verizon Wireless. All rights reserved. -// - -import Foundation -import VDS -import MVMCore - -//MARK: - Decodable Defaults -extension VDSFontCategory { - public enum DefaultFeature: DecodableDefault.Source { - public static var defaultValue: VDSFontCategory { .feature } - } - public enum DefaultTitle: DecodableDefault.Source { - public static var defaultValue: VDSFontCategory { .title } - } - public enum DefaultBody: DecodableDefault.Source { - public static var defaultValue: VDSFontCategory { .body } - } - public enum DefaultMicro: DecodableDefault.Source { - public static var defaultValue: VDSFontCategory { .micro } - } -} - -extension VDSFontSize { - public enum Default2XLarge: DecodableDefault.Source { - public static var defaultValue: VDSFontSize { .xxlarge } - } - public enum DefaultXLarge: DecodableDefault.Source { - public static var defaultValue: VDSFontSize { .xlarge } - } - public enum DefaultLarge: DecodableDefault.Source { - public static var defaultValue: VDSFontSize { .large } - } - public enum DefaultMedium: DecodableDefault.Source { - public static var defaultValue: VDSFontSize { .medium } - } - public enum DefaultSmall: DecodableDefault.Source { - public static var defaultValue: VDSFontSize { .small } - } - public enum DefaultXSmall: DecodableDefault.Source { - public static var defaultValue: VDSFontSize { .xsmall } - } -} - -extension VDSTextPosition { - public enum DefaultLeft: DecodableDefault.Source { - public static var defaultValue: VDSTextPosition { .left } - } - public enum DefaultRight: DecodableDefault.Source { - public static var defaultValue: VDSTextPosition { .right } - } - public enum DefaultCenter: DecodableDefault.Source { - public static var defaultValue: VDSTextPosition { .center } - } -} - -extension VDSFontWeight { - public enum DefaultBold: DecodableDefault.Source { - public static var defaultValue: VDSFontWeight { .bold } - } - public enum DefaultRegular: DecodableDefault.Source { - public static var defaultValue: VDSFontWeight { .regular } - } -} - -extension VDSToggle { - public enum DefaultOffText: DecodableDefault.Source { - public static var defaultValue: String { "Off" } - } - public enum DefaultOnText: DecodableDefault.Source { - public static var defaultValue: String { "On" } - } -} - -extension Surface { - public enum DefaultLight: DecodableDefault.Source { - public static var defaultValue: Surface { .light } - } - public enum DefaultDark: DecodableDefault.Source { - public static var defaultValue: Surface { .dark } - } -} - -extension DecodableDefault { - - public struct Surface { - public typealias Light = DecodableDefault.Wrapper - public typealias Dark = DecodableDefault.Wrapper - } - - public struct VDSTypography { - public typealias FontCategoryFeature = DecodableDefault.Wrapper - public typealias FontCategoryTitle = DecodableDefault.Wrapper - public typealias FontCategoryBody = DecodableDefault.Wrapper - public typealias FontCategoryMicro = DecodableDefault.Wrapper - public typealias FontWeightBold = DecodableDefault.Wrapper - public typealias FontWeightRegular = DecodableDefault.Wrapper - public typealias FontSize2XLarge = DecodableDefault.Wrapper - public typealias FontSizeXLarge = DecodableDefault.Wrapper - public typealias FontSizeLarge = DecodableDefault.Wrapper - public typealias FontSizeMedium = DecodableDefault.Wrapper - public typealias FontSizeSmall = DecodableDefault.Wrapper - public typealias FontSizeXSmall = DecodableDefault.Wrapper - public typealias TextPositionLeft = DecodableDefault.Wrapper - public typealias TextPositionRight = DecodableDefault.Wrapper - public typealias TextPositionCenter = DecodableDefault.Wrapper - } -// public struct VDSToggle { -// public typealias OffText = DecodableDefault.Wrapper -// public typealias OnText = DecodableDefault.Wrapper -// } -} diff --git a/JSONCreator_iOS/JSONCreator/TestToggle.swift b/JSONCreator_iOS/JSONCreator/TestToggle.swift deleted file mode 100644 index c9055ca..0000000 --- a/JSONCreator_iOS/JSONCreator/TestToggle.swift +++ /dev/null @@ -1,101 +0,0 @@ -// -// Toggle.swift -// MVMCoreUI -// -// Created by Kevin Christiano on 12/4/19. -// Copyright © 2019 Verizon Wireless. All rights reserved. -// - -import MVMCore -import MVMCoreUI -import UIKit -import VDS - -extension MoleculeViewProtocol { - public func onModelChange(model: MoleculeModelProtocol) { - if let backgroundColor = model.backgroundColor { - self.backgroundColor = backgroundColor.uiColor - } - - if let accessibilityIdentifier = model.accessibilityIdentifier { - self.accessibilityIdentifier = accessibilityIdentifier - } - } -} - -extension Modelable where Self: MoleculeViewProtocol { - public init(model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { - self.init(with: model as! ModelType) - self.set(with: model, delegateObject, additionalData) - } -} - -open class TestToggle: VDSToggleBase, MoleculeViewProtocol, MVMCoreViewProtocol { - - /// The state on the toggle. Default value: false. - open override var isOn: Bool { - didSet { - _ = FormValidator.validate(delegate: delegateObject?.formHolderDelegate) - } - } - - open override var isEnabled: Bool { - didSet { - model.enabled = isEnabled && !model.readOnly - } - } - - //-------------------------------------------------- - // MARK: - Delegate - //-------------------------------------------------- - - private var delegateObject: MVMCoreUIDelegateObject? - - //-------------------------------------------------- - // MARK: - Initializers - //-------------------------------------------------- - open override func onStateChange(viewModel: ModelType) { - super.onStateChange(viewModel: viewModel) - onModelChange(model: viewModel) - } - - // MARK:- MoleculeViewProtocol - public func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - - guard let castedModel = model as? ModelType else { return } - set(with: castedModel) - - self.delegateObject = delegateObject - - guard let formFieldProtocol = model as? FormFieldProtocol else { return } - FormValidator.setupValidation(for: formFieldProtocol, delegate: delegateObject?.formHolderDelegate) - - let additionalDataWithSource = additionalData.dictionaryAdding(key: KeySourceModel, value: model) - if castedModel.action != nil || castedModel.alternateAction != nil { - onChange = { [weak self] in - guard let self = self else { return } - if self.isOn { - if let action = castedModel.action { - MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: additionalDataWithSource, delegateObject: delegateObject) - } - } else { - if let action = castedModel.alternateAction ?? castedModel.action { - MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: additionalDataWithSource, delegateObject: delegateObject) - } - } - } - } - } - - public static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return 24 - } -} - -// MARK: - MVMCoreUIViewConstrainingProtocol -extension TestToggle: MVMCoreUIViewConstrainingProtocol { - - public func needsToBeConstrained() -> Bool { true } - - public func horizontalAlignment() -> UIStackView.Alignment { .trailing } -} diff --git a/JSONCreator_iOS/JSONCreator/TestToggleModel.swift b/JSONCreator_iOS/JSONCreator/TestToggleModel.swift deleted file mode 100644 index bf01592..0000000 --- a/JSONCreator_iOS/JSONCreator/TestToggleModel.swift +++ /dev/null @@ -1,202 +0,0 @@ -// -// TestToggleModel.swift -// JSONCreator -// -// Created by Matt Bruce on 7/27/22. -// Copyright © 2022 Verizon Wireless. All rights reserved. -// - -import Foundation -import MVMCore -import MVMCoreUI -import VDS - -//MARK: - Model -public class TestToggleModel: MoleculeModelProtocol, FormFieldProtocol, VDSToggleModel { - - - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - - public static var identifier: String = "testToggle" - public var moleculeName: String = TestToggleModel.identifier - - public var action: ActionModelProtocol? - public var alternateAction: ActionModelProtocol? - public var accessibilityIdentifier: String? - public var backgroundColor: MVMCoreUI.Color? - - //FormFieldProtocol - public var fieldKey: String? - public var groupName: String = FormValidator.defaultGroupName - public var baseValue: AnyHashable? - public var readOnly: Bool = false - public var enabled: Bool = true - - //ToggleModelProtocol - public var id: String? - public var showText: Bool = false - public var on: Bool = false - public var surface: Surface = .light - public var inputId: String? - public var value: AnyHashable? - public var dataAnalyticsTrack: String? - public var dataClickStream: String? - public var dataTrack: String? - public var disabled: Bool = false - public var accessibilityHintEnabled: String? - public var accessibilityHintDisabled: String? - public var accessibilityValueEnabled: String? - public var accessibilityValueDisabled: String? - public var accessibilityLabelEnabled: String? - public var accessibilityLabelDisabled: String? - public var fontSize: VDSFontSize = .small - public var textPosition: VDSTextPosition = .left - public var fontWeight: VDSFontWeight = .regular - public var offText: String = "Off" - public var onText: String = "On" - //-------------------------------------------------- - // MARK: - Keys - //-------------------------------------------------- - - private enum CodingKeys: String, CodingKey { - case moleculeName - case action - case alternateAction - case accessibilityIdentifier - case fieldKey - case groupName - case readOnly - case enabled //which to use - - case id - case showText - case on - case surface - case inputId - case value - case dataAnalyticsTrack - case dataClickStream - case dataTrack - case disabled //which to use - case fontSize - case textPosition - case fontWeight - case offText - case onText - } - - //-------------------------------------------------- - // MARK: - Form Valdiation - //-------------------------------------------------- - - public func formFieldValue() -> AnyHashable? { - guard enabled else { return nil } - if let value = value { - return value - } else { - return on - } - } - - //-------------------------------------------------- - // MARK: - Server Value - //-------------------------------------------------- - open func formFieldServerValue() -> AnyHashable? { - return formFieldValue() - } - - //-------------------------------------------------- - // MARK: - Initializer - //-------------------------------------------------- - required public convenience init(){ - self.init(false) - } - - public init(_ state: Bool) { - self.on = state - baseValue = state - } - - //-------------------------------------------------- - // MARK: - Codec - //-------------------------------------------------- - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - - //molecule - action = try typeContainer.decodeModelIfPresent(codingKey: .action) - alternateAction = try typeContainer.decodeModelIfPresent(codingKey: .alternateAction) - accessibilityIdentifier = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityIdentifier) - - //formField - fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) - readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) ?? false - if let groupName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { - self.groupName = groupName - } - - //vds toggle - id = try typeContainer.decodeIfPresent(String.self, forKey: .id) - showText = try typeContainer.decodeIfPresent(Bool.self, forKey: .showText) ?? false - on = try typeContainer.decodeIfPresent(Bool.self, forKey: .on) ?? false - - surface = try typeContainer.decodeIfPresent(Surface.self, forKey: .surface) ?? .light - dataAnalyticsTrack = try typeContainer.decodeIfPresent(String.self, forKey: .dataAnalyticsTrack) - dataClickStream = try typeContainer.decodeIfPresent(String.self, forKey: .dataClickStream) - dataTrack = try typeContainer.decodeIfPresent(String.self, forKey: .dataTrack) - fontSize = try typeContainer.decodeIfPresent(VDSFontSize.self, forKey: .fontSize) ?? .small - textPosition = try typeContainer.decodeIfPresent(VDSTextPosition.self, forKey: .textPosition) ?? .left - fontWeight = try typeContainer.decodeIfPresent(VDSFontWeight.self, forKey: .fontWeight) ?? .regular - offText = try typeContainer.decodeIfPresent(String.self, forKey: .offText) ?? "Off" - onText = try typeContainer.decodeIfPresent(String.self, forKey: .onText) ?? "On" - - accessibilityHintEnabled = MVMCoreUIUtility.hardcodedString(withKey: "AccToggleHint") - accessibilityHintDisabled = MVMCoreUIUtility.hardcodedString(withKey: "AccDisabled") - accessibilityValueEnabled = MVMCoreUIUtility.hardcodedString(withKey: "AccOn") - accessibilityValueDisabled = MVMCoreUIUtility.hardcodedString(withKey: "AccOff") - accessibilityLabelEnabled = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") - accessibilityLabelDisabled = accessibilityLabelEnabled - - if let _enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) { - enabled = _enabled && !readOnly - disabled = !_enabled && readOnly - } else if let _disabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .disabled) { - enabled = !_disabled && !readOnly - disabled = _disabled && readOnly - } - - baseValue = on - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - //molecule - try container.encode(moleculeName, forKey: .moleculeName) - try container.encodeModelIfPresent(action, forKey: .action) - try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction) - try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) - //formField - try container.encodeIfPresent(fieldKey, forKey: .fieldKey) - try container.encodeIfPresent(groupName, forKey: .groupName) - try container.encode(readOnly, forKey: .readOnly) - try container.encode(enabled, forKey: .enabled) - //vds toggle - try container.encodeIfPresent(id, forKey: .id) - try container.encode(showText, forKey: .showText) - try container.encode(on, forKey: .on) - try container.encodeIfPresent(surface, forKey: .surface) - try container.encodeIfPresent(inputId, forKey: .inputId) - try container.encode(dataAnalyticsTrack, forKey: .dataAnalyticsTrack) - try container.encode(dataClickStream, forKey: .dataClickStream) - try container.encode(dataTrack, forKey: .dataTrack) - try container.encode(disabled, forKey: .disabled) - try container.encodeIfPresent(fontSize, forKey: .fontSize) - try container.encodeIfPresent(textPosition, forKey: .textPosition) - try container.encodeIfPresent(fontWeight, forKey: .fontWeight) - try container.encodeIfPresent(offText, forKey: .offText) - try container.encodeIfPresent(onText, forKey: .onText) - } -} diff --git a/JSONCreator_iOS/JSONCreator/WifiViewController.swift b/JSONCreator_iOS/JSONCreator/WifiViewController.swift deleted file mode 100644 index b617939..0000000 --- a/JSONCreator_iOS/JSONCreator/WifiViewController.swift +++ /dev/null @@ -1,112 +0,0 @@ -// -// WifiViewController.swift -// JSONCreator -// -// Created by Matt Bruce on 6/27/22. -// Copyright © 2022 Verizon Wireless. All rights reserved. -// - -import Foundation -import MVMCoreUI - -class WifiViewController: UIViewController { -// MFSTLandingViewController *vc=[MFSTLandingViewController new]; -// vc.isForFiveG = true; -// vc.ssIDs = ssIDs; -// MFSTHistoryViewController *vc2=[MFSTHistoryViewController new]; -// vc2.isForFiveG = true; -// NSArray *tabInfo = @[ -// @{ -// @"presentationStyle" : @"push", -// @"itemName" : kST_OVERVIEW, -// @"title" : @"kST_OVERVIEW", -// @"actionType" :@"openPage", -// @"appContext" : @"mobileFirstSS", -// @"pageType" : DHCRegistry.PageTypeDHCLanding -// }, -// @{ -// @"presentationStyle" : @"push", -// @"itemName" : kST_HISTORY, -// @"title" : kST_HISTORY, -// @"actionType" : @"openPage", -// @"appContext" : @"mobileFirstSS", -// @"pageType" : DHCRegistry.PageTypeDHCHistoryLanding -// } -// ]; -// NSError *error = nil; -// MFSubNavManagerController *viewControllerToLoad = [[MFSubNavManagerController alloc] initWithViewControllers:@[vc,vc2] loadObject:vc.loadObject tabsInfo:tabInfo selectedIndex: 0 shouldEnableSwipeGestures:YES error:&error]; -// [[MVMCoreNavigationHandler sharedNavigationHandler] pushViewController:viewControllerToLoad animated:YES]; - -} - -class PrimaryWifiViewController: UIViewController { - let inset = 16.0 - - private lazy var stackView: UIStackView = { - let stack = UIStackView(frame: .zero) - stack.translatesAutoresizingMaskIntoConstraints = false - stack.spacing = 16 - stack.axis = .vertical - stack.alignment = .leading - stack.distribution = .fillProportionally - return stack - }() - - override func viewDidLoad() { - super.viewDidLoad() - - view.addSubview(stackView) - NSLayoutConstraint.constraintPinSubview(stackView, - pinTop: true, topConstant: inset, - pinBottom: true, bottomConstant: inset, - pinLeft: true, leftConstant: inset, - pinRight: true, rightConstant: inset) - - let primary1 = wifiWidget(for: "Primary-1", title: "", description: "", password: "123456") - let primary2 = wifiWidget(for: "Primary-2", title: "", description: "", password: "123456") - let secondary1 = wifiWidget(for: "Secondary-1", title: "", description: "", password: "123456") - let secondary2 = wifiWidget(for: "Secondary-2", title: "", description: "", password: "123456") - - stackView.addArrangedSubview(primary1) - stackView.addArrangedSubview(primary2) - stackView.addArrangedSubview(secondary1) - stackView.addArrangedSubview(secondary2) - } - - private func wifiWidget(for wifiId: String, title: String, description: String, password: String, enabled: Bool = true) -> WifiWidget { - let extraParameters: JSONValueDictionary = ["wifiId": JSONValue.init(stringLiteral: wifiId)] - - let editAction = ActionOpenPageModel(pageType: "editWifi", - presentationStyle: "push", - extraParameters: extraParameters) - - let enabledAction = ActionOpenPageModel(pageType: "enabledWifi", - presentationStyle: "push", - extraParameters: extraParameters) - - let shareAction = ActionShareModel(sharedText: "ssid: \(wifiId) password: \(password)", - sharedType: "Text") - - let wifiWidgetModel = WifiWidgetModel(wifiId: wifiId, - title: title, - description: description, - password: password, - enabled: enabled, - editTitle: "Edit Wi-Fi details", - shareTitle: "Share Wi-Fi", - enabledAction: enabledAction, - editAction: editAction, - shareAction: shareAction, - useHorizontalMargins: true, leftPadding: inset, rightPadding: inset, - useVerticalMargins: true, topPadding: inset, bottomPadding: inset) - - let wifiWidget = WifiWidget(frame: .zero) - wifiWidget.translatesAutoresizingMaskIntoConstraints = false - wifiWidget.set(with: wifiWidgetModel, nil, nil) - - return wifiWidget - } - -} - - diff --git a/JSONCreator_iOS/JSONCreator/WifiWidget.swift b/JSONCreator_iOS/JSONCreator/WifiWidget.swift deleted file mode 100644 index 02a889e..0000000 --- a/JSONCreator_iOS/JSONCreator/WifiWidget.swift +++ /dev/null @@ -1,207 +0,0 @@ -// -// WifiWidgetMolecule.swift -// MVMFrameworksSample -// -// Created by Matt Bruce on 6/17/22. -// - -import Foundation -import UIKit -import MVMCore -import MVMCoreUI - -@objcMembers open class TestLabelToggle: View { - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - - public let label = Label(fontStyle: .BoldBodySmall) - public let toggle = TestToggle() - - //-------------------------------------------------- - // MARK: - MVMCoreViewProtocol - //-------------------------------------------------- - - open override func updateView(_ size: CGFloat) { - super.updateView(size) - label.updateView(size) - toggle.updateView(size) - } - - open override func setupView() { - super.setupView() - - addSubview(label) - addSubview(toggle) - label.setContentHuggingPriority(.required, for: .vertical) - NSLayoutConstraint.pinViews(leftView: label, rightView: toggle, alignTop: false) - } - - open override class func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - guard let model = model as? TestLabelToggleModel, - let toggleHeight = Toggle.estimatedHeight(with: model.toggle, delegateObject), - let labelHeight = Label.estimatedHeight(with: model.label, delegateObject) - else { return nil } - - return max(toggleHeight, labelHeight) - } - - open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - - guard let labelToggleModel = model as? TestLabelToggleModel else { return } - - label.set(with: labelToggleModel.label, delegateObject, additionalData) - toggle.set(with: labelToggleModel.toggle, delegateObject, additionalData) - } - - // MARK: - MoleculeViewProtocol - open override func reset() { - super.reset() - label.reset() - toggle.reset() - label.setFontStyle(.BoldBodySmall) - } -} - -public class TestLabelToggleModel: MoleculeModelProtocol { - public static var identifier: String = "testLabelToggle" - public var moleculeName: String = TestLabelToggleModel.identifier - public var backgroundColor: Color? - public var label: LabelModel - public var toggle: TestToggleModel - - public init(_ label: LabelModel, _ toggle: TestToggleModel) { - self.label = label - self.toggle = toggle - } - - private enum CodingKeys: String, CodingKey { - case moleculeName - case backgroundColor - case label - case toggle - } - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey:.backgroundColor) - label = try typeContainer.decode(LabelModel.self, forKey:.label) - toggle = try typeContainer.decode(TestToggleModel.self, forKey:.toggle) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(moleculeName, forKey: .moleculeName) - try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) - try container.encode(label, forKey: .label) - try container.encode(toggle, forKey: .toggle) - } -} - - -public class WifiWidget: View { - //-------------------------------------------------- - // MARK: - Outlets - //-------------------------------------------------- - public private(set) var titleLabelToggle: TestLabelToggle = { - let tlt = TestLabelToggle() - tlt.label.setFontStyle(.BoldBodyLarge) - return tlt - }() - public let descriptionLabel = Label(fontStyle: .RegularMicro) - public let passwordTextField = TextEntryField() - public let editTitleLink = Link() - public let shareTitleLink = Link() - - private lazy var stack1: Stack = { - return Stack.createStack(with: [titleLabelToggle, descriptionLabel], - axis: .vertical, spacing: Padding.Two) - }() - - private lazy var stack2: Stack = { - return Stack.createStack(with: [ - (view: editTitleLink, model: StackItemModel()), - (view: shareTitleLink, model: StackItemModel(horizontalAlignment: .trailing)) - ], - axis: .horizontal, - spacing: Padding.Two) - }() - - private var row2 = StackItemModel() - private var row3 = StackItemModel() - - private lazy var stack: Stack = { - let model = StackModel(molecules: [StackItemModel(), row2, row3], - axis: .vertical, - spacing: Padding.Four) - let stackItems = [StackItem(andContain:stack1), StackItem(andContain: passwordTextField), StackItem(andContain:stack2)] - return Stack(with: model, stackItems: stackItems) - }() - - //-------------------------------------------------- - // MARK: - Lifecycle - //-------------------------------------------------- - - /// Initial configuration of class and view. - @objc final public override func setupView() { - super.setupView() - - isAccessibilityElement = false - setContentCompressionResistancePriority(.required, for: .vertical) - accessibilityElements = [titleLabelToggle.label, descriptionLabel, editTitleLink, shareTitleLink] - - stack1.restack() - stack2.restack() - stack.restack() - - stack.backgroundColor = .clear - addSubview(stack) - - NSLayoutConstraint.constraintPinSubview(stack, - pinTop: true, topConstant: 16, - pinBottom: true, bottomConstant: 16, - pinLeft: true, leftConstant: 16, - pinRight: true, rightConstant: 16) - } - - open override func reset() { - super.reset() - stack.reset() - } - - open func restack(){ - stack1.restack() - stack2.restack() - stack.restack() - } - - open override func updateView(_ size: CGFloat) { - super.updateView(size) - stack.updateView(size) - } - - //------------------------------------------------------ - // MARK: - Atomization - //------------------------------------------------------ - - open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable: Any]?) { - super.set(with: model, delegateObject, additionalData) - - guard let model = model as? WifiWidgetModel else { return } - backgroundColor = model.backgroundColor?.uiColor ?? .mvmCoolGray1 - - //Password Text Field Model - passwordTextField.set(with: model.passwordModel, delegateObject, additionalData) - - //Update the Stacks with the models - stack1.updateContainedMolecules(with: [model.titleModel, model.descriptionModel], delegateObject, additionalData) - stack2.updateContainedMolecules(with: [model.editModel, model.shareModel], delegateObject, additionalData) - - //show/hide 2 and 3 rows - row2.gone = !model.enabled - row3.gone = !model.enabled - stack.restack() - } -} - - diff --git a/JSONCreator_iOS/JSONCreator/WifiWidgetModel.swift b/JSONCreator_iOS/JSONCreator/WifiWidgetModel.swift deleted file mode 100644 index fe739d0..0000000 --- a/JSONCreator_iOS/JSONCreator/WifiWidgetModel.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// WifiWidgetModel.swift -// MVMFrameworksSample -// -// Created by Matt Bruce on 6/17/22. -// - -import Foundation -import MVMCore -import MVMCoreUI - -public class WifiWidgetModel: ContainerModel, MoleculeModelProtocol { - //MoleculeModelProtocol - public var backgroundColor: Color? - public static var identifier: String { "wifiWidget" } - public var moleculeName: String = WifiWidgetModel.identifier - - //UI Bound Properties - public var wifiId: String - public var title: String - public var description: String - public var password: String - public var enabled: Bool = false - public var editTitle: String - public var shareTitle: String - - //Click Actions - public var enabledAction: ActionModelProtocol - public var editAction: ActionModelProtocol - public var shareAction: ActionModelProtocol - - public init(wifiId: String, title: String, description: String, password: String, enabled: Bool, editTitle: String, shareTitle: String, enabledAction: ActionModelProtocol, editAction: ActionModelProtocol, shareAction: ActionModelProtocol, - horizontalAlignment: UIStackView.Alignment? = nil, verticalAlignment: UIStackView.Alignment? = nil, useHorizontalMargins: Bool? = nil, leftPadding: CGFloat? = nil, rightPadding: CGFloat? = nil, useVerticalMargins: Bool? = nil, topPadding: CGFloat? = nil, bottomPadding: CGFloat? = nil) { - - self.wifiId = wifiId - self.title = title - self.description = description - self.password = password - self.enabled = enabled - self.editTitle = editTitle - self.shareTitle = shareTitle - self.enabledAction = enabledAction - self.editAction = editAction - self.shareAction = shareAction - super.init(horizontalAlignment: horizontalAlignment, verticalAlignment: verticalAlignment, useHorizontalMargins: useHorizontalMargins, leftPadding: leftPadding, rightPadding: rightPadding, useVerticalMargins: useVerticalMargins, topPadding: topPadding, bottomPadding: bottomPadding) - } - - private enum CodingKeys: String, CodingKey { - case moleculeName - case backgroundColor - case wifiId, title, description, password, enabled, editTitle, shareTitle - case enabledAction, editAction, shareAction - } - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) - wifiId = try typeContainer.decode(String.self, forKey: .wifiId) - title = try typeContainer.decode(String.self, forKey: .title) - description = try typeContainer.decode(String.self, forKey: .description) - password = try typeContainer.decode(String.self, forKey: .password) - enabled = try typeContainer.decode(Bool.self, forKey: .enabled) - editTitle = try typeContainer.decode(String.self, forKey: .editTitle) - shareTitle = try typeContainer.decode(String.self, forKey: .shareTitle) - enabledAction = try typeContainer.decodeModel(codingKey: .enabledAction) - editAction = try typeContainer.decodeModel(codingKey: .editAction) - shareAction = try typeContainer.decodeModel(codingKey: .shareAction) - try super.init(from: decoder) - } - - public override func encode(to encoder: Encoder) throws { - try super.encode(to: encoder) - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) - try container.encode(moleculeName, forKey: .moleculeName) - try container.encode(wifiId, forKey: .wifiId) - try container.encode(title, forKey: .title) - try container.encode(description, forKey: .description) - try container.encode(password, forKey: .password) - try container.encode(enabled, forKey: .enabled) - try container.encode(editTitle, forKey: .editTitle) - try container.encode(shareTitle, forKey: .shareTitle) - try container.encodeModel(enabledAction, forKey: .enabledAction) - try container.encodeModel(editAction, forKey: .editAction) - try container.encodeModel(shareAction, forKey: .shareAction) - } - -} - -extension WifiWidgetModel { - - public var titleModel: TestLabelToggleModel { - let labelModel = LabelModel(text: title) - let toggleModel = TestToggleModel(enabled) - toggleModel.action = enabledAction - toggleModel.showText = true - return TestLabelToggleModel(labelModel, toggleModel) - } - - public var passwordModel: TextEntryFieldModel { - let model = TextEntryFieldModel(with: password) - model.fieldKey = "preferredLastName" - model.type = .password - model.readOnly = true - return model - } - - public var descriptionModel: LabelModel { LabelModel(text: description) } - public var editModel: LinkModel { LinkModel(title: editTitle, action: editAction) } - public var shareModel: LinkModel { LinkModel(title: shareTitle, action: shareAction) } - -} - - - From cc5c68094cc9236c88787983a775bbafdf44c954 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Fri, 21 Oct 2022 08:19:56 -0500 Subject: [PATCH 07/15] Signed-off-by: Matt Bruce --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6cbbf79..0d5093b 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,7 @@ __MACOSX ## Build generated build/ DerivedData/ +SharedFrameworks/ ## Various settings *.pbxuser From d8cda0f3dc5e5298977f089a2a17d56a0bb086d4 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 27 Oct 2022 09:03:27 -0500 Subject: [PATCH 08/15] Signed-off-by: Matt Bruce --- .../JSONCreator.xcodeproj/project.pbxproj | 4 + JSONCreator_iOS/JSONCreator/AppDelegate.swift | 145 +----------- .../JSON/Samples/FormContactInfo.json | 124 +++------- .../JSON/Samples/ToggleSample.json | 60 +++++ .../MF/JSONCreatorActionHandler.swift | 12 +- JSONCreator_iOS/JSONCreator/TestToggle.swift | 211 ++++++++++++++++++ 6 files changed, 310 insertions(+), 246 deletions(-) create mode 100644 JSONCreator_iOS/JSONCreator/JSON/Samples/ToggleSample.json create mode 100644 JSONCreator_iOS/JSONCreator/TestToggle.swift diff --git a/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj b/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj index 88447c8..a8825a0 100644 --- a/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj +++ b/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj @@ -77,6 +77,7 @@ EA5B696F2866BC1000B17D2E /* MVMCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EA5B696C2866BC1000B17D2E /* MVMCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; EA5B69702866BC1000B17D2E /* MVMCoreUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA5B696D2866BC1000B17D2E /* MVMCoreUI.framework */; }; EA5B69712866BC1000B17D2E /* MVMCoreUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EA5B696D2866BC1000B17D2E /* MVMCoreUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + EA797B2C2902D4BB00DBAFE6 /* TestToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA797B2B2902D4BB00DBAFE6 /* TestToggle.swift */; }; EAA658152875FA5E00484A7D /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA658142875FA5E00484A7D /* VDSFormControlsTokens.xcframework */; }; EAA658162875FA5E00484A7D /* VDSFormControlsTokens.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EAA658142875FA5E00484A7D /* VDSFormControlsTokens.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; EACA5E5E2853DBC900CBA65B /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EACA5E5D2853DBC900CBA65B /* VDSColorTokens.xcframework */; }; @@ -164,6 +165,7 @@ EA3361FA2891D54A0071C351 /* VDSTypographyTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSTypographyTokens.xcframework; path = ../SharedFrameworks/VDSTypographyTokens.xcframework; sourceTree = ""; }; EA5B696C2866BC1000B17D2E /* MVMCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA5B696D2866BC1000B17D2E /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + EA797B2B2902D4BB00DBAFE6 /* TestToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestToggle.swift; sourceTree = ""; }; EA7E676927582F2200ABF773 /* MVMCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA7E676A27582F2200ABF773 /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EAA658142875FA5E00484A7D /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = ""; }; @@ -228,6 +230,7 @@ D2B1E3FB22F4A6930065F95C /* Assets.xcassets */, D2B1E3FD22F4A6930065F95C /* LaunchScreen.storyboard */, D2B1E40022F4A6930065F95C /* Info.plist */, + EA797B2B2902D4BB00DBAFE6 /* TestToggle.swift */, ); path = JSONCreator; sourceTree = ""; @@ -479,6 +482,7 @@ D2FC4FB025897ACB00061EA4 /* OrderTracker.swift in Sources */, EA09CDD8282C40CC00A7835F /* GMFGBLEHandlerProtocol.swift in Sources */, EA09CDDC282C40CC00A7835F /* GMFGFotaHandler.swift in Sources */, + EA797B2C2902D4BB00DBAFE6 /* TestToggle.swift in Sources */, EA09CDD6282C40CC00A7835F /* GMFGConstant.swift in Sources */, D27564C925939E91003CA713 /* Links.swift in Sources */, EA09CDE6282C416C00A7835F /* BluetoothDebuggableProtocol.swift in Sources */, diff --git a/JSONCreator_iOS/JSONCreator/AppDelegate.swift b/JSONCreator_iOS/JSONCreator/AppDelegate.swift index 8becbf4..7f7d456 100644 --- a/JSONCreator_iOS/JSONCreator/AppDelegate.swift +++ b/JSONCreator_iOS/JSONCreator/AppDelegate.swift @@ -130,149 +130,6 @@ extension AppDelegate: MVMCoreGlobalTopAlertDelegateProtocol { extension AppDelegate { func register(){ - ModelRegistry.register(handler: TextEntryField.self, for: TextEntryField64Model.self) - ModelRegistry.register(handler: EmailVerifyField.self, for: EmailVerifyModel.self) - ModelRegistry.register(handler: ToggleWifiActionHandler.self, for: ToggleWifiActionModel.self) - } -} - -@objcMembers open class EmailVerifyModel: MoleculeModelProtocol { - - public static var identifier: String = "emailVerifyField" - public var moleculeName: String = EmailVerifyModel.identifier - public var emailField: TextEntryFieldModel - public var bottomMolecule: MoleculeModelProtocol - public var email: String - public var backgroundColor: MVMCoreUI.Color? - - public init(emailField: TextEntryFieldModel, email: String, bottomMolecule: MoleculeModelProtocol) { - self.emailField = emailField - self.email = email - self.bottomMolecule = bottomMolecule - } - - public required init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - emailField = try typeContainer.decode(TextEntryFieldModel.self, forKey: .emailField) - email = try typeContainer.decode(String.self, forKey: .email) - bottomMolecule = try typeContainer.decodeModel(codingKey: .bottomMolecule) - backgroundColor = try typeContainer.decodeIfPresent(Color.self, forKey: .backgroundColor) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(moleculeName, forKey: .moleculeName) - try container.encode(emailField, forKey: .emailField) - try container.encode(email, forKey: .email) - try container.encodeIfPresent(backgroundColor, forKey: .backgroundColor) - try container.encodeModel(bottomMolecule, forKey: .bottomMolecule) - } - - private enum CodingKeys: String, CodingKey { - case moleculeName - case emailField - case bottomMolecule - case backgroundColor - case email - } -} - -@objcMembers open class EmailVerifyField: View { - - private let emailField = TextEntryField() - - private var bottomView: MoleculeViewProtocol? - - private var emailVerifyFieldModel: EmailVerifyModel? { - return model as? EmailVerifyModel - } - - private var isBottomViewHidden: Bool { - guard let emailVerifyFieldModel = emailVerifyFieldModel, let text = emailField.text else { return true } - return !(emailVerifyFieldModel.email.caseInsensitiveCompare(text) == .orderedSame) - } - - private let containerView = MVMCoreUICommonViewsUtility.commonView() - - open override func setupView() { - super.setupView() - backgroundColor = .clear - clipsToBounds = true - addSubview(containerView) - NSLayoutConstraint.constraintPinSubview(toSuperview: containerView) - containerView.addSubview(emailField) - NSLayoutConstraint.constraintPinSubview(emailField, pinTop: true, pinBottom: false, pinLeft: true, pinRight: true) - addEmailFieldObserver() - } - - private func addEmailFieldObserver() { - NotificationCenter.default.addObserver(self, selector: #selector(onEmailValueChange), name: UITextField.textDidChangeNotification, object: emailField.textField) - } - - open override func set(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?, _ additionalData: [AnyHashable : Any]?) { - super.set(with: model, delegateObject, additionalData) - - guard let model = model as? EmailVerifyModel else { return } - - emailField.set(with: model.emailField, delegateObject, additionalData) - - if let bottomView = self.bottomView ?? ModelRegistry.createMolecule(model.bottomMolecule, delegateObject: delegateObject, additionalData: additionalData) { - bottomView.set(with: model.bottomMolecule, delegateObject, additionalData) - self.bottomView = bottomView - addBottomView(bottomView: bottomView) - } - } - - public func onEmailValueChange() { - bottomView?.isHidden = isBottomViewHidden - } - - private func addBottomView(bottomView: MoleculeViewProtocol) { - - containerView.addSubview(bottomView) - - bottomView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true - containerView.trailingAnchor.constraint(equalTo: bottomView.trailingAnchor).isActive = true - bottomView.topAnchor.constraint(equalTo: emailField.bottomAnchor).isActive = true - containerView.bottomAnchor.constraint(equalTo: bottomView.bottomAnchor).isActive = true - bottomView.isHidden = isBottomViewHidden - } -} - -class ToggleWifiActionModel: ActionModelProtocol { - static var identifier: String = "toggleWifi" - var extraParameters: JSONValueDictionary? - var analyticsData: JSONValueDictionary? - var wifiId: String - var actionType: String = ToggleWifiActionModel.identifier - - init(wifiId: String) { - self.wifiId = wifiId - } -} - -class TextEntryField64Model: TextEntryFieldModel { - open override class var identifier: String { "textFieldBase64" } - - open override func formFieldServerValue() -> AnyHashable? { - guard let value = super.formFieldServerValue() as? String else { return nil } - return value.base64Encoded() - } - - required init(from decoder: Decoder) throws { - try super.init(from: decoder) - text = text?.base64Decoded() - } -} - -extension String { - - func base64Encoded() -> String? { - data(using: .utf8)?.base64EncodedString() - } - - func base64Decoded() -> String? { - guard let data = Data(base64Encoded: self) else { return nil } - return String(data: data, encoding: .utf8) + ModelRegistry.register(handler: TestToggle.self, for: TestToggleModel.self) } } diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json index 6918dfb..9f494ea 100644 --- a/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json @@ -22,72 +22,43 @@ { "moleculeName": "stackItem", "molecule": { - "moleculeName": "testToggle", + "moleculeName": "toggle", "fieldKey": "isActive" } }, - { - "moleculeName": "stackItem", - "molecule": { - "moleculeName": "textField", - "fieldKey": "firstName", - "type": "text", - "errorMessage": "Please enter a valid first name.", - "placeholder": "John A", - "titleLabel": { - "moleculeName": "label", - "text": "First Name" + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName":"labelToggle", + "label":{ + "moleculeName": "label", + "text":"Label Text Goes Here" + }, + "toggle":{ + "moleculeName": "toggle" + } + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName":"headlineBodyToggle", + "headlineBody":{ + "moleculeName": "headlineBody", + "headline":{ + "moleculeName": "label", + "text": "Headline Text Goes Here" + }, + "body":{ + "moleculeName": "label", + "text": "Body Text Goes Here" + } + }, + "toggle":{ + "moleculeName": "toggle" + } } } - }, - { - "moleculeName": "stackItem", - "molecule": { - "moleculeName": "textField", - "fieldKey": "lastName", - "type": "text", - "placeholder": "Smith", - "errorMessage": "Please enter a valid last name.", - "titleLabel": { - "moleculeName": "label", - "text": "Last Name" - } - } - }, - { - "moleculeName": "stackItem", - "molecule": { - "moleculeName": "textField", - "fieldKey": "phoneNumber", - "type": "phone", - "placeholder": "212-555-1234", - "title": "Contact Phone Number", - "errorMessage": "Please enter a valid phone number." - } - }, - { - "moleculeName": "stackItem", - "molecule": { - "moleculeName": "textField", - "fieldKey": "emailID", - "type": "text", - "placeholder": "JSMith123@gmail.com", - "title": "Email", - "errorMessage": "Please enter a valid greeting name." - } - }, - { - "moleculeName": "stackItem", - "molecule": { - "moleculeName": "textFieldBase64", - "fieldKey": "zipcode", - "type": "number", - "placeholder": "90210", - "title": "Zip Code", - "text": "NzUwMzQ=", - "errorMessage": "Please enter a valid zip code." - } - } ] }, "footer": { @@ -113,36 +84,7 @@ { "groupName": "default", "rules": [ - { - "type": "regex", - "fields": [ - "emailID" - ], - "regex": "^[a-zA-Z0-9](\\.?\\_?\\-?[a-zA-Z0-9]){0,}@[a-zA-Z0-9-_]+\\.([a-zA-Z0-9-_]{1,}\\.){0,}[a-zA-Z]{2,}$" - }, - { - "type": "regex", - "fields": [ - "zipcode" - ], - "regex": "^\\d{5}(?:[-\\s]\\d{4})?$" - }, - { - "regex": "^(\\d{3})[\\s.-]{0,1}(\\d{3})[\\s.-]{0,1}(\\d{4})$", - "type": "regex", - "fields": [ - "phoneNumber" - ] - }, - { - "type": "allRequired", - "ruleId": "requiredRule", - "fields": [ - "emailID", - "firstName", - "lastName" - ] - } + ] } ] diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/ToggleSample.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/ToggleSample.json new file mode 100644 index 0000000..e9a76a0 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/ToggleSample.json @@ -0,0 +1,60 @@ +{ + "Page": { + "template": "stack", + "pageType": "moleculeStack", + "screenHeading": "Manage Profile", + "hideFabOverlay": true, + "suppressPostLaunchRequests": false, + "tabBarHidden": true, + "header": { + "moleculeName": "header", + "molecule": { + "moleculeName": "headlineBody", + "headline": { + "moleculeName": "label", + "text": "Zenkey" + } + } + }, + "stack": { + "moleculeName": "stack", + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "toggle", + "fieldKey": "isActive" + } + } + ] + }, + "footer": { + "moleculeName": "footer", + "molecule": { + "moleculeName": "twoButtonView", + "primaryButton": { + "moleculeName": "button", + "title": "Edit", + "groupName": "default", + "action": { + "actionType": "openPage", + "pageType": "updateProfile", + "extraParameters": { + "from": "none" + }, + "presentationStyle": "push" + } + } + } + }, + "formRules": [ + { + "groupName": "default", + "rules": [ + + ] + } + ] + } +} + diff --git a/JSONCreator_iOS/JSONCreator/MF/JSONCreatorActionHandler.swift b/JSONCreator_iOS/JSONCreator/MF/JSONCreatorActionHandler.swift index 77e25b0..378af3c 100644 --- a/JSONCreator_iOS/JSONCreator/MF/JSONCreatorActionHandler.swift +++ b/JSONCreator_iOS/JSONCreator/MF/JSONCreatorActionHandler.swift @@ -22,17 +22,7 @@ import MVMCoreUI } public class JSONCreatorActionHandler: MVMCoreUIActionHandler { - public override func handleOtherActions(_ actionType: String?, actionInformation: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?, delegateObject: DelegateObject?) -> Bool { - if actionType == "print" { - if actionInformation?.boolForKey("delay") ?? false { - sleep(2) - } - print(actionInformation?.stringForkey("text") ?? "fail") - return true - } - return super.handleOtherActions(actionType, actionInformation: actionInformation, additionalData: additionalData, delegateObject: delegateObject) - } - + public static func doStuff() { MVMCoreObject.sharedInstance()?.actionHandler = JSONCreatorActionHandler() ModelRegistry.register(ActionPrintModel.self) diff --git a/JSONCreator_iOS/JSONCreator/TestToggle.swift b/JSONCreator_iOS/JSONCreator/TestToggle.swift new file mode 100644 index 0000000..92f92f4 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/TestToggle.swift @@ -0,0 +1,211 @@ +// +// TestToggle.swift +// JSONCreator +// +// Created by Matt Bruce on 10/21/22. +// Copyright © 2022 Verizon Wireless. All rights reserved. +// + +import Foundation +import MVMCore +import MVMCoreUI +import UIKit +import VDS + +/** + A custom implementation of Apple's UISwitch. + + By default this class begins in the off state. + + Container: The background of the toggle control. + Knob: The circular indicator that slides on the container. + */ +open class TestToggle: ToggleBase, VDSMoleculeViewProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + public var viewModel: TestToggleModel! + public var delegateObject: MVMCoreUIDelegateObject? + public var additionalData: [AnyHashable: Any]? + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + public override func initialSetup() { + super.initialSetup() + + publisher(for: .touchUpInside) + .sink {[weak self] toggle in + guard let self = self else { return } + self.toggle() + }.store(in: &subscribers) + + publisher(for: .valueChanged) + .sink {[weak self] toggle in + guard let self = self else { return } + self.valueChanged(isOn: toggle.isOn) + }.store(in: &subscribers) + + accessibilityLabelEnabled = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") + accessibilityLabelDisabled = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") + accessibilityHintEnabled = MVMCoreUIUtility.hardcodedString(withKey: "AccToggleHint") + accessibilityHintDisabled = MVMCoreUIUtility.hardcodedString(withKey: "AccDisabled") + accessibilityValueEnabled = MVMCoreUIUtility.hardcodedString(withKey: "AccOn") + accessibilityValueDisabled = MVMCoreUIUtility.hardcodedString(withKey: "AccOff") + } + + // MARK:- MVMCoreViewProtocol + open func updateView(_ size: CGFloat) {} + + open func viewModelDidUpdate() { + guard let viewModel else { return } + + additionalData = additionalData.dictionaryAdding(key: KeySourceModel, value: viewModel) + } + + private func valueChanged(isOn: Bool){ + guard let viewModel else { return } + //sync the value on the viewModel + viewModel.selected = isOn + + //tell the form you changed + _ = FormValidator.validate(delegate: self.delegateObject?.formHolderDelegate) + + if viewModel.action != nil || viewModel.alternateAction != nil { + var action: ActionModelProtocol? + if isOn { + action = viewModel.action + } else { + action = viewModel.alternateAction ?? viewModel.action + } + if let action { + MVMCoreUIActionHandler.performActionUnstructured(with: action, + sourceModel: viewModel, + additionalData: additionalData, + delegateObject: delegateObject) + } + } + + print("toggle value changed to: \(isOn)") + print("viewModel server value: \(viewModel.formFieldServerValue()!)") + } + + public static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { + return 44 + } + + private typealias ActionDefinition = (model: ActionModelProtocol, + sourceModel: MoleculeModelProtocol?) + + private func performActionUnstructured(definition: ActionDefinition) { + MVMCoreUIActionHandler.performActionUnstructured(with: definition.model, + sourceModel: definition.sourceModel, + additionalData: additionalData, + delegateObject: delegateObject) + } +} +// MARK: - MVMCoreUIViewConstrainingProtocol +extension TestToggle { + + public func needsToBeConstrained() -> Bool { true } + + public func horizontalAlignment() -> UIStackView.Alignment { .trailing } +} + +public class TestToggleModel: MoleculeModelProtocol, FormFieldProtocol { + //-------------------------------------------------- + // MARK: - Properties + //-------------------------------------------------- + + public static var identifier: String = "testToggle" + public var backgroundColor: Color? //not used + + public var selected: Bool = false + public var enabled: Bool = true + public var readOnly: Bool = false + public var action: ActionModelProtocol? + public var alternateAction: ActionModelProtocol? + public var accessibilityText: String? + public var fieldKey: String? + public var groupName: String = FormValidator.defaultGroupName + public var baseValue: AnyHashable? + + //-------------------------------------------------- + // MARK: - Keys + //-------------------------------------------------- + + private enum CodingKeys: String, CodingKey { + case moleculeName + case state + case enabled + case readOnly + case action + case accessibilityIdentifier + case alternateAction + case accessibilityText + case fieldKey + case groupName + } + + //-------------------------------------------------- + // MARK: - Form Valdiation + //-------------------------------------------------- + + public func formFieldValue() -> AnyHashable? { + guard enabled else { return nil } + return selected + } + + //-------------------------------------------------- + // MARK: - Server Value + //-------------------------------------------------- + open func formFieldServerValue() -> AnyHashable? { + return formFieldValue() + } + + //-------------------------------------------------- + // MARK: - Initializer + //-------------------------------------------------- + + public init(_ state: Bool) { + selected = state + baseValue = state + } + + //-------------------------------------------------- + // MARK: - Codec + //-------------------------------------------------- + + required public init(from decoder: Decoder) throws { + let typeContainer = try decoder.container(keyedBy: CodingKeys.self) + + if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) { + selected = state + } + action = try typeContainer.decodeModelIfPresent(codingKey: .action) + alternateAction = try typeContainer.decodeModelIfPresent(codingKey: .alternateAction) + accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) + baseValue = selected + fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) + if let gName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { + groupName = gName + } + enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true + readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) ?? false + + + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) + try container.encodeModelIfPresent(action, forKey: .action) + try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction) + try container.encode(moleculeName, forKey: .moleculeName) + try container.encode(selected, forKey: .state) + try container.encode(enabled, forKey: .enabled) + try container.encodeIfPresent(fieldKey, forKey: .fieldKey) + try container.encodeIfPresent(groupName, forKey: .groupName) + try container.encode(readOnly, forKey: .readOnly) + } +} From d696ded6e7fb7cb4fc7070bf87fed02c61792934 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 27 Oct 2022 09:20:51 -0500 Subject: [PATCH 09/15] added samples Signed-off-by: Matt Bruce --- .../JSON/Samples/AccountSecurity.json | 194 +++++++++++++++++ .../JSON/Samples/DigitalFieldValidation.json | 133 ++++++++++++ .../JSONCreator/JSON/Samples/HMP.json | 196 ++++++++++++++++++ 3 files changed, 523 insertions(+) create mode 100644 JSONCreator_iOS/JSONCreator/JSON/Samples/AccountSecurity.json create mode 100644 JSONCreator_iOS/JSONCreator/JSON/Samples/DigitalFieldValidation.json create mode 100644 JSONCreator_iOS/JSONCreator/JSON/Samples/HMP.json diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/AccountSecurity.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/AccountSecurity.json new file mode 100644 index 0000000..19f0e0e --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/AccountSecurity.json @@ -0,0 +1,194 @@ +{ + "ResponseInfo": { + "locale": "EN", + "server": "10-74-170-83.ebiz.verizon.com-mf_prepayss01", + "code": "00000", + "hideNotificationLogo": false, + "message": "0", + "buildNumber": "6698", + "type": "Success", + "requestId": "dbb1ee17-69fe-464e-8a82-9563b83422ab", + "topAlertTime": 0 + }, + "Page": { + "navigationBar": { + "moleculeName": "navigationBar", + "title": "Account security" + }, + "pageType": "tbd", + "cache": false, + "screenHeading": "Account security", + "template": "stack", + "header": { + "moleculeName": "header", + "molecule": { + "moleculeName": "headlineBody", + "headline": { + "moleculeName": "label", + "text": "Next up, let's secure this account." + }, + "body": { + "moleculeName": "label", + "attributes": [ + { + "topPadding": 30, + "length": 19, + "action": { + "actionType": "openPage", + "analyticsData": { + "vzdl.page.pageLinkName": "/mf/Manage Profile|Email Verify", + "vzdl.page.linkName": "Verify" + }, + "pageType": "whyWeAskingAEMPR" + }, + "location": 113, + "type": "action" + } + ], + "text": "We'll use this PIN and security question for verification and help accessing the account. Make sure the PIN meets these requirements." + } + } + }, + "stack": { + "moleculeName": "stack", + "molecules": [ + { + "moleculeName": "stackItem", + "horizontalAlignment": "leading", + "molecule": { + "moleculeName": "digitTextField", + "editable": true, + "title": "Account security PIN", + "digits": 4, + "fieldKey": "securityPIN" + } + }, + { + "moleculeName": "stackItem", + "horizontalAlignment": "leading", + "molecule": { + "moleculeName": "digitTextField", + "editable": true, + "title": "Confirm account security PIN", + "type": "secure", + "digits": 4, + "fieldKey": "confirmSecurityPIN" + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "dropDown", + "fieldKey": "securityQuestion", + "title": "Security question", + "options": [ + "What was the first live concert you attended?", + "Where did you and your spouse first meet?", + "What was your favorite place to visit as a child?", + "What was the first name of your first roommate?", + "What is the name of a memorable place?", + "What is the first name of your best friend?", + "What was your favorite restaurant in college?" + ], + "selectedIndex": 0 + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textField", + "title": "Security answer", + "type": "text", + "fieldKey": "securityAnswer" + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "label", + "text": "Be sure to remember these details. They'll be needed to access the account later." + } + } + ] + }, + "footer": { + "moleculeName": "footer", + "molecule": { + "moleculeName": "twoButtonView", + "primaryButton": { + "groupName": "default", + "moleculeName": "button", + "title": "Next", + "action": { + "analyticsData": { + "vzdl.page.linkName": "Legal Disclosure" + }, + "fieldKey": "next", + "actionType": "openPage", + "extraParameters": { + "from": "none" + }, + "presentationStyle": "push", + "pageType": "legalDisclosurePR" + } + } + } + }, + "formRules": [ + { + "groupName": "default", + "rules": [ + { + "regex": "^(?!.*0123|.*1234|.*2345|.*3456|.*4567|.*5678|.*6789|.*3210|.*4321|.*5432|.*6543|.*7654|.*8765|.*9876|.*0000|.*1111|.*2222|.*3333|.*4444|.*5555|.*6666|.*7777|.*8888|.*9999).*$", + "type": "regex", + "fields": [ + "securityPIN" + ], + "errorMessage": { + "securityPIN": "Your PIN can't have numbers in order or repeating." + } + }, + { + "type": "equals", + "fields": [ + "securityPIN", + "confirmSecurityPIN" + ], + "errorMessage": { + "confirmSecurityPIN": "Your account PIN entries don't match. Try again." + } + }, + { + "type": "regex", + "regex": "^[A-Za-z0-9 ]{3,10}$", + "fields": [ + "securityAnswer" + ], + "errorMessage": { + "securityAnswer": "Your security answer can only letters, numbers, spaces, and periods." + } + }, + { + "type": "regex", + "regex": "^.{3,10}$", + "fields": [ + "securityAnswer" + ], + "errorMessage": { + "securityAnswer": "Your answer must be between 3-10 characters." + } + }, + { + "type": "allRequired", + "ruleId": "requiredRule", + "fields": [ + "securityPIN", + "confirmSecurityPIN", + "securityAnswer" + ] + } + ] + } + ] + } + } diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/DigitalFieldValidation.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/DigitalFieldValidation.json new file mode 100644 index 0000000..4720b28 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/DigitalFieldValidation.json @@ -0,0 +1,133 @@ +{ + "ResponseInfo": { + "locale": "EN", + "server": "10-74-170-83.ebiz.verizon.com-mf_prepayss01", + "code": "00000", + "hideNotificationLogo": false, + "message": "0", + "buildNumber": "6698", + "type": "Success", + "requestId": "dbb1ee17-69fe-464e-8a82-9563b83422ab", + "topAlertTime": 0 + }, + "Page": { + "navigationBar": { + "moleculeName": "navigationBar", + "title": "Account security" + }, + "pageType": "tbd", + "cache": false, + "screenHeading": "Account security", + "template": "stack", + "stack": { + "moleculeName": "stack", + "molecules": [ + { + "moleculeName": "stackItem", + "horizontalAlignment": "leading", + "molecule": { + "moleculeName": "digitTextField", + "editable": true, + "title": "Account security PIN", + "digits": 4, + "fieldKey": "securityPIN" + } + }, + { + "moleculeName": "stackItem", + "horizontalAlignment": "leading", + "molecule": { + "moleculeName": "digitTextField", + "editable": true, + "title": "Confirm account security PIN", + "type": "text", + "digits": 4, + "fieldKey": "confirmSecurityPIN" + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textField", + "fieldKey": "preferredFirstName", + "type": "text", + "text": "", + "errorMessage": "Please enter a valid first name.", + "title": "Preferred First Name", + "feedback": "This will replace greeting name if you have one.", + "required": false + } + } + ] + }, + "footer": { + "moleculeName": "footer", + "molecule": { + "moleculeName": "twoButtonView", + "primaryButton": { + "groupName": "default", + "moleculeName": "button", + "title": "Next", + "action": { + "analyticsData": { + "vzdl.page.linkName": "Legal Disclosure" + }, + "fieldKey": "next", + "actionType": "openPage", + "extraParameters": { + "from": "none" + }, + "presentationStyle": "push", + "pageType": "legalDisclosurePR" + } + } + } + }, + "formRules": [ + { + "groupName": "default", + "rules": [ + { + "regex": "^[0-6]{0,4}$", + "type": "regex", + "fields": [ + "securityPIN" + ], + "errorMessage": { + "securityPIN": "Your PIN can't have numbers in order or repeating." + } + }, + { + "type": "equals", + "fields": [ + "securityPIN", + "confirmSecurityPIN" + ], + "errorMessage": { + "confirmSecurityPIN": "Your account PIN entries don't match. Try again." + } + }, + { + "regex": "^[0-9a-zA-Z@\\.\\-\\’_?]{0,25}$", + "ruleId": "anyAlphaNumeric", + "errorMessage": { + "preferredFirstName": "Only use for First Name" + }, + "type": "regex", + "fields": [ + "preferredFirstName" + ] + }, + { + "type": "allRequired", + "ruleId": "requiredRule", + "fields": [ + "securityPIN", + "confirmSecurityPIN" + ] + } + ] + } + ] + } + } diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/HMP.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/HMP.json new file mode 100644 index 0000000..ad04bb7 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/HMP.json @@ -0,0 +1,196 @@ +{ + "ResponseInfo" : { + "locale" : "EN", + "server" : "loghost-mf_postpayss01", + "userMessage" : "0", + "code" : "00000", + "appSessionExtended" : true, + "message" : "0", + "mdn" : "7165723410", + "buildNumber" : "61", + "type" : "Success", + "requestId" : "12321a81-a48b-4023-94d9-7bd42af6d65a", + "topAlertTime" : 0 + }, + "Page" : { + "pageStatNames" : [ + "\/mf\/retail\/appt\/flow", + "\/mf\/retail\/appt\/reasons" + ], + "template" : "list", + "footer" : { + "moleculeName" : "footer", + "molecule" : { + "moleculeName" : "twoButtonView", + "primaryButton" : { + "groupName" : "default", + "moleculeName" : "button", + "title" : "Continue", + "action" : { + "analyticsData" : { + "vzdl.page.linkName" : "Continue" + }, + "extraParameters" : { + "destPageType" : "rtlApptsStoreLocator", + "FromStoreDetails" : "N" + }, + "actionType" : "openPage", + "title" : "Confirm", + "presentationStyle" : "push", + "pageType" : "launchApp" + } + } + } + }, + "molecules" : [ + { + "dropDown" : { + "moleculeName" : "dropDown", + "action" : { + "actionType" : "noop" + }, + "placeholder" : "Select a reason for visit", + "title" : "What can we help you with?", + "fieldKey" : "reason", + "type" : "text", + "options" : [ + "Account Support", + "Shop", + "Device Setup\/Troubleshooting", + "Other" + ] + }, + "line" : { + "moleculeName" : "line", + "type" : "none" + }, + "moleculeName" : "dropDownListItem", + "molecules" : [ + [ + { + "line" : { + "moleculeName" : "line", + "type" : "none" + }, + "topPadding" : 0, + "moleculeName" : "listItem", + "molecule" : { + "title" : "Relating to", + "fieldKey" : "subReason", + "moleculeName" : "dropDown", + "options" : [ + "Autopay question or issue", + "Billing issue", + "Misleading promise or information inquiry", + "Disconnect line", + "Payment arrangements", + "Promised promotion or offer", + "Other" + ], + "type" : "text", + "action" : { + "actionType" : "noop" + } + } + } + ], + [ + + ], + [ + + ], + [ + + ] + ] + } + ], + "cache" : false, + "header" : { + "moleculeName" : "header", + "molecule" : { + "moleculeName" : "headlineBody", + "headline" : { + "moleculeName" : "label", + "text" : "Make an appointment to see a representative." + }, + "body" : { + "moleculeName" : "label", + "text" : "We currently have limited staffing. For your safety and the safety of our employees, we are only scheduling appointments to provide one-on-one support for account maintenance or device troubleshooting.", + "fontStyle" : "BoldBodySmall" + } + } + }, + "tab" : [ + { + "appContext" : "mobileFirstSS", + "title" : "Schedule", + "actionType" : "openPage", + "moleculeName" : "label", + "presentationStyle" : "root", + "pageType" : "rtlReasonAndSubReasons" + }, + { + "appContext" : "mobileFirstSS", + "title" : "My Appointments", + "actionType" : "openPage", + "moleculeName" : "label", + "presentationStyle" : "root", + "pageType" : "rtlViewAllScheduledAppts" + } + ], + "pageType" : "rtlReasonAndSubReasons", + "suppressPostLaunchRequests" : false, + "formRules" : [ + { + "groupName": "default", + "rules": [ + { + "type": "allRequired", + "ruleId": "allRequired", + "fields": [ + "reason", + "subReason" + ] + } + ], + "effects": [ + { + "type": "dynamicRuleFormFieldEffect", + "fieldKey": "subReason", + "activatedRuleIds": ["allRequired"], + "rules": [ + { + "type": "regex", + "regex": "^Account Support$", + "fields": [ + "reason" + ] + } + ] + }, + { + "type": "enableFormFieldEffect", + "fieldKey": "subReason", + "rules": [ + { + "type": "regex", + "regex": "^Account Support$", + "fields": [ + "reason" + ] + } + ] + } + ] + } + ], + "navigationBar" : { + "moleculeName" : "navigationBar", + "title" : "Make Appointment" + }, + "tabBarHidden" : true, + "isAtomicTabs" : true + } +} From d59f88f9c6854b92a5a8010a89bc850bc348e758 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 27 Oct 2022 10:05:21 -0500 Subject: [PATCH 10/15] Signed-off-by: Matt Bruce --- dependency.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dependency.txt b/dependency.txt index 45a8cd2..e567159 100644 --- a/dependency.txt +++ b/dependency.txt @@ -2,3 +2,5 @@ mvm_core https://gitlab.verizon.com/BPHV_MIPS/mvm_core.git develop mvm_core_ui https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui.git develop + +vds_ios https://gitlab.verizon.com/BPHV_MIPS/vds_ios.git develop From dd6e1008b5b4a8abd2095178c93133534f473992 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 12 Jan 2023 15:57:39 -0600 Subject: [PATCH 11/15] updated for tilet Signed-off-by: Matt Bruce --- .../JSONCreator/JSON/Samples/tiletSample.json | 46 +++++++++++++++++++ dependency.txt | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 JSONCreator_iOS/JSONCreator/JSON/Samples/tiletSample.json diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/tiletSample.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/tiletSample.json new file mode 100644 index 0000000..c7c4bdd --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/tiletSample.json @@ -0,0 +1,46 @@ +{ + "Page": { + "template": "stack", + "pageType": "moleculeStack", + "screenHeading": "Tilet Sample", + "hideFabOverlay": true, + "suppressPostLaunchRequests": false, + "tabBarHidden": true, + "header": { + "moleculeName": "header", + "molecule": { + "moleculeName": "headlineBody", + "headline": { + "moleculeName": "label", + "text": "Tilet Variations" + } + } + }, + "stack": { + "moleculeName": "stack", + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "tilet", + "surface": "light", + "title": { + "text": "Save $XX on your monthly bill.", + "typographicalStyle": "BoldTitleSmall" + }, + "subTitle": { + "text": "Enroll in Auto Pay & paper-free billing to save on your monthly bill.", + "typographicalStyle": "BodySmall", + "textColor": "primary" + }, + "directionalIcon": { + "size": "medium", + "surface": "dark" + } + } + } + ] + }, + "footer": {} + } +} diff --git a/dependency.txt b/dependency.txt index e567159..4cf000f 100644 --- a/dependency.txt +++ b/dependency.txt @@ -1,6 +1,6 @@ mvm_core https://gitlab.verizon.com/BPHV_MIPS/mvm_core.git develop -mvm_core_ui https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui.git develop +mvm_core_ui https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui.git feature/ONEAPP-2811-Tilet vds_ios https://gitlab.verizon.com/BPHV_MIPS/vds_ios.git develop From f97c2dd79fbefd167937fbeb456da6acdefb56db Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 1 Feb 2023 12:39:56 -0600 Subject: [PATCH 12/15] Signed-off-by: Matt Bruce --- .../JSONCreator.xcodeproj/project.pbxproj | 59 ++++++++++++++ Supporting Files/Artifactory/Artifactory.sh | 81 +++++++++++++++++++ .../Artifactory/ArtifactoryItems.txt | 3 + .../Artifactory/DownloadArtifactoryItems.sh | 79 ++++++++++++++++++ 4 files changed, 222 insertions(+) create mode 100755 Supporting Files/Artifactory/Artifactory.sh create mode 100644 Supporting Files/Artifactory/ArtifactoryItems.txt create mode 100644 Supporting Files/Artifactory/DownloadArtifactoryItems.sh diff --git a/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj b/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj index a8825a0..ec1f6d1 100644 --- a/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj +++ b/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj @@ -18,6 +18,17 @@ name = UpdateDependency; productName = UpdateDependency; }; + EA985CBC298AE8CB00F2FF2E /* Artifactory */ = { + isa = PBXAggregateTarget; + buildConfigurationList = EA985CBF298AE8CC00F2FF2E /* Build configuration list for PBXAggregateTarget "Artifactory" */; + buildPhases = ( + EA985CC0298AE8D000F2FF2E /* ShellScript */, + ); + dependencies = ( + ); + name = Artifactory; + productName = Artifactory; + }; /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ @@ -395,6 +406,9 @@ D2B1E43722F9F84E0065F95C = { CreatedOnToolsVersion = 10.3; }; + EA985CBC298AE8CB00F2FF2E = { + CreatedOnToolsVersion = 14.2; + }; }; }; buildConfigurationList = D2B1E3EA22F4A68F0065F95C /* Build configuration list for PBXProject "JSONCreator" */; @@ -414,6 +428,7 @@ targets = ( D2B1E3EE22F4A68F0065F95C /* JSONCreator */, D2B1E43722F9F84E0065F95C /* UpdateDependency */, + EA985CBC298AE8CB00F2FF2E /* Artifactory */, ); }; /* End PBXProject section */ @@ -450,6 +465,23 @@ shellPath = /bin/sh; shellScript = "#!/bin/bash\n\ncd ../\nsh update.sh\n"; }; + EA985CC0298AE8D000F2FF2E /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n# Type a script or drag a script file from your workspace to insert its path.\ncd \"${PROJECT_DIR}\"\ncd ../\ncd \"Supporting Files/Artifactory\"\nmkdir -p \"../../SharedFrameworks\"\nsh DownloadArtifactoryItems.sh\n"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -708,6 +740,24 @@ }; name = Release; }; + EA985CBD298AE8CC00F2FF2E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = FCMA4QKS77; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + EA985CBE298AE8CC00F2FF2E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = FCMA4QKS77; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -738,6 +788,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + EA985CBF298AE8CC00F2FF2E /* Build configuration list for PBXAggregateTarget "Artifactory" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EA985CBD298AE8CC00F2FF2E /* Debug */, + EA985CBE298AE8CC00F2FF2E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = D2B1E3E722F4A68F0065F95C /* Project object */; diff --git a/Supporting Files/Artifactory/Artifactory.sh b/Supporting Files/Artifactory/Artifactory.sh new file mode 100755 index 0000000..6ad5af6 --- /dev/null +++ b/Supporting Files/Artifactory/Artifactory.sh @@ -0,0 +1,81 @@ +#!/bin/bash + +#First arg should be the local path, second arg should be the remote path. +VERSION="2.0" +#Update onces stable +#APITOKEN=AKCp5cbwXBA2Rarq6WagmFFxQxcxsARGxSq5g1H8NaGm7837KTgwdWPqsp7FdgRa13B7AcpGN +#URL=https://oneartifactorycloud.verizon.com/artifactory + +APITOKEN=AKCp5ZmHP8B1dkLtdSh23bMcWHtrWzoB3SfhoCNpEC5e3dKNhiKsn8TPMZQafXzm2qkeXFXE6 +URL=https://oneartifactoryprod.verizon.com/artifactory + +LOCALPATH="${1}" +REMOTEPATH="${2}" +LOGFILE=$3 +LOCALDIR=$(dirname "${LOCALPATH}") +LOCALBASE=$(basename "${LOCALPATH}") +NEWFILEPATH="${LOCALDIR}"/$(basename "${REMOTEPATH}") +VERSIONFILE=./Checksums/"${LOCALBASE}".txt + +if [ -z $LOGFILE ]; then +LOGFILE="/tmp/${LOCALBASE}.txt" +fi + +#first argument is error message. +exit_with_error () { +echo "Error: $1" >> "${LOGFILE}" +if [ -f "${NEWFILEPATH}" ]; then +rm -rf "${NEWFILEPATH}" 2>>"${LOGFILE}" +fi +exit 1 +} + +echo "----------------------------------------------------------" >> $LOGFILE +echo "Logs for ${LOCALBASE}" >> $LOGFILE + +echo -e "Local Target: ${LOCALPATH}" >> $LOGFILE +echo -e "Remote Source: ${REMOTEPATH}" >> $LOGFILE + +if [ -z "$LOCALPATH" ]; then +exit_with_error "Missing local path argument" +fi + +if [ -z "$REMOTEPATH" ]; then +exit_with_error "Missing filename path argument" +fi + +#get local and remote checksums for comparison +echo -e "Getting checksums..." >> $LOGFILE +echo -e "URL: ${URL}/api/storage/${REMOTEPATH}" >> $LOGFILE +JSON=$(curl --header "X-JFrog-Art-Api: ${APITOKEN}" -X GET "${URL}/api/storage/${REMOTEPATH}" 2>>"${LOGFILE}") +CHECKSUM=$(echo "$JSON" | python3 -c 'import sys, json; print(json.load(sys.stdin)["checksums"]["sha1"])') 2>>"${LOGFILE}" +if [[ -z "$CHECKSUM" ]]; then +exit_with_error "No Checksum found in json: ${JSON}" +fi +echo "Remote checksum ${CHECKSUM}" >> "${LOGFILE}" +OLDCHECKSUM=$(cat "${VERSIONFILE}" 2>/dev/null) +echo "Local checksum ${OLDCHECKSUM}" >> "${LOGFILE}" + +#get new framework if no original framework, no local checksum, or remote checksum is different from local. +if [ ! -e "${LOCALPATH}" ] || [ -z "$OLDCHECKSUM" ] || [ "$CHECKSUM" != "$OLDCHECKSUM" ]; then +echo "Downloading..." >> "${LOGFILE}" +echo -e "URL: ${URL}/${REMOTEPATH}" >> $LOGFILE +curl --header "X-JFrog-Art-Api: ${APITOKEN}" -f -X GET "$URL/$REMOTEPATH" --output "${NEWFILEPATH}" 2>>"${LOGFILE}" +if [ $? -eq 0 ] && [ -e "${NEWFILEPATH}" ]; then +echo "Finished Downloading, begin unzip" >> "${LOGFILE}" +unzip -q -o "${NEWFILEPATH}" -d "${LOCALDIR}" 2>>"${LOGFILE}" +if [ $? -eq 0 ]; then +echo "Finished unzipping, remove zip" >> "${LOGFILE}" +rm -rf "${NEWFILEPATH}" 2>>"${LOGFILE}" +echo "Writing new checksum to file" >> "${LOGFILE}" +echo "${CHECKSUM}" > "${VERSIONFILE}" 2>>"${LOGFILE}" +echo "Successfully downloaded and unzipped archive." >> "${LOGFILE}" +else +exit_with_error "Error unzipping" +fi +else +exit_with_error "Failed to download" +fi +else +echo "Successful, No New Version" >> "${LOGFILE}" +fi diff --git a/Supporting Files/Artifactory/ArtifactoryItems.txt b/Supporting Files/Artifactory/ArtifactoryItems.txt new file mode 100644 index 0000000..8ee8db3 --- /dev/null +++ b/Supporting Files/Artifactory/ArtifactoryItems.txt @@ -0,0 +1,3 @@ +../../SharedFrameworks/VDSColorTokens.xcframework GVJV_VDS_Maven/%40vds-tokens/ios/VDSColorTokens.1.0.6.xcframework.zip +../../SharedFrameworks/VDSFormControlsTokens.xcframework GVJV_VDS_Maven/@vds-tokens/ios/VDSFormControlsTokens.1.0.7.xcframework.zip +../../SharedFrameworks/VDSTypographyTokens.xcframework GVJV_VDS_Maven/@vds-tokens/ios/VDSTypographyTokens.2.0.0.xcframework.zip diff --git a/Supporting Files/Artifactory/DownloadArtifactoryItems.sh b/Supporting Files/Artifactory/DownloadArtifactoryItems.sh new file mode 100644 index 0000000..7ab8500 --- /dev/null +++ b/Supporting Files/Artifactory/DownloadArtifactoryItems.sh @@ -0,0 +1,79 @@ +#!/bin/sh + +# DownloadArtifactoryItems.sh +# myverizon +# +# Created by Kyle on 3/2/20. +# Copyright © 2020 Verizon Wireless Inc. All rights reserved. + +ARTIFACTORYITEMS=./ArtifactoryItems.txt +ARTIFACTORY=Artifactory.sh + +update_artifactory_item () { + #echo "Run Artifactory for ${1} from ${2}" + ./${ARTIFACTORY} "${1}" "${2}" +} + +#Loop through items needed to download and download them. +PIDARRAY=() +LOGFILEARRAY=() +while read -r LOCALFILE REMOTEFILE; do + + #for if the directory has a parameter, such as PROJECT_DIR + FILE=$(eval echo ${LOCALFILE}) + LOGFILE="/tmp/$(basename ${FILE}).txt" + rm $LOGFILE 2> /dev/null + touch $LOGFILE + + #download them in parallel and store the PIDS. + update_artifactory_item "${FILE}" $REMOTEFILE $LOGFILE & + PIDARRAY+=($!) + LASTPID=${PIDARRAY[${#PIDARRAY[@]}-1]} + LOGFILEARRAY+=($LOGFILE) + echo "Process ${LASTPID} spawned for ${REMOTEFILE##*/}" + +done < "${ARTIFACTORYITEMS}" + +#wait for all processes to finish and fail if one of them fails. +INDEX=-1 +TOTAL=${#PIDARRAY[@]} +for i in "${PIDARRAY[@]}"; do + + INDEX=$((INDEX + 1)) + LOGFILE=${LOGFILEARRAY[$((INDEX))]} + + echo "\n\n$((INDEX + 1)) / ${TOTAL} (PID: ${i}) " + + # tail the subprocess log + tail -n +1 -f $LOGFILE & + READ_PID=$! + + # wait for subprocess to finish + sleep 0.05 # Allow tail -n +1 to print + wait $i + + # catch any subprocess non-zero status + status=$? + if [[ $status -gt 0 ]]; then + FAILED_PID_STATUS=$status + FAILED_PID=$i + fi + + # kill the running tail, consume the kill output + kill $READ_PID + wait $READ_PID > /dev/null 2>&1 + + # Need proper way to terminate children. + #if [[ -n $FAILED_PID ]] && [[ -n $FAIL_EARLY ]]; then + # echo "\n\nProcess ${FAILED_PID} failed with exit code ${FAILED_PID_STATUS}" + # echo $( ps -o pgid $FAILED_PID | grep [0-9] | tr -d ' ' ) + # kill -9 $(printf '%s ' "${PIDARRAY[@]}") + # exit $FAILED_PID_STATUS + #fi + +done + +if [[ -n $FAILED_PID_STATUS ]]; then + echo "\n\nProcess ${FAILED_PID} failed with exit code ${FAILED_PID_STATUS}" + exit $FAILED_PID_STATUS +fi From 394a21bf53bceaba28f185b4b8e28d3e0fd26182 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 30 Mar 2023 10:53:14 -0500 Subject: [PATCH 13/15] updated sample Signed-off-by: Matt Bruce --- .../JSON/Samples/FormContactInfo.json | 128 ++++++++++++------ .../JSON/Samples/TileleListSample.json | 110 +++++++++++++++ .../JSON/Samples/TileletSample.json | 64 +++++++++ .../JSONCreator/JSON/Samples/tiletSample.json | 46 ------- JSONCreator_iOS/JSONCreator/TestToggle.swift | 25 ++-- 5 files changed, 273 insertions(+), 100 deletions(-) create mode 100644 JSONCreator_iOS/JSONCreator/JSON/Samples/TileleListSample.json create mode 100644 JSONCreator_iOS/JSONCreator/JSON/Samples/TileletSample.json delete mode 100644 JSONCreator_iOS/JSONCreator/JSON/Samples/tiletSample.json diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json index 9f494ea..5b28f2a 100644 --- a/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/FormContactInfo.json @@ -19,46 +19,67 @@ "stack": { "moleculeName": "stack", "molecules": [ - { - "moleculeName": "stackItem", - "molecule": { - "moleculeName": "toggle", - "fieldKey": "isActive" - } - }, - { - "moleculeName": "stackItem", - "molecule": { - "moleculeName":"labelToggle", - "label":{ - "moleculeName": "label", - "text":"Label Text Goes Here" - }, - "toggle":{ - "moleculeName": "toggle" - } - } - }, - { - "moleculeName": "stackItem", - "molecule": { - "moleculeName":"headlineBodyToggle", - "headlineBody":{ - "moleculeName": "headlineBody", - "headline":{ - "moleculeName": "label", - "text": "Headline Text Goes Here" - }, - "body":{ - "moleculeName": "label", - "text": "Body Text Goes Here" - } - }, - "toggle":{ - "moleculeName": "toggle" - } + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textView", + "fieldKey": "firstName", + "type": "text", + "errorMessage": "Please enter a valid first name.", + "placeholder": "John A", + "titleLabel": { + "moleculeName": "label", + "text": "First Name" } } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textField", + "fieldKey": "lastName", + "type": "text", + "placeholder": "Smith", + "errorMessage": "Please enter a valid last name.", + "titleLabel": { + "moleculeName": "label", + "text": "Last Name" + } + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textField", + "fieldKey": "phoneNumber", + "type": "phone", + "placeholder": "212-555-1234", + "title": "Contact Phone Number", + "errorMessage": "Please enter a valid phone number." + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textField", + "fieldKey": "emailID", + "type": "text", + "placeholder": "JSMith123@gmail.com", + "title": "Email", + "errorMessage": "Please enter a valid greeting name." + } + }, + { + "moleculeName": "stackItem", + "molecule": { + "moleculeName": "textField", + "fieldKey": "zipcode", + "type": "number", + "placeholder": "90210", + "title": "Zip Code", + "errorMessage": "Please enter a valid zip code." + } + } ] }, "footer": { @@ -84,7 +105,36 @@ { "groupName": "default", "rules": [ - + { + "type": "regex", + "fields": [ + "emailID" + ], + "regex": "^[a-zA-Z0-9](\\.?\\_?\\-?[a-zA-Z0-9]){0,}@[a-zA-Z0-9-_]+\\.([a-zA-Z0-9-_]{1,}\\.){0,}[a-zA-Z]{2,}$" + }, + { + "type": "regex", + "fields": [ + "zipcode" + ], + "regex": "^\\d{5}(?:[-\\s]\\d{4})?$" + }, + { + "regex": "^(\\d{3})[\\s.-]{0,1}(\\d{3})[\\s.-]{0,1}(\\d{4})$", + "type": "regex", + "fields": [ + "phoneNumber" + ] + }, + { + "type": "allRequired", + "ruleId": "requiredRule", + "fields": [ + "emailID", + "firstName", + "lastName" + ] + } ] } ] diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/TileleListSample.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/TileleListSample.json new file mode 100644 index 0000000..4e56264 --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/TileleListSample.json @@ -0,0 +1,110 @@ +{ + "ResponseInfo": {}, + "Page": { + "pageType": "contactUs", + "screenHeading": "Select an international plan", + "template": "list", + "molecules": [ + { + "moleculeName": "listItem", + "molecule": { + "directionalIcon": { + "size": "medium" + }, + "moleculeName": "tilelet", + "subTitle": { + "text": "You are enrolled in Auto Pay & paper-free billing." + }, + "title": { + "text": "You’re getting $50 off on your monthly bill." + }, + "action": { + "actionType": "openPage", + "analyticsData": { + "vzdl.page.displayChannel": "mva", + "vzwi.mvmapp.pageTypeLink": "settingsLanding|Auto Pay discount", + "vzdl.page.id": "settingslanding", + "vzdl.page.linkName": "Auto Pay discount", + "vzdl.page.sourceChannel": "mva", + "vzdl.page.name": "settings landing" + }, + "pageType": "managePmts", + "presentationStyle": "push", + "requestURL": "https://mobile-exp-qa2.vzw.com/mobile/nsa/nos/gw/launchapp/l2/webview", + "extraParameters": { + "pageTitle": "Auto Pay discount", + "screenHeading": "Auto Pay discount", + "browserUrl": "https://vzwqa2.verizonwireless.com/digital/nsa/secure/ui/payment/settings#/enrollAandP", + "locale": "EN", + "flowName": "accountsettings" + }, + "title": "Auto Pay discount" + } + } + }, + { + "moleculeName":"listItem", + "molecule": { + "moleculeName": "twoButtonView", + "primaryButton": { + "moleculeName": "button", + "title": "Edit", + "groupName": "default", + "action": { + "actionType": "openPage", + "pageType": "updateProfile", + "extraParameters": { + "from": "none" + }, + "presentationStyle": "push" + } + } + } + },{ + "moleculeName":"listItem", + "molecule": { + "moleculeName": "label", + "text": "afa\ndasfdsa\nadfadfda\nasadfsafa\nafsafsa\nafsadfas\nadffafaf" + } + },{ + "moleculeName":"listItem", + "molecule": { + "moleculeName": "label", + "text": "afa\ndasfdsa\nadfadfda\nasadfsafa\nafsafsa\nafsadfas\nadffafaf" + } + },{ + "moleculeName":"listItem", + "molecule": { + "moleculeName": "label", + "text": "afa\ndasfdsa\nadfadfda\nasadfsafa\nafsafsa\nafsadfas\nadffafaf\ndafsdssfafs" + } + },{ + "moleculeName":"listItem", + "molecule": { + "moleculeName": "label", + "text": "afa\ndasfdsa\nadfadfda\nasadfsafa\nafsafsa\nafsadfas\nadffafaf\n\nadsfa\nadfs" + } + },{ + "moleculeName":"listItem", + "molecule": { + "moleculeName": "label", + "text": "afa\ndasfdsa\nadfadfda\nasadfsafa\nafsafsa\nafsadfas\nadffafafttttt" + } + },{ + "moleculeName":"listItem", + "molecule": { + "moleculeName": "label", + "text": "afa\ndasfdsa\nadfadfda\nasadfsafa\nafsafsa\nafsadfas\nadffafaf\n\nadsfa\nadfs" + } + },{ + "moleculeName":"listItem", + "molecule": { + "moleculeName": "label", + "text": "afa\ndasfdsa\nadfadfda\nasadfsafa\nafsafsa\nafsadfas\nadffafafttttt" + } + } + ], + "middle": { + } + } +} diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/TileletSample.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/TileletSample.json new file mode 100644 index 0000000..e9b5dda --- /dev/null +++ b/JSONCreator_iOS/JSONCreator/JSON/Samples/TileletSample.json @@ -0,0 +1,64 @@ +{ + "Page": { + "template": "stack", + "pageType": "moleculeStack", + "screenHeading": "Tilet Sample", + "hideFabOverlay": true, + "suppressPostLaunchRequests": false, + "tabBarHidden": true, + "header": { + "moleculeName": "header", + "molecule": { + "moleculeName": "headlineBody", + "headline": { + "moleculeName": "label", + "text": "Tilet Variations" + } + } + }, + "stack": { + "moleculeName": "stack", + "molecules": [ + { + "moleculeName": "stackItem", + "molecule": + { + "directionalIcon": { + "size": "medium" + }, + "moleculeName": "tilelet", + "subTitle": { + "text": "You are enrolled in Auto Pay & paper-free billing." + }, + "title": { + "text": "You’re getting $50 off on your monthly bill." + }, + "action": { + "actionType": "openPage", + "analyticsData": { + "vzdl.page.displayChannel": "mva", + "vzwi.mvmapp.pageTypeLink": "settingsLanding|Auto Pay discount", + "vzdl.page.id": "settingslanding", + "vzdl.page.linkName": "Auto Pay discount", + "vzdl.page.sourceChannel": "mva", + "vzdl.page.name": "settings landing" + }, + "pageType": "managePmts", + "presentationStyle": "push", + "requestURL": "https://mobile-exp-qa2.vzw.com/mobile/nsa/nos/gw/launchapp/l2/webview", + "extraParameters": { + "pageTitle": "Auto Pay discount", + "screenHeading": "Auto Pay discount", + "browserUrl": "https://vzwqa2.verizonwireless.com/digital/nsa/secure/ui/payment/settings#/enrollAandP", + "locale": "EN", + "flowName": "accountsettings" + }, + "title": "Auto Pay discount" + } + } + } + ] + }, + "footer": {} + } +} diff --git a/JSONCreator_iOS/JSONCreator/JSON/Samples/tiletSample.json b/JSONCreator_iOS/JSONCreator/JSON/Samples/tiletSample.json deleted file mode 100644 index c7c4bdd..0000000 --- a/JSONCreator_iOS/JSONCreator/JSON/Samples/tiletSample.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "Page": { - "template": "stack", - "pageType": "moleculeStack", - "screenHeading": "Tilet Sample", - "hideFabOverlay": true, - "suppressPostLaunchRequests": false, - "tabBarHidden": true, - "header": { - "moleculeName": "header", - "molecule": { - "moleculeName": "headlineBody", - "headline": { - "moleculeName": "label", - "text": "Tilet Variations" - } - } - }, - "stack": { - "moleculeName": "stack", - "molecules": [ - { - "moleculeName": "stackItem", - "molecule": { - "moleculeName": "tilet", - "surface": "light", - "title": { - "text": "Save $XX on your monthly bill.", - "typographicalStyle": "BoldTitleSmall" - }, - "subTitle": { - "text": "Enroll in Auto Pay & paper-free billing to save on your monthly bill.", - "typographicalStyle": "BodySmall", - "textColor": "primary" - }, - "directionalIcon": { - "size": "medium", - "surface": "dark" - } - } - } - ] - }, - "footer": {} - } -} diff --git a/JSONCreator_iOS/JSONCreator/TestToggle.swift b/JSONCreator_iOS/JSONCreator/TestToggle.swift index 92f92f4..61c7c08 100644 --- a/JSONCreator_iOS/JSONCreator/TestToggle.swift +++ b/JSONCreator_iOS/JSONCreator/TestToggle.swift @@ -20,7 +20,7 @@ import VDS Container: The background of the toggle control. Knob: The circular indicator that slides on the container. */ -open class TestToggle: ToggleBase, VDSMoleculeViewProtocol { +open class TestToggle: VDS.Toggle, VDSMoleculeViewProtocol { //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- @@ -33,30 +33,25 @@ open class TestToggle: ToggleBase, VDSMoleculeViewProtocol { //-------------------------------------------------- public override func initialSetup() { super.initialSetup() - - publisher(for: .touchUpInside) - .sink {[weak self] toggle in - guard let self = self else { return } - self.toggle() - }.store(in: &subscribers) - publisher(for: .valueChanged) .sink {[weak self] toggle in guard let self = self else { return } self.valueChanged(isOn: toggle.isOn) }.store(in: &subscribers) - - accessibilityLabelEnabled = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") - accessibilityLabelDisabled = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") - accessibilityHintEnabled = MVMCoreUIUtility.hardcodedString(withKey: "AccToggleHint") - accessibilityHintDisabled = MVMCoreUIUtility.hardcodedString(withKey: "AccDisabled") - accessibilityValueEnabled = MVMCoreUIUtility.hardcodedString(withKey: "AccOn") - accessibilityValueDisabled = MVMCoreUIUtility.hardcodedString(withKey: "AccOff") } // MARK:- MVMCoreViewProtocol open func updateView(_ size: CGFloat) {} + override open func updateView() { + super.updateView() + + //accessibility + accessibilityLabel = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") + accessibilityHint = isEnabled ? MVMCoreUIUtility.hardcodedString(withKey: "AccToggleHint") : MVMCoreUIUtility.hardcodedString(withKey: "AccDisabled") + accessibilityValue = isOn ? MVMCoreUIUtility.hardcodedString(withKey: "AccOn") : MVMCoreUIUtility.hardcodedString(withKey: "AccOff") + } + open func viewModelDidUpdate() { guard let viewModel else { return } From cb3b932c0422be95a9118fb9847b0b19eb909b81 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 6 Apr 2023 15:26:19 -0500 Subject: [PATCH 14/15] removed testtoggle Signed-off-by: Matt Bruce --- .../JSONCreator.xcodeproj/project.pbxproj | 4 - JSONCreator_iOS/JSONCreator/AppDelegate.swift | 4 +- JSONCreator_iOS/JSONCreator/TestToggle.swift | 211 ------------------ 3 files changed, 2 insertions(+), 217 deletions(-) delete mode 100644 JSONCreator_iOS/JSONCreator/TestToggle.swift diff --git a/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj b/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj index ec1f6d1..48c0ec6 100644 --- a/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj +++ b/JSONCreator_iOS/JSONCreator.xcodeproj/project.pbxproj @@ -88,7 +88,6 @@ EA5B696F2866BC1000B17D2E /* MVMCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EA5B696C2866BC1000B17D2E /* MVMCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; EA5B69702866BC1000B17D2E /* MVMCoreUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA5B696D2866BC1000B17D2E /* MVMCoreUI.framework */; }; EA5B69712866BC1000B17D2E /* MVMCoreUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EA5B696D2866BC1000B17D2E /* MVMCoreUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - EA797B2C2902D4BB00DBAFE6 /* TestToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA797B2B2902D4BB00DBAFE6 /* TestToggle.swift */; }; EAA658152875FA5E00484A7D /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA658142875FA5E00484A7D /* VDSFormControlsTokens.xcframework */; }; EAA658162875FA5E00484A7D /* VDSFormControlsTokens.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = EAA658142875FA5E00484A7D /* VDSFormControlsTokens.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; EACA5E5E2853DBC900CBA65B /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EACA5E5D2853DBC900CBA65B /* VDSColorTokens.xcframework */; }; @@ -176,7 +175,6 @@ EA3361FA2891D54A0071C351 /* VDSTypographyTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSTypographyTokens.xcframework; path = ../SharedFrameworks/VDSTypographyTokens.xcframework; sourceTree = ""; }; EA5B696C2866BC1000B17D2E /* MVMCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA5B696D2866BC1000B17D2E /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - EA797B2B2902D4BB00DBAFE6 /* TestToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestToggle.swift; sourceTree = ""; }; EA7E676927582F2200ABF773 /* MVMCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA7E676A27582F2200ABF773 /* MVMCoreUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = MVMCoreUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EAA658142875FA5E00484A7D /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = ""; }; @@ -241,7 +239,6 @@ D2B1E3FB22F4A6930065F95C /* Assets.xcassets */, D2B1E3FD22F4A6930065F95C /* LaunchScreen.storyboard */, D2B1E40022F4A6930065F95C /* Info.plist */, - EA797B2B2902D4BB00DBAFE6 /* TestToggle.swift */, ); path = JSONCreator; sourceTree = ""; @@ -514,7 +511,6 @@ D2FC4FB025897ACB00061EA4 /* OrderTracker.swift in Sources */, EA09CDD8282C40CC00A7835F /* GMFGBLEHandlerProtocol.swift in Sources */, EA09CDDC282C40CC00A7835F /* GMFGFotaHandler.swift in Sources */, - EA797B2C2902D4BB00DBAFE6 /* TestToggle.swift in Sources */, EA09CDD6282C40CC00A7835F /* GMFGConstant.swift in Sources */, D27564C925939E91003CA713 /* Links.swift in Sources */, EA09CDE6282C416C00A7835F /* BluetoothDebuggableProtocol.swift in Sources */, diff --git a/JSONCreator_iOS/JSONCreator/AppDelegate.swift b/JSONCreator_iOS/JSONCreator/AppDelegate.swift index 02f7b84..c91ecf1 100644 --- a/JSONCreator_iOS/JSONCreator/AppDelegate.swift +++ b/JSONCreator_iOS/JSONCreator/AppDelegate.swift @@ -88,7 +88,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele // MARK: - Split view - func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool { + func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewControlller:UIViewController, onto primaryViewController:UIViewController) -> Bool { //guard let secondaryAsNavController = secondaryViewController as? UINavigationController else { return false } //guard let _ = secondaryAsNavController.topViewController as? DetailViewController else { return false } return true @@ -122,6 +122,6 @@ extension AppDelegate: MVMCoreGlobalTopAlertDelegateProtocol { extension AppDelegate { func register(){ - ModelRegistry.register(handler: TestToggle.self, for: TestToggleModel.self) + //ModelRegistry.register(handler: AtomicClass.self, for: AtomicClasseModel.self) } } diff --git a/JSONCreator_iOS/JSONCreator/TestToggle.swift b/JSONCreator_iOS/JSONCreator/TestToggle.swift deleted file mode 100644 index 92f92f4..0000000 --- a/JSONCreator_iOS/JSONCreator/TestToggle.swift +++ /dev/null @@ -1,211 +0,0 @@ -// -// TestToggle.swift -// JSONCreator -// -// Created by Matt Bruce on 10/21/22. -// Copyright © 2022 Verizon Wireless. All rights reserved. -// - -import Foundation -import MVMCore -import MVMCoreUI -import UIKit -import VDS - -/** - A custom implementation of Apple's UISwitch. - - By default this class begins in the off state. - - Container: The background of the toggle control. - Knob: The circular indicator that slides on the container. - */ -open class TestToggle: ToggleBase, VDSMoleculeViewProtocol { - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - public var viewModel: TestToggleModel! - public var delegateObject: MVMCoreUIDelegateObject? - public var additionalData: [AnyHashable: Any]? - - //-------------------------------------------------- - // MARK: - Initializers - //-------------------------------------------------- - public override func initialSetup() { - super.initialSetup() - - publisher(for: .touchUpInside) - .sink {[weak self] toggle in - guard let self = self else { return } - self.toggle() - }.store(in: &subscribers) - - publisher(for: .valueChanged) - .sink {[weak self] toggle in - guard let self = self else { return } - self.valueChanged(isOn: toggle.isOn) - }.store(in: &subscribers) - - accessibilityLabelEnabled = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") - accessibilityLabelDisabled = MVMCoreUIUtility.hardcodedString(withKey: "Toggle_buttonlabel") - accessibilityHintEnabled = MVMCoreUIUtility.hardcodedString(withKey: "AccToggleHint") - accessibilityHintDisabled = MVMCoreUIUtility.hardcodedString(withKey: "AccDisabled") - accessibilityValueEnabled = MVMCoreUIUtility.hardcodedString(withKey: "AccOn") - accessibilityValueDisabled = MVMCoreUIUtility.hardcodedString(withKey: "AccOff") - } - - // MARK:- MVMCoreViewProtocol - open func updateView(_ size: CGFloat) {} - - open func viewModelDidUpdate() { - guard let viewModel else { return } - - additionalData = additionalData.dictionaryAdding(key: KeySourceModel, value: viewModel) - } - - private func valueChanged(isOn: Bool){ - guard let viewModel else { return } - //sync the value on the viewModel - viewModel.selected = isOn - - //tell the form you changed - _ = FormValidator.validate(delegate: self.delegateObject?.formHolderDelegate) - - if viewModel.action != nil || viewModel.alternateAction != nil { - var action: ActionModelProtocol? - if isOn { - action = viewModel.action - } else { - action = viewModel.alternateAction ?? viewModel.action - } - if let action { - MVMCoreUIActionHandler.performActionUnstructured(with: action, - sourceModel: viewModel, - additionalData: additionalData, - delegateObject: delegateObject) - } - } - - print("toggle value changed to: \(isOn)") - print("viewModel server value: \(viewModel.formFieldServerValue()!)") - } - - public static func estimatedHeight(with model: MoleculeModelProtocol, _ delegateObject: MVMCoreUIDelegateObject?) -> CGFloat? { - return 44 - } - - private typealias ActionDefinition = (model: ActionModelProtocol, - sourceModel: MoleculeModelProtocol?) - - private func performActionUnstructured(definition: ActionDefinition) { - MVMCoreUIActionHandler.performActionUnstructured(with: definition.model, - sourceModel: definition.sourceModel, - additionalData: additionalData, - delegateObject: delegateObject) - } -} -// MARK: - MVMCoreUIViewConstrainingProtocol -extension TestToggle { - - public func needsToBeConstrained() -> Bool { true } - - public func horizontalAlignment() -> UIStackView.Alignment { .trailing } -} - -public class TestToggleModel: MoleculeModelProtocol, FormFieldProtocol { - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - - public static var identifier: String = "testToggle" - public var backgroundColor: Color? //not used - - public var selected: Bool = false - public var enabled: Bool = true - public var readOnly: Bool = false - public var action: ActionModelProtocol? - public var alternateAction: ActionModelProtocol? - public var accessibilityText: String? - public var fieldKey: String? - public var groupName: String = FormValidator.defaultGroupName - public var baseValue: AnyHashable? - - //-------------------------------------------------- - // MARK: - Keys - //-------------------------------------------------- - - private enum CodingKeys: String, CodingKey { - case moleculeName - case state - case enabled - case readOnly - case action - case accessibilityIdentifier - case alternateAction - case accessibilityText - case fieldKey - case groupName - } - - //-------------------------------------------------- - // MARK: - Form Valdiation - //-------------------------------------------------- - - public func formFieldValue() -> AnyHashable? { - guard enabled else { return nil } - return selected - } - - //-------------------------------------------------- - // MARK: - Server Value - //-------------------------------------------------- - open func formFieldServerValue() -> AnyHashable? { - return formFieldValue() - } - - //-------------------------------------------------- - // MARK: - Initializer - //-------------------------------------------------- - - public init(_ state: Bool) { - selected = state - baseValue = state - } - - //-------------------------------------------------- - // MARK: - Codec - //-------------------------------------------------- - - required public init(from decoder: Decoder) throws { - let typeContainer = try decoder.container(keyedBy: CodingKeys.self) - - if let state = try typeContainer.decodeIfPresent(Bool.self, forKey: .state) { - selected = state - } - action = try typeContainer.decodeModelIfPresent(codingKey: .action) - alternateAction = try typeContainer.decodeModelIfPresent(codingKey: .alternateAction) - accessibilityText = try typeContainer.decodeIfPresent(String.self, forKey: .accessibilityText) - baseValue = selected - fieldKey = try typeContainer.decodeIfPresent(String.self, forKey: .fieldKey) - if let gName = try typeContainer.decodeIfPresent(String.self, forKey: .groupName) { - groupName = gName - } - enabled = try typeContainer.decodeIfPresent(Bool.self, forKey: .enabled) ?? true - readOnly = try typeContainer.decodeIfPresent(Bool.self, forKey: .readOnly) ?? false - - - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encodeIfPresent(accessibilityIdentifier, forKey: .accessibilityIdentifier) - try container.encodeModelIfPresent(action, forKey: .action) - try container.encodeModelIfPresent(alternateAction, forKey: .alternateAction) - try container.encode(moleculeName, forKey: .moleculeName) - try container.encode(selected, forKey: .state) - try container.encode(enabled, forKey: .enabled) - try container.encodeIfPresent(fieldKey, forKey: .fieldKey) - try container.encodeIfPresent(groupName, forKey: .groupName) - try container.encode(readOnly, forKey: .readOnly) - } -} From 60bb674e944b916fd228ed37eb0c6df2970a1223 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Thu, 6 Apr 2023 15:39:07 -0500 Subject: [PATCH 15/15] changed dependency Signed-off-by: Matt Bruce --- dependency.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependency.txt b/dependency.txt index 4cf000f..e567159 100644 --- a/dependency.txt +++ b/dependency.txt @@ -1,6 +1,6 @@ mvm_core https://gitlab.verizon.com/BPHV_MIPS/mvm_core.git develop -mvm_core_ui https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui.git feature/ONEAPP-2811-Tilet +mvm_core_ui https://gitlab.verizon.com/BPHV_MIPS/mvm_core_ui.git develop vds_ios https://gitlab.verizon.com/BPHV_MIPS/vds_ios.git develop