From fa6dc34b22976a8225420cff917bbc2e9019d652 Mon Sep 17 00:00:00 2001 From: Kamil Czajka Date: Fri, 12 Jun 2020 08:51:37 +0200 Subject: [PATCH] Release 2.0.3 --- .gitignore | 2 +- SiliconLabsApp-Bridging-Header.h | 2 + SiliconLabsApp.xcodeproj/project.pbxproj | 112 ++++-- .../BluetoothControllers/SILCentralManager.m | 59 +-- .../SILDiscoveredPeripheral.h | 4 +- .../SILDiscoveredPeripheral.m | 13 +- .../Categories/CBService+Categories.m | 4 +- .../NSString+SILBrowserNotifications.h | 5 + .../NSString+SILBrowserNotifications.m | 5 + SiliconLabsApp/Categories/UIImage+SILImages.h | 4 + SiliconLabsApp/Categories/UIImage+SILImages.m | 8 +- .../Categories/UIViewController+Toast.swift | 57 +++ .../Helpers/SILRealmConfiguration.swift | 77 ++++ .../FieldRows/SILBitRowModel.m | 4 + .../FieldRows/SILEnumerationFieldRowModel.m | 4 + .../FieldRows/SILValueFieldRowModel.m | 4 + .../SILCharacteristicTableModel.h | 2 + .../SILCharacteristicTableModel.m | 16 + .../Models/SILAdTypeCBPeripheralDecoder.swift | 315 ++++++++++++++++ .../Models/SILAdTypeEddystoneDecoder.swift | 267 ++++++++++++++ .../Models/SILAdvertisementDataModel.h | 18 +- SiliconLabsApp/Models/SILBeacon.h | 14 +- SiliconLabsApp/Models/SILBeacon.m | 74 ++-- SiliconLabsApp/Models/SILBitFieldFieldModel.m | 4 + .../Models/SILBrowserConnectionsViewModel.h | 2 + .../Models/SILBrowserConnectionsViewModel.m | 71 +++- .../Models/SILBrowserSavedSearches.h | 3 +- .../Models/SILBrowserSavedSearches.m | 4 +- .../Models/SILDisconnectionToastModel.swift | 70 ++++ .../SILDiscoveredPeripheralDisplayData.h | 1 - .../SILDiscoveredPeripheralDisplayData.m | 86 ++--- .../Models/SILEncodingPseudoFieldRowModel.m | 4 + .../SILGattPropertiesErrorToastModel.swift | 71 ++++ .../Models/SILOTAFirmwareUpdateManager.m | 34 +- .../Models/SILSavedSearchesRealmModel.h | 3 +- .../Models/SILSavedSearchesRealmModel.m | 3 +- SiliconLabsApp/Models/SILToastModelType.swift | 17 + SiliconLabsApp/Models/SILUUIDProvider.h | 10 +- SiliconLabsApp/Models/SILUUIDProvider.m | 26 +- .../Protocols/SILCharacteristicFieldRow.h | 1 + SiliconLabsApp/SILAppDelegate.m | 1 + .../SILAppBluetoothBrowser.storyboard | 162 +++------ .../SILAppBluetoothBrowserDetails.storyboard | 98 +++-- .../DevelopApps/SILKeychain.storyboard | 173 ++++++++- .../Contents.json | 6 +- .../MenuIcon.imageset/MenuIcon.png | Bin 0 -> 271 bytes .../MenuIcon.imageset/MenuIcon2-1.png | Bin 0 -> 522 bytes .../MenuIcon.imageset/MenuIcon2.png | Bin 0 -> 522 bytes .../beacon.imageset/icon - beacon - light.png | Bin 480 -> 0 bytes .../icon - beacon - light@2x.png | Bin 807 -> 0 bytes .../icon - beacon - light@3x.png | Bin 1217 -> 0 bytes .../Contents.json | 6 +- .../beaconDark.imageset/icon - beacon.png | Bin 0 -> 716 bytes .../beaconDark.imageset/icon - beacon@2x.png | Bin 0 -> 1669 bytes .../beaconDark.imageset/icon - beacon@3x.png | Bin 0 -> 2604 bytes .../beaconWhite.imageset/Contents.json | 23 ++ .../icon - beacon-white.png | Bin 0 -> 528 bytes .../icon - beacon-white@2x.png | Bin 0 -> 1200 bytes .../icon - beacon-white@3x.png | Bin 0 -> 1796 bytes .../connectable.imageset/Contents.json | 6 +- .../connectable.imageset/connectable.png | Bin 1243 -> 0 bytes .../connectable.imageset/connectable@2x.png | Bin 2291 -> 0 bytes .../connectable.imageset/connectable@3x.png | Bin 3631 -> 0 bytes .../connectable.imageset/icon - bluetooth.png | Bin 0 -> 544 bytes .../icon - bluetooth@2x.png | Bin 0 -> 968 bytes .../icon - bluetooth@3x.png | Bin 0 -> 1270 bytes .../favouriteOff.imageset/Contents.json | 6 +- .../favouriteOff.imageset/favourite.png | Bin 1007 -> 0 bytes .../favouriteOff.imageset/favourite@2x.png | Bin 1954 -> 0 bytes .../favouriteOff.imageset/favourite@3x.png | Bin 3070 -> 0 bytes .../icon - star - off.png | Bin 0 -> 482 bytes .../icon - star - off@2x.png | Bin 0 -> 785 bytes .../icon - star - off@3x.png | Bin 0 -> 1138 bytes .../filterOff.imageset/Contents.json | 23 ++ .../icon - filter - off.png | Bin 0 -> 333 bytes .../icon - filter - off@2x.png | Bin 0 -> 438 bytes .../icon - filter - off@3x.png | Bin 0 -> 582 bytes .../Contents.json | 6 +- .../filterOffSelected.png | Bin 0 -> 237 bytes .../filterOffSelected@2x.png | Bin 0 -> 358 bytes .../filterOffSelected@3x.png | Bin 0 -> 503 bytes .../Contents.json | 6 +- .../icon - filter - active@2x.png | Bin 0 -> 561 bytes .../icon - filter - active@3x.png | Bin 0 -> 1040 bytes .../icon_-_filter_-_active3x_20x20.png | Bin 0 -> 1164 bytes .../Contents.json | 6 +- .../icon - filter - active.png | Bin 0 -> 497 bytes .../icon - filter - active@2x.png | Bin 0 -> 834 bytes .../icon - filter - active@3x.png | Bin 0 -> 1469 bytes .../icon - beacon - dark.png | Bin 524 -> 0 bytes .../icon - beacon - dark@2x.png | Bin 887 -> 0 bytes .../icon - beacon - dark@3x.png | Bin 1318 -> 0 bytes .../Contents.json | 23 -- .../icon - beacon - dark.png | Bin 524 -> 0 bytes .../icon - beacon - dark@2x.png | Bin 887 -> 0 bytes .../icon - beacon - dark@3x.png | Bin 1318 -> 0 bytes .../Contents.json | 23 -- .../icon - beacon - light.png | Bin 480 -> 0 bytes .../icon - beacon - light@2x.png | Bin 807 -> 0 bytes .../icon - beacon - light@3x.png | Bin 1217 -> 0 bytes .../icon - star - off.png | Bin 443 -> 0 bytes .../icon - star - off@2x.png | Bin 732 -> 0 bytes .../icon - star - off@3x.png | Bin 1053 -> 0 bytes .../icon - star - off.png | Bin 443 -> 0 bytes .../icon - star - off@2x.png | Bin 732 -> 0 bytes .../icon - star - off@3x.png | Bin 1053 -> 0 bytes .../icon - wifi - dark.png | Bin 517 -> 0 bytes .../icon - wifi - dark@2x.png | Bin 935 -> 0 bytes .../icon - wifi - dark@3x.png | Bin 1333 -> 0 bytes .../icon - wifi - dark.png | Bin 517 -> 0 bytes .../icon - wifi - dark@2x.png | Bin 935 -> 0 bytes .../icon - wifi - dark@3x.png | Bin 1333 -> 0 bytes .../icon - wifi - light.png | Bin 495 -> 0 bytes .../icon - wifi - light@2x.png | Bin 888 -> 0 bytes .../icon - wifi - light@3x.png | Bin 1263 -> 0 bytes .../Contents.json | 6 +- .../loading pulse.imageset/loading pulse.png | Bin 0 -> 13108 bytes .../loading pulse@2x.png | Bin 0 -> 32209 bytes .../loading pulse@3x.png | Bin 0 -> 54013 bytes .../rssi.imageset/icon - wifi - light.png | Bin 495 -> 540 bytes .../rssi.imageset/icon - wifi - light@2x.png | Bin 888 -> 943 bytes .../rssi.imageset/icon - wifi - light@3x.png | Bin 1263 -> 1322 bytes .../Contents.json | 0 .../icon - wifi - light.png | Bin 0 -> 210 bytes .../icon - wifi - light@2x.png | Bin 0 -> 400 bytes .../icon - wifi - light@3x.png | Bin 0 -> 635 bytes .../SupportingFiles/Localizable.strings | 6 +- .../Info/SILAppSelectionInfoViewController.m | 2 +- .../SILAppSelectionViewController.h | 1 + .../SILAppSelectionViewController.m | 1 - .../SILDevelopNavigationViewController.m | 1 + .../SILDebugServicesMenuTableViewCell.swift | 25 ++ .../SILDebugServicesMenuTableViewCell.xib | 46 +++ .../SILDebugServicesMenuViewController.swift | 60 ++++ ...ugServicesMenuViewControllerDelegate.swift | 15 + .../Details/OTA}/SILOTAHUDView.h | 4 +- .../Details/OTA}/SILOTAHUDView.m | 0 .../Details/OTA}/SILOTAHUDView.xib | 58 +-- .../OTA/SILOTAProgressViewController.m | 2 +- .../Details/OTA/SILOTASetupViewController.m | 5 +- .../Details/OTA/SILOTAUICoordinator.m | 35 +- .../Filter/SILBrowserFilterViewController.m | 106 ------ .../Info/SILKeychainInfoViewController.swift | 38 ++ ...ILKeychainInfoViewControllerDelegate.swift | 13 + .../KeyChains/SILKeychainViewController.swift | 44 +++ .../SILBluetoothBrowserViewController.m | 60 ++-- .../Utils/SILAnimatedUIButton.swift | 28 ++ ...SILBluetoothBrowserExpandableViewManager.h | 3 +- ...SILBluetoothBrowserExpandableViewManager.m | 31 +- .../SILBrowserDeviceAdTypeViewCell.swift | 22 ++ .../Views/SILBrowserServiceViewCell.swift | 22 -- ...CharacteristicEncodingFieldTableViewCell.h | 1 + ...CharacteristicEncodingFieldTableViewCell.m | 6 + .../SILDebugCharacteristicTableViewCell.m | 86 ++--- .../SILDebugServicesViewController.m | 134 ++++++- .../ViewModels/DebugDeviceViewModel.swift | 2 +- .../SILAdvertisementDataViewModel.m | 69 ++-- .../ViewModels/SILBrowserFilterViewModel.h | 1 - .../ViewModels/SILBrowserFilterViewModel.m | 31 +- ...DiscoveredPeripheralDisplayDataViewModel.h | 1 - ...DiscoveredPeripheralDisplayDataViewModel.m | 8 - SiliconLabsApp/Views/SILAlertBarView.h | 22 -- SiliconLabsApp/Views/SILAlertBarView.m | 76 ---- .../org.bluetooth.service.fitness_machine.xml | 338 ++++++++++++++++++ ...org.bluetooth.service.insulin_delivery.xml | 292 +++++++++++++++ ...ooth.service.internet_protocol_support.xml | 22 ++ ...rg.bluetooth.service.mesh_provisioning.xml | 64 ++++ .../org.bluetooth.service.mesh_proxy.xml | 64 ++++ ...oth.service.reconnection_configuration.xml | 13 + 169 files changed, 3050 insertions(+), 862 deletions(-) create mode 100644 SiliconLabsApp/Categories/UIViewController+Toast.swift create mode 100644 SiliconLabsApp/Helpers/SILRealmConfiguration.swift create mode 100644 SiliconLabsApp/Models/SILAdTypeCBPeripheralDecoder.swift create mode 100644 SiliconLabsApp/Models/SILAdTypeEddystoneDecoder.swift create mode 100644 SiliconLabsApp/Models/SILDisconnectionToastModel.swift create mode 100644 SiliconLabsApp/Models/SILGattPropertiesErrorToastModel.swift create mode 100644 SiliconLabsApp/Models/SILToastModelType.swift rename SiliconLabsApp/SupportingFiles/Images.xcassets/{icon - star - off-1.imageset => MenuIcon.imageset}/Contents.json (66%) create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/MenuIcon.imageset/MenuIcon.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/MenuIcon.imageset/MenuIcon2-1.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/MenuIcon.imageset/MenuIcon2.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/beacon.imageset/icon - beacon - light.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/beacon.imageset/icon - beacon - light@2x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/beacon.imageset/icon - beacon - light@3x.png rename SiliconLabsApp/SupportingFiles/Images.xcassets/{icon - star - off.imageset => beaconDark.imageset}/Contents.json (66%) create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/beaconDark.imageset/icon - beacon.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/beaconDark.imageset/icon - beacon@2x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/beaconDark.imageset/icon - beacon@3x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/beaconWhite.imageset/Contents.json create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/beaconWhite.imageset/icon - beacon-white.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/beaconWhite.imageset/icon - beacon-white@2x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/beaconWhite.imageset/icon - beacon-white@3x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/connectable.imageset/connectable.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/connectable.imageset/connectable@2x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/connectable.imageset/connectable@3x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/connectable.imageset/icon - bluetooth.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/connectable.imageset/icon - bluetooth@2x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/connectable.imageset/icon - bluetooth@3x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/favourite.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/favourite@2x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/favourite@3x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/icon - star - off.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/icon - star - off@2x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/icon - star - off@3x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/filterOff.imageset/Contents.json create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/filterOff.imageset/icon - filter - off.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/filterOff.imageset/icon - filter - off@2x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/filterOff.imageset/icon - filter - off@3x.png rename SiliconLabsApp/SupportingFiles/Images.xcassets/{icon - wifi - dark-1.imageset => filterOffSelected.imageset}/Contents.json (65%) create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/filterOffSelected.imageset/filterOffSelected.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/filterOffSelected.imageset/filterOffSelected@2x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/filterOffSelected.imageset/filterOffSelected@3x.png rename SiliconLabsApp/SupportingFiles/Images.xcassets/{icon - beacon - dark-1.imageset => filterOnActive.imageset}/Contents.json (62%) create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActive.imageset/icon - filter - active@2x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActive.imageset/icon - filter - active@3x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActive.imageset/icon_-_filter_-_active3x_20x20.png rename SiliconLabsApp/SupportingFiles/Images.xcassets/{beacon.imageset => filterOnActiveSelected.imageset}/Contents.json (63%) create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActiveSelected.imageset/icon - filter - active.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActiveSelected.imageset/icon - filter - active@2x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActiveSelected.imageset/icon - filter - active@3x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark-1.imageset/icon - beacon - dark.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark-1.imageset/icon - beacon - dark@2x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark-1.imageset/icon - beacon - dark@3x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark.imageset/Contents.json delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark.imageset/icon - beacon - dark.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark.imageset/icon - beacon - dark@2x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark.imageset/icon - beacon - dark@3x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - light.imageset/Contents.json delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - light.imageset/icon - beacon - light.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - light.imageset/icon - beacon - light@2x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - light.imageset/icon - beacon - light@3x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - star - off-1.imageset/icon - star - off.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - star - off-1.imageset/icon - star - off@2x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - star - off-1.imageset/icon - star - off@3x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - star - off.imageset/icon - star - off.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - star - off.imageset/icon - star - off@2x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - star - off.imageset/icon - star - off@3x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark-1.imageset/icon - wifi - dark.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark-1.imageset/icon - wifi - dark@2x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark-1.imageset/icon - wifi - dark@3x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark.imageset/icon - wifi - dark.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark.imageset/icon - wifi - dark@2x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark.imageset/icon - wifi - dark@3x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - light.imageset/icon - wifi - light.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - light.imageset/icon - wifi - light@2x.png delete mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - light.imageset/icon - wifi - light@3x.png rename SiliconLabsApp/SupportingFiles/Images.xcassets/{icon - wifi - dark.imageset => loading pulse.imageset}/Contents.json (65%) create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/loading pulse.imageset/loading pulse.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/loading pulse.imageset/loading pulse@2x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/loading pulse.imageset/loading pulse@3x.png rename SiliconLabsApp/SupportingFiles/Images.xcassets/{icon - wifi - light.imageset => rssiWhite.imageset}/Contents.json (100%) create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/rssiWhite.imageset/icon - wifi - light.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/rssiWhite.imageset/icon - wifi - light@2x.png create mode 100644 SiliconLabsApp/SupportingFiles/Images.xcassets/rssiWhite.imageset/icon - wifi - light@3x.png create mode 100644 SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/Menu/SILDebugServicesMenuTableViewCell.swift create mode 100644 SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/Menu/SILDebugServicesMenuTableViewCell.xib create mode 100644 SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/Menu/SILDebugServicesMenuViewController.swift create mode 100644 SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/Menu/SILDebugServicesMenuViewControllerDelegate.swift rename SiliconLabsApp/{Views => ViewControllers/BluetoothBrowser/Details/OTA}/SILOTAHUDView.h (87%) rename SiliconLabsApp/{Views => ViewControllers/BluetoothBrowser/Details/OTA}/SILOTAHUDView.m (100%) rename SiliconLabsApp/{Views => ViewControllers/BluetoothBrowser/Details/OTA}/SILOTAHUDView.xib (95%) create mode 100644 SiliconLabsApp/ViewControllers/BluetoothBrowser/KeyChains/Info/SILKeychainInfoViewController.swift create mode 100644 SiliconLabsApp/ViewControllers/BluetoothBrowser/KeyChains/Info/SILKeychainInfoViewControllerDelegate.swift create mode 100644 SiliconLabsApp/ViewControllers/BluetoothBrowser/Utils/SILAnimatedUIButton.swift create mode 100644 SiliconLabsApp/ViewControllers/BluetoothBrowser/Views/SILBrowserDeviceAdTypeViewCell.swift delete mode 100644 SiliconLabsApp/ViewControllers/BluetoothBrowser/Views/SILBrowserServiceViewCell.swift delete mode 100644 SiliconLabsApp/Views/SILAlertBarView.h delete mode 100644 SiliconLabsApp/Views/SILAlertBarView.m create mode 100644 SiliconLabsApp/XML/services/org.bluetooth.service.fitness_machine.xml create mode 100644 SiliconLabsApp/XML/services/org.bluetooth.service.insulin_delivery.xml create mode 100644 SiliconLabsApp/XML/services/org.bluetooth.service.internet_protocol_support.xml create mode 100644 SiliconLabsApp/XML/services/org.bluetooth.service.mesh_provisioning.xml create mode 100644 SiliconLabsApp/XML/services/org.bluetooth.service.mesh_proxy.xml create mode 100644 SiliconLabsApp/XML/services/org.bluetooth.service.reconnection_configuration.xml diff --git a/.gitignore b/.gitignore index 0e37ed07..7813ba0c 100644 --- a/.gitignore +++ b/.gitignore @@ -60,7 +60,7 @@ xcuserdata/ *.dSYM # CocoaPods - Refactored to standalone file - +Pods/* # Carthage - Refactored to standalone file diff --git a/SiliconLabsApp-Bridging-Header.h b/SiliconLabsApp-Bridging-Header.h index 8a9aa0ad..29a1eb71 100644 --- a/SiliconLabsApp-Bridging-Header.h +++ b/SiliconLabsApp-Bridging-Header.h @@ -20,3 +20,5 @@ #import "UIColor+SILColors.h" #import "NSString+SILBrowserNotifications.h" #import "SILBluetoothBrowser+Constants.h" +#import "SILAdvertisementDataModel.h" +#import "SILBluetoothModelManager.h" diff --git a/SiliconLabsApp.xcodeproj/project.pbxproj b/SiliconLabsApp.xcodeproj/project.pbxproj index 06e161b2..f74e8000 100644 --- a/SiliconLabsApp.xcodeproj/project.pbxproj +++ b/SiliconLabsApp.xcodeproj/project.pbxproj @@ -54,7 +54,6 @@ 078138591BE9B014001EFE7E /* SILTextFieldEntryCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 078138571BE9B014001EFE7E /* SILTextFieldEntryCell.m */; }; 0797E34D1BF0071D0046EF0E /* SILTextFieldEntryCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0797E34B1BF0071D0046EF0E /* SILTextFieldEntryCell.xib */; }; 0797E34E1BF0071D0046EF0E /* SILDebugCharacteristicEncodingViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0797E34C1BF0071D0046EF0E /* SILDebugCharacteristicEncodingViewController.xib */; }; - 07A9563F1BDD40CB0028D333 /* SILAlertBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 07A9563E1BDD40CB0028D333 /* SILAlertBarView.m */; }; 07B1AD781BFD043000D4D454 /* SILBeaconViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 07B1AD771BFD043000D4D454 /* SILBeaconViewModel.m */; }; 07B1AD7B1BFD05CA00D4D454 /* SILBGBeaconViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 07B1AD7A1BFD05CA00D4D454 /* SILBGBeaconViewModel.m */; }; 07B1AD7E1BFD0A2100D4D454 /* SILIBeaconViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 07B1AD7D1BFD0A2100D4D454 /* SILIBeaconViewModel.m */; }; @@ -95,7 +94,6 @@ 0C2FCB0C1F9A542300F4F259 /* SILBluetoothModelManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 07BBA74D1BD5858F00C2B07E /* SILBluetoothModelManager.m */; }; 0C2FCB0D1F9A542300F4F259 /* SILSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = E607A3501A8D4FD100DAAFD3 /* SILSettings.m */; }; 0C2FCB0E1F9A542300F4F259 /* SILWeakTargetWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = E642661F1A855DA8006C6B2F /* SILWeakTargetWrapper.m */; }; - 0C2FCB0F1F9A542300F4F259 /* SILAlertBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 07A9563E1BDD40CB0028D333 /* SILAlertBarView.m */; }; 0C2FCB101F9A542300F4F259 /* SILBeaconRegistry.m in Sources */ = {isa = PBXBuildFile; fileRef = E6CCCFEB1A73040C0004B2F4 /* SILBeaconRegistry.m */; }; 0C2FCB111F9A542300F4F259 /* SILDebugServiceTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 07B8A8CF1BCD6E3B001948C1 /* SILDebugServiceTableViewCell.m */; }; 0C2FCB121F9A542300F4F259 /* SILDebugAdvDetailsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 078138461BE99502001EFE7E /* SILDebugAdvDetailsViewController.m */; }; @@ -282,7 +280,6 @@ 0F4E50FF21525FDC00F58ACE /* SILBluetoothModelManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 07BBA74D1BD5858F00C2B07E /* SILBluetoothModelManager.m */; }; 0F4E510021525FDC00F58ACE /* SILSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = E607A3501A8D4FD100DAAFD3 /* SILSettings.m */; }; 0F4E510121525FDC00F58ACE /* SILWeakTargetWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = E642661F1A855DA8006C6B2F /* SILWeakTargetWrapper.m */; }; - 0F4E510221525FDC00F58ACE /* SILAlertBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 07A9563E1BDD40CB0028D333 /* SILAlertBarView.m */; }; 0F4E510321525FDC00F58ACE /* SILBeaconRegistry.m in Sources */ = {isa = PBXBuildFile; fileRef = E6CCCFEB1A73040C0004B2F4 /* SILBeaconRegistry.m */; }; 0F4E510421525FDC00F58ACE /* SILDebugServiceTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 07B8A8CF1BCD6E3B001948C1 /* SILDebugServiceTableViewCell.m */; }; 0F4E510521525FDC00F58ACE /* SILDebugAdvDetailsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 078138461BE99502001EFE7E /* SILDebugAdvDetailsViewController.m */; }; @@ -464,8 +461,14 @@ 1E113D08244ECD6600442752 /* SILDiscoveredPeripheralIdentifierProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E113D07244ECD6600442752 /* SILDiscoveredPeripheralIdentifierProvider.swift */; }; 1E1907B123FD850700DD014C /* SILSavedSearchesRealmModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E1907B023FD850700DD014C /* SILSavedSearchesRealmModel.m */; }; 1E1907B523FD8B9900DD014C /* SILBeaconTypeRealmModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E1907B423FD8B9900DD014C /* SILBeaconTypeRealmModel.m */; }; + 1E1A43C1246EA3CB0052DE8D /* SILAdTypeCBPeripheralDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E1A43C0246EA3CB0052DE8D /* SILAdTypeCBPeripheralDecoder.swift */; }; + 1E1A43C324728F770052DE8D /* SILAdTypeEddystoneDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E1A43C224728F770052DE8D /* SILAdTypeEddystoneDecoder.swift */; }; + 1E1AE4AE247FCB7F00E5F238 /* SILRealmConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E1AE4AD247FCB7F00E5F238 /* SILRealmConfiguration.swift */; }; 1E2D26D8244050430006B84A /* SILDocumentPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E2D26D7244050430006B84A /* SILDocumentPickerViewController.swift */; }; 1E2D9CAC23BA48D600816EC0 /* SILBluetoothBrowserViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E2D9CAB23BA48D600816EC0 /* SILBluetoothBrowserViewController.m */; }; + 1E48A1E92484E27300C188C0 /* SILAnimatedUIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E48A1E82484E27300C188C0 /* SILAnimatedUIButton.swift */; }; + 1E48A1ED2484FBCF00C188C0 /* SILGattPropertiesErrorToastModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E48A1EC2484FBCF00C188C0 /* SILGattPropertiesErrorToastModel.swift */; }; + 1E48A1EF2484FC0B00C188C0 /* SILToastModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E48A1EE2484FC0B00C188C0 /* SILToastModelType.swift */; }; 1E4C37A42429095300C822E4 /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1E4C37A12429095300C822E4 /* Roboto-Bold.ttf */; }; 1E4C37A52429095300C822E4 /* Roboto-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1E4C37A22429095300C822E4 /* Roboto-Medium.ttf */; }; 1E4C37A62429095300C822E4 /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1E4C37A32429095300C822E4 /* Roboto-Regular.ttf */; }; @@ -480,6 +483,14 @@ 1E6AF80123CDB32800EE8280 /* SILBrowserConnectionsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E6AF80023CDB32800EE8280 /* SILBrowserConnectionsViewController.m */; }; 1E6AF80423CDB33600EE8280 /* SILBrowserFilterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E6AF80323CDB33600EE8280 /* SILBrowserFilterViewController.m */; }; 1E70A97B240403320021C51B /* SILBrowserConnectionsViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E70A97A240403320021C51B /* SILBrowserConnectionsViewModel.m */; }; + 1E90F5E42473E73E0013AABD /* SILDebugServicesMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E90F5E32473E73E0013AABD /* SILDebugServicesMenuViewController.swift */; }; + 1E90F5E72473E8340013AABD /* SILDebugServicesMenuTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E90F5E52473E8340013AABD /* SILDebugServicesMenuTableViewCell.swift */; }; + 1E90F5E82473E8340013AABD /* SILDebugServicesMenuTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1E90F5E62473E8340013AABD /* SILDebugServicesMenuTableViewCell.xib */; }; + 1E90F5EA2473EA650013AABD /* SILDebugServicesMenuViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E90F5E92473EA650013AABD /* SILDebugServicesMenuViewControllerDelegate.swift */; }; + 1E90F5EC24767C680013AABD /* UIViewController+Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E90F5EB24767C680013AABD /* UIViewController+Toast.swift */; }; + 1E90F5EE24767CC30013AABD /* SILDisconnectionToastModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E90F5ED24767CC30013AABD /* SILDisconnectionToastModel.swift */; }; + 1E90F5F12476C3C90013AABD /* SILKeychainInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E90F5EF2476C3C90013AABD /* SILKeychainInfoViewController.swift */; }; + 1E90F5F42477B1D00013AABD /* SILKeychainInfoViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E90F5F32477B1D00013AABD /* SILKeychainInfoViewControllerDelegate.swift */; }; 1E93CDE423A8EBF90022640E /* SILUILabels.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E93CDE323A8EBF90022640E /* SILUILabels.m */; }; 1E93CDEA23A91FA20022640E /* SILAppSelectionViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E93CDE923A91FA20022640E /* SILAppSelectionViewController.m */; }; 1E967BAF24041F3E00D89B48 /* SILConnectedPeripheralDataModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 1E967BAE24041F3E00D89B48 /* SILConnectedPeripheralDataModel.m */; }; @@ -557,7 +568,7 @@ 4C4F2DDF2407F603005D43BB /* SILBottomCornersCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4F2DDE2407F603005D43BB /* SILBottomCornersCell.swift */; }; 4C4F2DE124080E50005D43BB /* SILRoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4F2DE024080E50005D43BB /* SILRoundedButton.swift */; }; 4C95EB7723FEC8600091FB5A /* SILAppBluetoothBrowserDetails.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4C95EB7623FEC8600091FB5A /* SILAppBluetoothBrowserDetails.storyboard */; }; - 4C97A51123E86525000C6894 /* SILBrowserServiceViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C97A51023E86525000C6894 /* SILBrowserServiceViewCell.swift */; }; + 4C97A51123E86525000C6894 /* SILBrowserDeviceAdTypeViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C97A51023E86525000C6894 /* SILBrowserDeviceAdTypeViewCell.swift */; }; 4C97A51323E87364000C6894 /* SILBrowserDeviceViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C97A51223E87364000C6894 /* SILBrowserDeviceViewCell.swift */; }; 4C97A51B23E88184000C6894 /* SILBrowserButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C97A51A23E88184000C6894 /* SILBrowserButton.swift */; }; 4C97A51D23E89534000C6894 /* SILBrowserDeviceViewCellDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C97A51C23E89534000C6894 /* SILBrowserDeviceViewCellDelegate.swift */; }; @@ -751,8 +762,6 @@ 078138571BE9B014001EFE7E /* SILTextFieldEntryCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SILTextFieldEntryCell.m; path = DebugApp/PopoverViewControllers/CharacteristicEncoding/SILTextFieldEntryCell.m; sourceTree = ""; }; 0797E34B1BF0071D0046EF0E /* SILTextFieldEntryCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = SILTextFieldEntryCell.xib; path = ../Base.lproj/Debug/Popover/SILTextFieldEntryCell.xib; sourceTree = ""; }; 0797E34C1BF0071D0046EF0E /* SILDebugCharacteristicEncodingViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = SILDebugCharacteristicEncodingViewController.xib; path = ../Base.lproj/Debug/Popover/SILDebugCharacteristicEncodingViewController.xib; sourceTree = ""; }; - 07A9563D1BDD40CB0028D333 /* SILAlertBarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SILAlertBarView.h; sourceTree = ""; }; - 07A9563E1BDD40CB0028D333 /* SILAlertBarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SILAlertBarView.m; sourceTree = ""; }; 07B1AD761BFD043000D4D454 /* SILBeaconViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SILBeaconViewModel.h; path = SiliconLabsApp/ViewModels/SILBeaconViewModel.h; sourceTree = SOURCE_ROOT; }; 07B1AD771BFD043000D4D454 /* SILBeaconViewModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SILBeaconViewModel.m; path = SiliconLabsApp/ViewModels/SILBeaconViewModel.m; sourceTree = SOURCE_ROOT; }; 07B1AD791BFD05CA00D4D454 /* SILBGBeaconViewModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SILBGBeaconViewModel.h; path = SiliconLabsApp/ViewModels/SILBGBeaconViewModel.h; sourceTree = SOURCE_ROOT; }; @@ -846,9 +855,15 @@ 1E1907B223FD851C00DD014C /* SILSavedSearchesRealmModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SILSavedSearchesRealmModel.h; sourceTree = ""; }; 1E1907B323FD8B8600DD014C /* SILBeaconTypeRealmModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SILBeaconTypeRealmModel.h; sourceTree = ""; }; 1E1907B423FD8B9900DD014C /* SILBeaconTypeRealmModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SILBeaconTypeRealmModel.m; sourceTree = ""; }; + 1E1A43C0246EA3CB0052DE8D /* SILAdTypeCBPeripheralDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILAdTypeCBPeripheralDecoder.swift; sourceTree = ""; }; + 1E1A43C224728F770052DE8D /* SILAdTypeEddystoneDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILAdTypeEddystoneDecoder.swift; sourceTree = ""; }; + 1E1AE4AD247FCB7F00E5F238 /* SILRealmConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILRealmConfiguration.swift; sourceTree = ""; }; 1E2D26D7244050430006B84A /* SILDocumentPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILDocumentPickerViewController.swift; sourceTree = ""; }; 1E2D9CAA23BA48D600816EC0 /* SILBluetoothBrowserViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SILBluetoothBrowserViewController.h; sourceTree = ""; }; 1E2D9CAB23BA48D600816EC0 /* SILBluetoothBrowserViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SILBluetoothBrowserViewController.m; sourceTree = ""; }; + 1E48A1E82484E27300C188C0 /* SILAnimatedUIButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILAnimatedUIButton.swift; sourceTree = ""; }; + 1E48A1EC2484FBCF00C188C0 /* SILGattPropertiesErrorToastModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILGattPropertiesErrorToastModel.swift; sourceTree = ""; }; + 1E48A1EE2484FC0B00C188C0 /* SILToastModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILToastModelType.swift; sourceTree = ""; }; 1E4C37A12429095300C822E4 /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Bold.ttf"; sourceTree = ""; }; 1E4C37A22429095300C822E4 /* Roboto-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Medium.ttf"; sourceTree = ""; }; 1E4C37A32429095300C822E4 /* Roboto-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Regular.ttf"; sourceTree = ""; }; @@ -875,6 +890,14 @@ 1E6AF80723CDB4DF00EE8280 /* SILBrowserFilterViewControllerDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SILBrowserFilterViewControllerDelegate.h; sourceTree = ""; }; 1E70A97A240403320021C51B /* SILBrowserConnectionsViewModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SILBrowserConnectionsViewModel.m; sourceTree = ""; }; 1E70A97C240403470021C51B /* SILBrowserConnectionsViewModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SILBrowserConnectionsViewModel.h; sourceTree = ""; }; + 1E90F5E32473E73E0013AABD /* SILDebugServicesMenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILDebugServicesMenuViewController.swift; sourceTree = ""; }; + 1E90F5E52473E8340013AABD /* SILDebugServicesMenuTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILDebugServicesMenuTableViewCell.swift; sourceTree = ""; }; + 1E90F5E62473E8340013AABD /* SILDebugServicesMenuTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SILDebugServicesMenuTableViewCell.xib; sourceTree = ""; }; + 1E90F5E92473EA650013AABD /* SILDebugServicesMenuViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILDebugServicesMenuViewControllerDelegate.swift; sourceTree = ""; }; + 1E90F5EB24767C680013AABD /* UIViewController+Toast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Toast.swift"; sourceTree = ""; }; + 1E90F5ED24767CC30013AABD /* SILDisconnectionToastModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SILDisconnectionToastModel.swift; sourceTree = ""; }; + 1E90F5EF2476C3C90013AABD /* SILKeychainInfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILKeychainInfoViewController.swift; sourceTree = ""; }; + 1E90F5F32477B1D00013AABD /* SILKeychainInfoViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILKeychainInfoViewControllerDelegate.swift; sourceTree = ""; }; 1E93CDE223A8EBF90022640E /* SILUILabels.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SILUILabels.h; sourceTree = ""; }; 1E93CDE323A8EBF90022640E /* SILUILabels.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SILUILabels.m; sourceTree = ""; }; 1E93CDE823A91FA20022640E /* SILAppSelectionViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SILAppSelectionViewController.h; sourceTree = ""; }; @@ -998,7 +1021,7 @@ 4C4F2DDE2407F603005D43BB /* SILBottomCornersCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILBottomCornersCell.swift; sourceTree = ""; }; 4C4F2DE024080E50005D43BB /* SILRoundedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILRoundedButton.swift; sourceTree = ""; }; 4C95EB7623FEC8600091FB5A /* SILAppBluetoothBrowserDetails.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SILAppBluetoothBrowserDetails.storyboard; sourceTree = ""; }; - 4C97A51023E86525000C6894 /* SILBrowserServiceViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILBrowserServiceViewCell.swift; sourceTree = ""; }; + 4C97A51023E86525000C6894 /* SILBrowserDeviceAdTypeViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILBrowserDeviceAdTypeViewCell.swift; sourceTree = ""; }; 4C97A51223E87364000C6894 /* SILBrowserDeviceViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILBrowserDeviceViewCell.swift; sourceTree = ""; }; 4C97A51A23E88184000C6894 /* SILBrowserButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILBrowserButton.swift; sourceTree = ""; }; 4C97A51C23E89534000C6894 /* SILBrowserDeviceViewCellDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SILBrowserDeviceViewCellDelegate.swift; sourceTree = ""; }; @@ -1631,11 +1654,22 @@ path = BluetoothBrowser; sourceTree = ""; }; + 1E48A1F02484FCA300C188C0 /* Toast */ = { + isa = PBXGroup; + children = ( + 1E48A1EE2484FC0B00C188C0 /* SILToastModelType.swift */, + 1E90F5ED24767CC30013AABD /* SILDisconnectionToastModel.swift */, + 1E48A1EC2484FBCF00C188C0 /* SILGattPropertiesErrorToastModel.swift */, + ); + name = Toast; + sourceTree = ""; + }; 1E4C37D3242C992300C822E4 /* Utils */ = { isa = PBXGroup; children = ( 1E4C37D6242C997B00C822E4 /* SILBluetoothBrowserExpandableViewManager.h */, 1E4C37D4242C996400C822E4 /* SILBluetoothBrowserExpandableViewManager.m */, + 1E48A1E82484E27300C188C0 /* SILAnimatedUIButton.swift */, ); path = Utils; sourceTree = ""; @@ -1643,6 +1677,7 @@ 1E56E9ED24169C1A00D5B92A /* KeyChains */ = { isa = PBXGroup; children = ( + 1E90F5F52477B42D0013AABD /* Info */, 4C3BA288240D58DF00CF3268 /* SILKeychainViewController.swift */, 4C2CB438240E7C840079040D /* SILMapCell.swift */, 4CEE629C241276EC00D88354 /* SILCell.swift */, @@ -1657,6 +1692,9 @@ 49FB4BE91E7A2EC200223F3E /* SILPopoverViewController.h */, 49FB4BEA1E7A2EC200223F3E /* SILPopoverViewController.m */, 49FB4BEB1E7A2EC200223F3E /* SILPopoverViewController.xib */, + DCB4310F1E8315CE00EE94F0 /* SILOTAHUDView.h */, + DCB431101E8315CE00EE94F0 /* SILOTAHUDView.m */, + DCB431121E8315EF00EE94F0 /* SILOTAHUDView.xib */, 491ADCB91E71EEA300AC2E69 /* SILOTAUICoordinator.h */, 491ADCBA1E71EEA300AC2E69 /* SILOTAUICoordinator.m */, 491ADCBC1E72E58300AC2E69 /* SILOTASetupViewController.h */, @@ -1724,9 +1762,30 @@ path = Filter; sourceTree = ""; }; + 1E90F5E22473E7220013AABD /* Menu */ = { + isa = PBXGroup; + children = ( + 1E90F5E32473E73E0013AABD /* SILDebugServicesMenuViewController.swift */, + 1E90F5E52473E8340013AABD /* SILDebugServicesMenuTableViewCell.swift */, + 1E90F5E62473E8340013AABD /* SILDebugServicesMenuTableViewCell.xib */, + 1E90F5E92473EA650013AABD /* SILDebugServicesMenuViewControllerDelegate.swift */, + ); + path = Menu; + sourceTree = ""; + }; + 1E90F5F52477B42D0013AABD /* Info */ = { + isa = PBXGroup; + children = ( + 1E90F5EF2476C3C90013AABD /* SILKeychainInfoViewController.swift */, + 1E90F5F32477B1D00013AABD /* SILKeychainInfoViewControllerDelegate.swift */, + ); + path = Info; + sourceTree = ""; + }; 1EA0119223E08E7E003B3E62 /* Details */ = { isa = PBXGroup; children = ( + 1E90F5E22473E7220013AABD /* Menu */, 1E56E9EE24169C5B00D5B92A /* OTA */, 07B8A8AB1BCD4D6E001948C1 /* ServicesTable */, 4C9EAB4824042A7B0023FFB1 /* SILServiceCell.swift */, @@ -1845,7 +1904,7 @@ 4C97A51223E87364000C6894 /* SILBrowserDeviceViewCell.swift */, 4C97A51C23E89534000C6894 /* SILBrowserDeviceViewCellDelegate.swift */, 4C97A51A23E88184000C6894 /* SILBrowserButton.swift */, - 4C97A51023E86525000C6894 /* SILBrowserServiceViewCell.swift */, + 4C97A51023E86525000C6894 /* SILBrowserDeviceAdTypeViewCell.swift */, ); path = Views; sourceTree = ""; @@ -2052,6 +2111,7 @@ E621B3491A655AFE00223C5A /* DataModels */ = { isa = PBXGroup; children = ( + 1E48A1F02484FCA300C188C0 /* Toast */, 4C2CB42F240E59EC0079040D /* Mapping */, 4C2C6336240923C70080CE76 /* Browser */, 1E586CDD23FBFEB900E2C385 /* Filter */, @@ -2103,6 +2163,8 @@ 1E967BAE24041F3E00D89B48 /* SILConnectedPeripheralDataModel.m */, E6CCCFE81A73040C0004B2F4 /* SILBeacon.h */, E6CCCFE91A73040C0004B2F4 /* SILBeacon.m */, + 1E1A43C0246EA3CB0052DE8D /* SILAdTypeCBPeripheralDecoder.swift */, + 1E1A43C224728F770052DE8D /* SILAdTypeEddystoneDecoder.swift */, ); name = DataModels; path = Models; @@ -2111,6 +2173,7 @@ E63DD42E1A71449500E0040F /* Categories */ = { isa = PBXGroup; children = ( + 1E90F5EB24767C680013AABD /* UIViewController+Toast.swift */, 4924BA691E7057F800AE9E56 /* CBPeripheral+Services.h */, 4924BA6A1E7057F800AE9E56 /* CBPeripheral+Services.m */, 4924BA6C1E70585100AE9E56 /* CBService+Categories.h */, @@ -2177,6 +2240,7 @@ 1E93CDE323A8EBF90022640E /* SILUILabels.m */, DC380D4F1E8A9FAA00898C12 /* EXTKeyPathCoding.h */, DC380D501E8A9FFD00898C12 /* metamacros.h */, + 1E1AE4AD247FCB7F00E5F238 /* SILRealmConfiguration.swift */, ); path = Helpers; sourceTree = ""; @@ -2196,13 +2260,8 @@ children = ( 496A42F11E7C582F006E87F2 /* SILBigRedButton.h */, 496A42F21E7C582F006E87F2 /* SILBigRedButton.m */, - DCB4310F1E8315CE00EE94F0 /* SILOTAHUDView.h */, - DCB431101E8315CE00EE94F0 /* SILOTAHUDView.m */, - DCB431121E8315EF00EE94F0 /* SILOTAHUDView.xib */, E666CBE91A77C75400676C5C /* SILSegmentedControl.h */, E666CBEA1A77C75400676C5C /* SILSegmentedControl.m */, - 07A9563D1BDD40CB0028D333 /* SILAlertBarView.h */, - 07A9563E1BDD40CB0028D333 /* SILAlertBarView.m */, ); path = Views; sourceTree = ""; @@ -2549,6 +2608,7 @@ 0C2FCBB81F9A542300F4F259 /* SILDebugAdvDetailsViewController.xib in Resources */, 4C95EB7723FEC8600091FB5A /* SILAppBluetoothBrowserDetails.storyboard in Resources */, 0C2FCBB91F9A542300F4F259 /* SILRetailBeaconDetailsViewController.xib in Resources */, + 1E90F5E82473E8340013AABD /* SILDebugServicesMenuTableViewCell.xib in Resources */, 1EEE315823F57CED0076E731 /* SILSavedSearchesTableViewCell.xib in Resources */, 0C2FCBBB1F9A542300F4F259 /* SILDebugCharacteristicPropertyView.xib in Resources */, 0C2FCBBF1F9A542300F4F259 /* SILRetailBeaconAppViewController.xib in Resources */, @@ -2918,6 +2978,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1E90F5F42477B1D00013AABD /* SILKeychainInfoViewControllerDelegate.swift in Sources */, 1EEE315D23F58C210076E731 /* SILBrowserFilterBeaconTypeViewController.m in Sources */, 1E1907B123FD850700DD014C /* SILSavedSearchesRealmModel.m in Sources */, 1E56E9E92416934D00D5B92A /* SILBluetoothBrowser+Constants.m in Sources */, @@ -2927,6 +2988,7 @@ 0C2FCB051F9A542300F4F259 /* SILOTAUICoordinator.m in Sources */, 1E93CDE423A8EBF90022640E /* SILUILabels.m in Sources */, 0C2FCB061F9A542300F4F259 /* SILActivityBarViewController.m in Sources */, + 1E90F5EC24767C680013AABD /* UIViewController+Toast.swift in Sources */, 4D9E26212212BFB200617DBA /* SILRangeTestBoardFeatures.swift in Sources */, 1E2D26D8244050430006B84A /* SILDocumentPickerViewController.swift in Sources */, 1EEE315923F57CED0076E731 /* SILBeaconTypeTableViewCell.m in Sources */, @@ -2934,13 +2996,13 @@ 0C2FCB081F9A542300F4F259 /* SILAppearance.m in Sources */, 0C2FCB091F9A542300F4F259 /* SILDiscoveredPeripheralDisplayData.m in Sources */, 0C2FCB0A1F9A542300F4F259 /* SILBigRedButton.m in Sources */, + 1E90F5F12476C3C90013AABD /* SILKeychainInfoViewController.swift in Sources */, 0C2FCB0B1F9A542300F4F259 /* SILDebugCharacteristicTableViewCell.m in Sources */, 1E586CDB23FBF3FC00E2C385 /* SILBrowserBeaconType.m in Sources */, 0C2FCB0C1F9A542300F4F259 /* SILBluetoothModelManager.m in Sources */, 0C2FCB0D1F9A542300F4F259 /* SILSettings.m in Sources */, 0C2FCB0E1F9A542300F4F259 /* SILWeakTargetWrapper.m in Sources */, 1EA0118E23DB2431003B3E62 /* SILDemoNavigationViewController.m in Sources */, - 0C2FCB0F1F9A542300F4F259 /* SILAlertBarView.m in Sources */, 0C2FCB101F9A542300F4F259 /* SILBeaconRegistry.m in Sources */, 1EBC4E3D2452DFBF005B8767 /* CBAttribute+UUID.swift in Sources */, 4C126076241118A20086951A /* UIColor+SILColors.swift in Sources */, @@ -2956,6 +3018,7 @@ 1EE61D7124000D4900D7E1E5 /* SILLogDataModel.m in Sources */, 0C2FCB181F9A542300F4F259 /* SILBluetoothBitFieldModel.m in Sources */, 0C2FCB191F9A542300F4F259 /* SILDiscoveredPeripheralDisplayDataViewModel.m in Sources */, + 1E90F5EE24767CC30013AABD /* SILDisconnectionToastModel.swift in Sources */, 0C2FCB1A1F9A542300F4F259 /* SILDebugDeviceTableViewCell.m in Sources */, 0C2FCB1B1F9A542300F4F259 /* DebugDeviceFilterViewController.swift in Sources */, 1E56E9E52416800B00D5B92A /* NSString+SILBrowserNotifications.m in Sources */, @@ -2965,6 +3028,8 @@ 1EDCA5B323E1A8A800F78B14 /* SILBrowserLogTableViewCell.m in Sources */, 4C2CB43D240EBC550079040D /* SILMapNameEditorViewController.swift in Sources */, 0C2FCB1F1F9A542300F4F259 /* SILDoubleKeyDictionaryPair.m in Sources */, + 1E48A1ED2484FBCF00C188C0 /* SILGattPropertiesErrorToastModel.swift in Sources */, + 1E1A43C1246EA3CB0052DE8D /* SILAdTypeCBPeripheralDecoder.swift in Sources */, 1E586CE023FBFEF900E2C385 /* SILBrowserSavedSearches.m in Sources */, 4C4F2DDF2407F603005D43BB /* SILBottomCornersCell.swift in Sources */, 0C2FCB211F9A542300F4F259 /* SILBluetoothEnumerationModel.m in Sources */, @@ -3006,7 +3071,9 @@ 0C2FCB3D1F9A542300F4F259 /* SILApp+AttributedProfiles.m in Sources */, 1E70A97B240403320021C51B /* SILBrowserConnectionsViewModel.m in Sources */, 0C2FCB3E1F9A542300F4F259 /* SILOTAFirmwareFile.m in Sources */, + 1E90F5E42473E73E0013AABD /* SILDebugServicesMenuViewController.swift in Sources */, 0C2FCB3F1F9A542300F4F259 /* SILBitRowModel.m in Sources */, + 1E1A43C324728F770052DE8D /* SILAdTypeEddystoneDecoder.swift in Sources */, 0C2FCB401F9A542300F4F259 /* SILRetailBeaconDetailsViewController.m in Sources */, 0C2FCB411F9A542300F4F259 /* SILCollectionViewRightAlignedFlowLayout.m in Sources */, 0C2FCB421F9A542300F4F259 /* UIColor+SILColors.m in Sources */, @@ -3034,8 +3101,10 @@ 0C2FCB501F9A542300F4F259 /* SILRetailBeaconAppViewController.m in Sources */, 0C2FCB511F9A542300F4F259 /* SILBeaconDataViewModel.m in Sources */, 0C2FCB521F9A542300F4F259 /* SILOTAFirmwareUpdate.m in Sources */, + 1E90F5E72473E8340013AABD /* SILDebugServicesMenuTableViewCell.swift in Sources */, 0C2FCB531F9A542300F4F259 /* SILBodySensorLocation.m in Sources */, 0C2FCB541F9A542300F4F259 /* TextFieldTableViewCell.swift in Sources */, + 1E48A1E92484E27300C188C0 /* SILAnimatedUIButton.swift in Sources */, 0C08FD5820CB1CA90016CABC /* SILConnectedLightingViewController.m in Sources */, 0C2FCB551F9A542300F4F259 /* SILCharacteristicFieldBuilder.m in Sources */, 0C2FCB561F9A542300F4F259 /* SILApp.m in Sources */, @@ -3044,7 +3113,7 @@ 4C4F2DE124080E50005D43BB /* SILRoundedButton.swift in Sources */, 0C2FCB571F9A542300F4F259 /* UIFont+Extensions.swift in Sources */, 0C2FCB581F9A542300F4F259 /* SILDebugServicesViewController.m in Sources */, - 4C97A51123E86525000C6894 /* SILBrowserServiceViewCell.swift in Sources */, + 4C97A51123E86525000C6894 /* SILBrowserDeviceAdTypeViewCell.swift in Sources */, 4CEE629D241276EC00D88354 /* SILCell.swift in Sources */, 0C2FCB591F9A542300F4F259 /* SILBeacon.m in Sources */, 0C2FCB5B1F9A542300F4F259 /* SILOTAProgressViewModel.m in Sources */, @@ -3081,6 +3150,8 @@ 0C2FCB761F9A542300F4F259 /* SILEnumerationFieldRowModel.m in Sources */, 0C2FCB771F9A542300F4F259 /* Int+Extensions.swift in Sources */, 0C2FCB781F9A542300F4F259 /* UIImage+SILHelpers.m in Sources */, + 1E1AE4AE247FCB7F00E5F238 /* SILRealmConfiguration.swift in Sources */, + 1E48A1EF2484FC0B00C188C0 /* SILToastModelType.swift in Sources */, 1E2D9CAC23BA48D600816EC0 /* SILBluetoothBrowserViewController.m in Sources */, 0C2FCB791F9A542300F4F259 /* SILOTAHUDView.m in Sources */, 0C2FCB7A1F9A542300F4F259 /* SILServiceTableModel.m in Sources */, @@ -3095,6 +3166,7 @@ 0C2FCB841F9A542300F4F259 /* DebugDeviceViewModel.swift in Sources */, 0C2FCB851F9A542300F4F259 /* SILDebugPopoverViewController.m in Sources */, 0C2FCB861F9A542300F4F259 /* SILRSSIMeasurement.m in Sources */, + 1E90F5EA2473EA650013AABD /* SILDebugServicesMenuViewControllerDelegate.swift in Sources */, 0C2FCB871F9A542300F4F259 /* SILIBeaconViewModel.m in Sources */, 4C2CB435240E725C0079040D /* SILMap.swift in Sources */, 0C2FCB891F9A542300F4F259 /* SILBluetoothBitModel.m in Sources */, @@ -3134,7 +3206,6 @@ 0F4E50FF21525FDC00F58ACE /* SILBluetoothModelManager.m in Sources */, 0F4E510021525FDC00F58ACE /* SILSettings.m in Sources */, 0F4E510121525FDC00F58ACE /* SILWeakTargetWrapper.m in Sources */, - 0F4E510221525FDC00F58ACE /* SILAlertBarView.m in Sources */, 0F4E510321525FDC00F58ACE /* SILBeaconRegistry.m in Sources */, 0F4E510421525FDC00F58ACE /* SILDebugServiceTableViewCell.m in Sources */, 0F4E510521525FDC00F58ACE /* SILDebugAdvDetailsViewController.m in Sources */, @@ -3284,7 +3355,6 @@ 07BBA74E1BD5858F00C2B07E /* SILBluetoothModelManager.m in Sources */, E607A3511A8D4FD100DAAFD3 /* SILSettings.m in Sources */, E64266201A855DA8006C6B2F /* SILWeakTargetWrapper.m in Sources */, - 07A9563F1BDD40CB0028D333 /* SILAlertBarView.m in Sources */, E6CCCFF11A73040C0004B2F4 /* SILBeaconRegistry.m in Sources */, 07B8A8D41BCD6E3B001948C1 /* SILDebugServiceTableViewCell.m in Sources */, 078138491BE99502001EFE7E /* SILDebugAdvDetailsViewController.m in Sources */, @@ -3489,7 +3559,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_RESOURCE_RULES_PATH = ""; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 23; + CURRENT_PROJECT_VERSION = 24; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 52444FG85C; DISPLAY_NAME = "EFR Connect"; @@ -3499,7 +3569,7 @@ INFOPLIST_FILE = "$(SRCROOT)/SiliconLabsApp/SupportingFiles/BlueGecko/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 2.0.2; + MARKETING_VERSION = 2.0.3; PRODUCT_BUNDLE_IDENTIFIER = com.silabs.BlueGeckoDemoApp; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "Blue Gecko Development"; @@ -3523,7 +3593,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_RESOURCE_RULES_PATH = ""; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 23; + CURRENT_PROJECT_VERSION = 24; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = 52444FG85C; DISPLAY_NAME = "EFR Connect"; @@ -3532,7 +3602,7 @@ INFOPLIST_FILE = "$(SRCROOT)/SiliconLabsApp/SupportingFiles/BlueGecko/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - MARKETING_VERSION = 2.0.2; + MARKETING_VERSION = 2.0.3; PRODUCT_BUNDLE_IDENTIFIER = com.silabs.BlueGeckoDemoApp; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = "Blue Gecko Development"; diff --git a/SiliconLabsApp/BluetoothControllers/SILCentralManager.m b/SiliconLabsApp/BluetoothControllers/SILCentralManager.m index 7fbcd490..ddd9c3ab 100644 --- a/SiliconLabsApp/BluetoothControllers/SILCentralManager.m +++ b/SiliconLabsApp/BluetoothControllers/SILCentralManager.m @@ -378,31 +378,30 @@ - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(C [self removeUnfiredConnectionTimeoutTimer]; [self handleConnectionFailureWithError:error]; [self postRegisterLogNotification:[SILLogDataModel prepareLogDescription:@"didFailToConnectPeripheral: " andPeripheral:peripheral andError:error]]; + [self postFailedToConnectPeripheral:peripheral andError:error]; } - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { NSLog(@"didDisconnectPeripheral: %@", peripheral.name); NSLog(@"error: %@", error); - if (self.connectedPeripheral && [self.connectedPeripheral isEqual:peripheral]) { - self.connectedPeripheral = nil; - - NSMutableDictionary *userInfo = nil; - if(self.disconnectingPeripheral) { - self.disconnectingPeripheral = nil; - } else { - userInfo = [NSMutableDictionary dictionary]; - userInfo[SILCentralManagerPeripheralKey] = peripheral; - if (error) { - userInfo[SILCentralManagerErrorKey] = error; - } - } - - [[NSNotificationCenter defaultCenter] postNotificationName:SILCentralManagerDidDisconnectPeripheralNotification - object:self - userInfo:userInfo]; + + BOOL wasConnected = [self.connectionsViewModel isConnectedPeripheral:peripheral]; + NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; + userInfo[SILCentralManagerPeripheralKey] = peripheral; + userInfo[SILNotificationKeyUUID] = peripheral.identifier.UUIDString; + if (error) { + userInfo[SILCentralManagerErrorKey] = error; } + + [[NSNotificationCenter defaultCenter] postNotificationName:SILCentralManagerDidDisconnectPeripheralNotification + object:self + userInfo:userInfo]; [self postRegisterLogNotification:[SILLogDataModel prepareLogDescription:@"didDisconnectPeripheral: " andPeripheral:peripheral andError:error]]; - [self postDeleteDisconnectedPeripheral:peripheral]; + if (wasConnected) { + [self postDeleteDisconnectedPeripheral:peripheral andError:error]; + } else { + [self postFailedToConnectPeripheral:peripheral andError:error]; + } } #pragma mark - Notifications @@ -446,11 +445,29 @@ - (void)removeScanForPeripheralsObserver:(id)observer { } - (void)postRegisterLogNotification:(NSString*)description { - [[NSNotificationCenter defaultCenter] postNotificationName:SILNotificationRegisterLog object:self userInfo:@{ SILNotificationKeyDescription : description}]; + [[NSNotificationCenter defaultCenter] postNotificationName:SILNotificationRegisterLog + object:self + userInfo:@{ + SILNotificationKeyDescription : description + }]; } -- (void)postDeleteDisconnectedPeripheral:(CBPeripheral*)peripheral { - [[NSNotificationCenter defaultCenter] postNotificationName:SILNotificationDeleteDisconnectedPeripheral object:self userInfo:@{ SILNotificationKeyUUID: peripheral.identifier.UUIDString}]; +- (void)postDeleteDisconnectedPeripheral:(CBPeripheral*)peripheral andError:(NSError*)error { + [[NSNotificationCenter defaultCenter] postNotificationName:SILNotificationDeleteDisconnectedPeripheral + object:self + userInfo:@{ + SILNotificationKeyUUID: peripheral.identifier.UUIDString, + SILNotificationKeyError: [NSString stringWithFormat:@"%ld", (long)error.code] + }]; +} + +- (void)postFailedToConnectPeripheral:(CBPeripheral*)peripheral andError:(NSError*)error { + [[NSNotificationCenter defaultCenter] postNotificationName:SILNotificationFailedToConnectPeripheral + object:self + userInfo:@{ + SILNotificationKeyPeripheralName: peripheral.name, + SILNotificationKeyError: [NSString stringWithFormat:@"%ld", (long)error.code] + }]; } #pragma mark - CLLocationManagerDelegate diff --git a/SiliconLabsApp/BluetoothControllers/SILDiscoveredPeripheral.h b/SiliconLabsApp/BluetoothControllers/SILDiscoveredPeripheral.h index e19cc5b7..6e163aa3 100644 --- a/SiliconLabsApp/BluetoothControllers/SILDiscoveredPeripheral.h +++ b/SiliconLabsApp/BluetoothControllers/SILDiscoveredPeripheral.h @@ -28,9 +28,11 @@ @property (strong, nonatomic) CBPeripheral *peripheral; @property (strong, nonatomic) SILRSSIMeasurementTable *RSSIMeasurementTable; @property (strong, nonatomic) NSString *advertisedLocalName; -@property (strong, nonatomic) NSArray *advertisedServiceUUIDs; +@property (strong, nonatomic) NSArray* advertisedServiceUUIDs; @property (strong, nonatomic) NSNumber *txPowerLevel; @property (strong, nonatomic) NSData *manufacturerData; +@property (strong, nonatomic) NSArray* solicitedServiceUUIDs; +@property (strong, nonatomic) NSDictionary* dataServiceData; @property (strong, nonatomic) SILBeacon* beacon; @property (nonatomic) BOOL isFavourite; @property double advertisingInterval; diff --git a/SiliconLabsApp/BluetoothControllers/SILDiscoveredPeripheral.m b/SiliconLabsApp/BluetoothControllers/SILDiscoveredPeripheral.m index 99393ccc..ad2de167 100644 --- a/SiliconLabsApp/BluetoothControllers/SILDiscoveredPeripheral.m +++ b/SiliconLabsApp/BluetoothControllers/SILDiscoveredPeripheral.m @@ -24,7 +24,7 @@ @implementation SILDiscoveredPeripheral NSString* const RSSIAppendingString = @" RSSI"; NSString* const ConnectableDevice = @"Connectable"; NSString* const NonConnectableDevice = @"Non-connectable"; - +NSString* const EddystoneService = @"FEAA"; + (NSString *)connectableDevice { return ConnectableDevice; } + (NSString *)nonConnectableDevice { return NonConnectableDevice; } @@ -51,13 +51,15 @@ - (instancetype)initWithPeripheral:(CBPeripheral *)peripheral - (void)updateWithAdvertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI andDiscoveringTimestamp:(double)timestamp { - self.advertisedLocalName = advertisementData[CBAdvertisementDataLocalNameKey] ?: self.peripheral.name; + self.advertisedLocalName = advertisementData[CBAdvertisementDataLocalNameKey]; self.advertisedServiceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey]; self.txPowerLevel = advertisementData[CBAdvertisementDataTxPowerLevelKey]; if (!self.isConnectable) { self.isConnectable = [advertisementData[CBAdvertisementDataIsConnectable] boolValue]; } self.manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey]; + self.solicitedServiceUUIDs = advertisementData[CBAdvertisementDataSolicitedServiceUUIDsKey]; + self.dataServiceData = advertisementData[CBAdvertisementDataServiceDataKey]; self.beacon = [self parseBeaconData:advertisementData]; if ([self isCorrectAdvertisingPacket:timestamp]) { self.packetReceivedCount++; @@ -112,6 +114,9 @@ - (SILBeacon*)parseBeaconData:(NSDictionary*)adverisement { if (error == nil) { return beacon; } + } else if ([self hasEddystoneService]) { + SILBeacon* eddystoneBeacon = [SILBeacon beaconWithEddystone:self.dataServiceData[[CBUUID UUIDWithString:EddystoneService]]]; + return eddystoneBeacon; } SILBeacon* unknownBeacon = [[SILBeacon alloc] init]; @@ -180,6 +185,10 @@ - (BOOL)isRangeTest { return [self isContainService:SILServiceNumberRangeTest]; } +- (BOOL)hasEddystoneService { + return [self isContainService:EddystoneService]; +} + - (BOOL)isContainService:(NSString *)serviceUUID { CBUUID * const service = [CBUUID UUIDWithString:serviceUUID]; return [self.advertisedServiceUUIDs containsObject:service]; diff --git a/SiliconLabsApp/Categories/CBService+Categories.m b/SiliconLabsApp/Categories/CBService+Categories.m index 5320467d..61c77c1e 100644 --- a/SiliconLabsApp/Categories/CBService+Categories.m +++ b/SiliconLabsApp/Categories/CBService+Categories.m @@ -27,7 +27,7 @@ - (BOOL)hasOTADataCharacteristic { } - (CBCharacteristic *)otaDataCharacteristic { - return [self characteristicForUUID:[SILUUIDProvider sharedProvider].otaCharacteristicDataUUID]; + return [self characteristicForUUID:[SILUUIDProvider sharedProvider].otaCharacteristicOTADataAttributeUUID]; } - (BOOL)hasOTAControlCharacteristic { @@ -35,7 +35,7 @@ - (BOOL)hasOTAControlCharacteristic { } - (CBCharacteristic *)otaControlCharacteristic { - return [self characteristicForUUID:[SILUUIDProvider sharedProvider].otaCharacteristicControlUUID]; + return [self characteristicForUUID:[SILUUIDProvider sharedProvider].otaCharacteristicOTAControlAttributeUUID]; } @end diff --git a/SiliconLabsApp/Categories/NSString+SILBrowserNotifications.h b/SiliconLabsApp/Categories/NSString+SILBrowserNotifications.h index 808ecd9e..f0e59caa 100644 --- a/SiliconLabsApp/Categories/NSString+SILBrowserNotifications.h +++ b/SiliconLabsApp/Categories/NSString+SILBrowserNotifications.h @@ -20,8 +20,13 @@ extern NSString * const SILNotificationReloadLogTableView; extern NSString * const SILNotificationDisconnectPeripheral; extern NSString * const SILNotificationDeleteDisconnectedPeripheral; extern NSString * const SILNotificationDisconnectAllPeripheral; +extern NSString * const SILNotificationFailedToConnectPeripheral; extern NSString * const SILNotificationCellsForVisibleRows; +extern NSString * const SILNotificationDisplayToastRequest; +extern NSString * const SILNotificationDisplayToastResponse; extern NSString * const SILNotificationKeyIndex; extern NSString * const SILNotificationKeyUUID; extern NSString * const SILNotificationKeyDescription; +extern NSString * const SILNotificationKeyError; +extern NSString * const SILNotificationKeyPeripheralName; diff --git a/SiliconLabsApp/Categories/NSString+SILBrowserNotifications.m b/SiliconLabsApp/Categories/NSString+SILBrowserNotifications.m index 04be4dd9..baa84b77 100644 --- a/SiliconLabsApp/Categories/NSString+SILBrowserNotifications.m +++ b/SiliconLabsApp/Categories/NSString+SILBrowserNotifications.m @@ -19,9 +19,14 @@ NSString * const SILNotificationReloadLogTableView = @"ReloadLogTableView"; NSString * const SILNotificationDisconnectPeripheral = @"DisconnectPeripheral"; NSString * const SILNotificationDeleteDisconnectedPeripheral = @"DeleteDisconnectedPeripheral"; +NSString * const SILNotificationFailedToConnectPeripheral = @"FailedToConnectPeripheral"; NSString * const SILNotificationDisconnectAllPeripheral = @"DisconnectAllPeripheral"; NSString * const SILNotificationCellsForVisibleRows = @"CellsForVisibleRows"; +NSString * const SILNotificationDisplayToastRequest = @"DisplayToastRequest"; +NSString * const SILNotificationDisplayToastResponse = @"DisplayToastResponse"; NSString * const SILNotificationKeyIndex = @"index"; NSString * const SILNotificationKeyUUID = @"uuid"; NSString * const SILNotificationKeyDescription = @"description"; +NSString * const SILNotificationKeyError = @"error"; +NSString * const SILNotificationKeyPeripheralName = @"peripheralName"; diff --git a/SiliconLabsApp/Categories/UIImage+SILImages.h b/SiliconLabsApp/Categories/UIImage+SILImages.h index 016c76fd..0ff97a95 100644 --- a/SiliconLabsApp/Categories/UIImage+SILImages.h +++ b/SiliconLabsApp/Categories/UIImage+SILImages.h @@ -76,6 +76,10 @@ extern NSString * const SILImageConnectOff; extern NSString * const SILImageConnectOn; extern NSString * const SILImageSearchOff; extern NSString * const SILImageSearchOn; +extern NSString * const SILImageFilterOff; +extern NSString * const SILImageFilterOffSelected; +extern NSString * const SILImageFilterOn; +extern NSString * const SILImageFilterOnSelected; extern NSString * const SILImageConnectable; extern NSString * const SILImageRSSI; extern NSString * const SILImageBeacon; diff --git a/SiliconLabsApp/Categories/UIImage+SILImages.m b/SiliconLabsApp/Categories/UIImage+SILImages.m index fb9d1143..51c74127 100644 --- a/SiliconLabsApp/Categories/UIImage+SILImages.m +++ b/SiliconLabsApp/Categories/UIImage+SILImages.m @@ -76,12 +76,16 @@ NSString * const SILImageConnectOn = @"connectOn"; NSString * const SILImageSearchOff = @"searchOff"; NSString * const SILImageSearchOn = @"searchOn"; +NSString * const SILImageFilterOff = @"filterOff"; +NSString * const SILImageFilterOffSelected = @"filterOffSelected"; +NSString * const SILImageFilterOn = @"filterOnActive"; +NSString * const SILImageFilterOnSelected = @"filterOnActiveSelected"; NSString * const SILImageConnectable = @"connectable"; NSString * const SILImageRSSI = @"rssi"; -NSString * const SILImageBeacon = @"beacon"; +NSString * const SILImageBeacon = @"beaconDark"; NSString * const SILImageFavouriteOff = @"favouriteOff"; NSString * const SILImageFavouriteOn = @"favouriteOn"; -NSString * const SILImageLoading = @"BTStrong"; +NSString * const SILImageLoading = @"loading pulse"; NSString * const SILImageEmptyView = @"debug_not_found"; NSString * const SILImageExitView = @"exitView"; NSString * const SILImageChevronCollapsed = @"chevron_collapsed"; diff --git a/SiliconLabsApp/Categories/UIViewController+Toast.swift b/SiliconLabsApp/Categories/UIViewController+Toast.swift new file mode 100644 index 00000000..031a1973 --- /dev/null +++ b/SiliconLabsApp/Categories/UIViewController+Toast.swift @@ -0,0 +1,57 @@ +// +// UIViewController+Toast.swift +// BlueGecko +// +// Created by Kamil Czajka on 11/05/2020. +// Copyright © 2020 SiliconLabs. All rights reserved. +// + +import Foundation + +@objc +public enum ToastType: Int { + case disconnectionError + case gattPropertiesError +} + +@objc +extension UIViewController { + @objc + func showToast(message : String, toastType: ToastType, completion: @escaping () -> ()) { + let values = displayParameters(for: toastType) + let AnimationDuration = 0.5 + let AnimationDelay = values.delay + let ToastHeight: CGFloat = values.height + let ToastMargin: CGFloat = 16.0 + let ToastBottomSpacing: CGFloat = values.bottomSpacing + let toastLabel = UILabel(frame: CGRect(x: ToastMargin, y: self.view.frame.size.height - ToastBottomSpacing, width: self.view.frame.size.width - 2 * ToastMargin, height: ToastHeight)) + toastLabel.backgroundColor = values.backgroundColor.withAlphaComponent(0.8) + toastLabel.textColor = UIColor.white + toastLabel.font = UIFont.robotoMedium(size: 14.0) + toastLabel.textAlignment = .center + toastLabel.numberOfLines = 0 + toastLabel.text = message + toastLabel.alpha = 1.0 + toastLabel.layer.cornerRadius = CornerRadiusStandardValue + toastLabel.clipsToBounds = true + self.view.addSubview(toastLabel) + UIView.animate(withDuration: AnimationDuration, delay: AnimationDelay, options: .curveEaseOut, animations: { + toastLabel.alpha = 0.0 + }, completion: {(isCompleted) in + toastLabel.removeFromSuperview() + completion() + }) + } + + @nonobjc + private func displayParameters(for toastType: ToastType) -> (delay: Double, height: CGFloat, bottomSpacing: CGFloat, backgroundColor: UIColor) { + switch toastType { + case .disconnectionError: + return (3.0, 60.0, 130.0, UIColor.sil_siliconLabsRed()) + case .gattPropertiesError: + return (3.0, 60.0, 65.0, UIColor.sil_siliconLabsRed()) + @unknown default: + return (0.0, 0.0, 0.0, .white) + } + } +} diff --git a/SiliconLabsApp/Helpers/SILRealmConfiguration.swift b/SiliconLabsApp/Helpers/SILRealmConfiguration.swift new file mode 100644 index 00000000..9005722a --- /dev/null +++ b/SiliconLabsApp/Helpers/SILRealmConfiguration.swift @@ -0,0 +1,77 @@ +// +// SILRealmConfiguration.swift +// BlueGecko +// +// Created by Kamil Czajka on 28/05/2020. +// Copyright © 2020 SiliconLabs. All rights reserved. +// + +import Foundation +import RealmSwift + +class SILRealmConfiguration : NSObject { + // Initial version of database + static let SchemeVersionEFR_2_0_0: UInt64 = 0 + + // Updated: + // - removed mappings for OTA characteristics, they already have names the same as in document AN1086, page 10 + // - removed mappings for missing services from Generic Access Profile (https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/) + // - removed "search by raw advertising data" from Saved Searches's objects + static let SchemeVersionEFR_2_0_3: UInt64 = 1 + + @objc + static func updateRealmConfigurationIfNeeded() { + let configuration = Realm.Configuration( + schemaVersion: SILRealmConfiguration.SchemeVersionEFR_2_0_3, + migrationBlock: { migration, oldSchemeVersion in + if oldSchemeVersion < SILRealmConfiguration.SchemeVersionEFR_2_0_3 { + SILRealmConfiguration.performUpdateDatabaseForEFR_2_0_3(migration: migration) + } + } + ) + Realm.Configuration.defaultConfiguration = configuration + } + + private static func performUpdateDatabaseForEFR_2_0_3(migration: Migration) { + func migrateServiceMappings() { + let servicesUUIDMappingsToRemove = [ + "1827", + "1826", + "183A", + "1820", + "1828", + "1829" + ] + + migration.enumerateObjects(ofType: "SILServiceMap") { oldObject, _ in + if let oldObject = oldObject, + let uuid = oldObject["uuid"] as? String, + servicesUUIDMappingsToRemove.contains(uuid) { + migration.delete(oldObject) + } + } + } + + func migrateCharacteristicMappings() { + let characteristicsUUIDMappingsToRemove = [ + "F7BF3564-FB6D-4E53-88A4-5E37E0326063", + "984227F3-34FC-4045-A5D0-2C581F81A153", + "4F4A2368-8CCA-451E-BFFF-CF0E2EE23E9F", + "4CC07BCF-0868-4B32-9DAD-BA4CC41E5316", + "25F05C0A-E917-46E9-B2A5-AA2BE1245AFE", + "0D77CC11-4AC1-49F2-BFA9-CD96AC7A92F8" + ] + + migration.enumerateObjects(ofType: "SILCharacteristicMap") { oldObject, _ in + if let oldObject = oldObject, + let uuid = oldObject["uuid"] as? String, + characteristicsUUIDMappingsToRemove.contains(uuid) { + migration.delete(oldObject) + } + } + } + + migrateServiceMappings() + migrateCharacteristicMappings() + } +} diff --git a/SiliconLabsApp/Models/AttributeTableModels/FieldRows/SILBitRowModel.m b/SiliconLabsApp/Models/AttributeTableModels/FieldRows/SILBitRowModel.m index 4c5b0e0e..0eea175f 100644 --- a/SiliconLabsApp/Models/AttributeTableModels/FieldRows/SILBitRowModel.m +++ b/SiliconLabsApp/Models/AttributeTableModels/FieldRows/SILBitRowModel.m @@ -47,6 +47,10 @@ - (NSString *)secondaryTitle { return self.fieldModel.name; } +- (void)clearValues { + self.toggleValue = @""; +} + //@discussion current implementation has this called by SILBitFieldFieldModel, which handles correct length - (NSInteger)consumeValue:(NSData *)value fromIndex:(NSInteger)index { diff --git a/SiliconLabsApp/Models/AttributeTableModels/FieldRows/SILEnumerationFieldRowModel.m b/SiliconLabsApp/Models/AttributeTableModels/FieldRows/SILEnumerationFieldRowModel.m index fdf42855..9509139d 100644 --- a/SiliconLabsApp/Models/AttributeTableModels/FieldRows/SILEnumerationFieldRowModel.m +++ b/SiliconLabsApp/Models/AttributeTableModels/FieldRows/SILEnumerationFieldRowModel.m @@ -49,6 +49,10 @@ - (NSString *)secondaryTitle { return self.fieldModel.name; } +- (void)clearValues { + [self consumeValue:[[NSData alloc] init] fromIndex:0]; +} + - (NSInteger)consumeValue:(NSData *)value fromIndex:(NSInteger)index { if (self.fieldModel.format) { NSData *fieldData = [[SILCharacteristicFieldValueResolver sharedResolver] subsectionOfData:value fromIndex:index forFormat:self.fieldModel.format]; diff --git a/SiliconLabsApp/Models/AttributeTableModels/FieldRows/SILValueFieldRowModel.m b/SiliconLabsApp/Models/AttributeTableModels/FieldRows/SILValueFieldRowModel.m index ab878faa..a08dd283 100644 --- a/SiliconLabsApp/Models/AttributeTableModels/FieldRows/SILValueFieldRowModel.m +++ b/SiliconLabsApp/Models/AttributeTableModels/FieldRows/SILValueFieldRowModel.m @@ -42,6 +42,10 @@ - (NSString *)secondaryTitle { return self.fieldModel.name; } +- (void)clearValues { + self.primaryValue = @""; +} + - (NSInteger)consumeValue:(NSData *)value fromIndex:(NSInteger)index { SILCharacteristicFieldValueResolver * const valueResolver = [SILCharacteristicFieldValueResolver sharedResolver]; NSData * const fieldData = [valueResolver subsectionOfData:value fromIndex:index forFormat:self.fieldModel.format]; diff --git a/SiliconLabsApp/Models/AttributeTableModels/SILCharacteristicTableModel.h b/SiliconLabsApp/Models/AttributeTableModels/SILCharacteristicTableModel.h index ae54bd9d..6118af0c 100644 --- a/SiliconLabsApp/Models/AttributeTableModels/SILCharacteristicTableModel.h +++ b/SiliconLabsApp/Models/AttributeTableModels/SILCharacteristicTableModel.h @@ -33,5 +33,7 @@ - (void)writeIfAllowedToPeripheral:(CBPeripheral *)peripheral error:(NSError * __autoreleasing *)error; - (NSData *)dataToWriteWithError:(NSError * __autoreleasing *)error; - (void)readCharacteristicIfAllowed; +- (BOOL)clearModel; +- (void)expandFieldIfNeeded; @end diff --git a/SiliconLabsApp/Models/AttributeTableModels/SILCharacteristicTableModel.m b/SiliconLabsApp/Models/AttributeTableModels/SILCharacteristicTableModel.m index 2e33da35..c9650295 100644 --- a/SiliconLabsApp/Models/AttributeTableModels/SILCharacteristicTableModel.m +++ b/SiliconLabsApp/Models/AttributeTableModels/SILCharacteristicTableModel.m @@ -14,6 +14,7 @@ #import "SILUUIDProvider.h" #import "SILLogDataModel.h" #import "BlueGecko.pch" +#import "SILEncodingPseudoFieldRowModel.h" #import @@ -84,6 +85,10 @@ - (void)toggleExpansionIfAllowed { self.isExpanded = !self.isExpanded; } +- (void)expandFieldIfNeeded { + self.isExpanded = YES; +} + - (NSString *)hexUuidString { return [self.characteristic getHexUuidValue]; } @@ -205,4 +210,15 @@ - (void)postRegisterLogNotification:(NSString*)description { [[NSNotificationCenter defaultCenter] postNotificationName:@"RegisterLog" object:self userInfo:@{ @"description" : description}]; } +- (BOOL)clearModel { + if (self.isUnknown) { + return NO; + } + for (id fieldRow in self.fieldTableRowModels) { + [fieldRow clearValues]; + } + + return YES; +} + @end diff --git a/SiliconLabsApp/Models/SILAdTypeCBPeripheralDecoder.swift b/SiliconLabsApp/Models/SILAdTypeCBPeripheralDecoder.swift new file mode 100644 index 00000000..3710cf60 --- /dev/null +++ b/SiliconLabsApp/Models/SILAdTypeCBPeripheralDecoder.swift @@ -0,0 +1,315 @@ +// +// SILAdTypeCBPeripheralDecoder.swift +// BlueGecko +// +// Created by Kamil Czajka on 15/05/2020. +// Copyright © 2020 SiliconLabs. All rights reserved. +// + +import Foundation + +class SILAdTypeCBPeripheralDecoder : NSObject { + private let peripheral: SILDiscoveredPeripheral + + @objc + init(peripheral: SILDiscoveredPeripheral) { + self.peripheral = peripheral + } + + @objc + func decode() -> [SILAdvertisementDataModel] { + var advertisementDataModels = [SILAdvertisementDataModel]() + + if peripheral.advertisedServiceUUIDs != nil { + advertisementDataModels += decodeAdvertisedSericeUUIDs() + } + + if peripheral.solicitedServiceUUIDs != nil { + advertisementDataModels += decodeSolicitedServiceUUIDs() + } + + if peripheral.txPowerLevel != nil { + if let txPowerLevelModel = SILAdvertisementDataModel(value: decodeTXPowerLevel(), type: .txPowerLevel) { + advertisementDataModels.append(txPowerLevelModel) + } + } + + if peripheral.advertisedLocalName != nil { + if let advertisementLocalNameModel = SILAdvertisementDataModel(value: decodeAdvertisementLocalName(), type: .completeLocalName) { + advertisementDataModels.append(advertisementLocalNameModel) + } + } + + if peripheral.manufacturerData != nil { + if let manufacturerDataModel = SILAdvertisementDataModel(value: decodeManufacturerData(), type: .manufacturerData) { + advertisementDataModels.append(manufacturerDataModel) + } + } + + if peripheral.dataServiceData != nil { + if let dataServiceDataModel = SILAdvertisementDataModel(value: decodeDataServiceData(), type: .dataServiceData) { + advertisementDataModels.append(dataServiceDataModel) + } + } + + if peripheral.beacon.type == .altBeacon { + if let altBeaconDataModel = SILAdvertisementDataModel(value: decodeAltBeaconData(), type: .altBeacon) { + advertisementDataModels.append(altBeaconDataModel) + } + } + + if peripheral.beacon.type == .eddystone { + let eddystoneDecoder = SILAdTypeEddystoneDecoder(eddystoneData: peripheral.beacon.eddystoneData) + let eddystoneDataModel = eddystoneDecoder.decode() + advertisementDataModels.append(eddystoneDataModel) + } + + return advertisementDataModels + } + + private func decodeAdvertisedSericeUUIDs() -> [SILAdvertisementDataModel] { + var advertisedServicesDataModels = [SILAdvertisementDataModel]() + let splitServicesArray = splitServices(services: peripheral.advertisedServiceUUIDs) + + if !splitServicesArray[0].isEmpty { + if let advertisedServiceUUIDsModel = SILAdvertisementDataModel(value: decodeAdvertisedServiceUUIDs16Bit(services16Bit: splitServicesArray[0]), type: .advertisedServiceUUIDs16Bit) { + advertisedServicesDataModels.append(advertisedServiceUUIDsModel) + } + } + + if !splitServicesArray[1].isEmpty { + if let advertisedServiceUUIDsModel = SILAdvertisementDataModel(value: decodeAdvertisedServiceUUIDs32Bit(services32Bit: splitServicesArray[1]), type: .advertisedServiceUUIDs32Bit) { + advertisedServicesDataModels.append(advertisedServiceUUIDsModel) + } + } + + if !splitServicesArray[2].isEmpty { + if let advertisedServiceUUIDsModel = SILAdvertisementDataModel(value: decodeAdvertisedServiceUUIDs128Bit(services128Bit: splitServicesArray[2]), type: .advertisedServiceUUIDs128Bit) { + advertisedServicesDataModels.append(advertisedServiceUUIDsModel) + } + } + + return advertisedServicesDataModels + } + + + private func decodeAdvertisedServiceUUIDs16Bit(services16Bit: [CBUUID]) -> String { + var advertisedServices = "" + var isFirst = true + for service in services16Bit { + if isFirst { + isFirst = false + } else { + advertisedServices += "\n" + } + + let bluetoothModel = SILBluetoothModelManager.shared()?.serviceModel(forUUIDString: service.uuidString) + + advertisedServices += "0x" + advertisedServices += service.uuidString + advertisedServices += " - " + advertisedServices += bluetoothModel?.name ?? "Unknown Service UUID" + } + + return advertisedServices + } + + private func decodeAdvertisedServiceUUIDs32Bit(services32Bit: [CBUUID]) -> String { + return decodeServicesUsingHex(services32Bit) + } + + private func decodeAdvertisedServiceUUIDs128Bit(services128Bit: [CBUUID]) -> String { + return decode128BitServices(services128Bit) + } + + private func decodeSolicitedServiceUUIDs() -> [SILAdvertisementDataModel] { + var solicitedServiceDataModels = [SILAdvertisementDataModel]() + let splitServicesArray = splitServices(services: peripheral.solicitedServiceUUIDs) + + if !splitServicesArray[0].isEmpty { + if let solicitedServiceUUIDsModel = SILAdvertisementDataModel(value: decodeSolicitedServiceUUIDs16Bit(services16Bit: splitServicesArray[0]), type: .solicitedServiceUUIDs16Bit) { + solicitedServiceDataModels.append(solicitedServiceUUIDsModel) + } + } + + if !splitServicesArray[2].isEmpty { + if let solicitedServiceUUIDsModel = SILAdvertisementDataModel(value: decodeSolicitedServiceUUIDs128Bit(services128Bit: splitServicesArray[2]), type: .solicitedServiceUUIDs128Bit) { + solicitedServiceDataModels.append(solicitedServiceUUIDsModel) + } + } + + return solicitedServiceDataModels + } + + private func decodeSolicitedServiceUUIDs16Bit(services16Bit: [CBUUID]) -> String { + return decodeServicesUsingHex(services16Bit) + } + + private func decodeSolicitedServiceUUIDs128Bit(services128Bit: [CBUUID]) -> String { + return decode128BitServices(services128Bit) + } + + private func decodeTXPowerLevel() -> String { + return self.peripheral.txPowerLevel.stringValue + } + + private func decodeAdvertisementLocalName() -> String { + return peripheral.advertisedLocalName + } + + private func decodeManufacturerData() -> String { + var manufacturerDataString = "" + + let parsedBytes = hexEncodedString(data: self.peripheral.manufacturerData) + + if parsedBytes.count < 4 { + manufacturerDataString += "PARSING ERROR: " + manufacturerDataString += parsedBytes + return manufacturerDataString + } + + let companyCodeIndex = parsedBytes.index(parsedBytes.startIndex, offsetBy: 4) + let companyCodeBytes = parsedBytes.prefix(upTo: companyCodeIndex) + let companyCodeSecondByteIndex = companyCodeBytes.index(companyCodeBytes.startIndex, offsetBy: 2) + let companyCodeSecondByte = companyCodeBytes.prefix(upTo: companyCodeSecondByteIndex) + let companyCodeFirstByte = companyCodeBytes.suffix(from: companyCodeSecondByteIndex) + + manufacturerDataString += "Company Code: 0x" + manufacturerDataString += companyCodeFirstByte + manufacturerDataString += companyCodeSecondByte + + if parsedBytes.count == 4 { + return manufacturerDataString + } + + let data = parsedBytes.suffix(from: companyCodeIndex) + + manufacturerDataString += "\nData: 0x" + manufacturerDataString += data + + return manufacturerDataString + } + + private func decodeDataServiceData() -> String { + var dataServiceData = "" + var isFirst = true + for (_, data) in self.peripheral.dataServiceData.enumerated() { + if isFirst { + isFirst = false + } else { + dataServiceData += "\n" + } + + dataServiceData += "UUID: 0x" + dataServiceData += data.key.uuidString + + let parsedBytes = hexEncodedString(data: data.value) + + if parsedBytes.count > 0 { + dataServiceData += " Data: 0x" + dataServiceData += parsedBytes + } + } + + return dataServiceData + } + + private func decodeAltBeaconData() -> String { + var altBeaconData = "" + let altBeacon = self.peripheral.beacon + var isFirst = true + + if let beaconID = altBeacon?.uuidString { + altBeaconData += "Beacon ID: " + altBeaconData += beaconID + isFirst = false + } + + if let manufacturerID = altBeacon?.manufacturerID { + if isFirst { + isFirst = false + } else { + altBeaconData += "\n" + } + + altBeaconData += "Manufacturer ID: " + altBeaconData += manufacturerID + } + + if let referenceRSSI = altBeacon?.refRSSI { + if isFirst { + isFirst = false + } else { + altBeaconData += "\n" + } + + altBeaconData += "Reference RSSI: " + altBeaconData += String(Int(truncating: referenceRSSI)) + altBeaconData += " dBm" + } + + return altBeaconData + } + + fileprivate func hexEncodedString(data: Data) -> String { + let format = "%02hhX" + return data.map { String(format: format, $0) }.joined() + } + + fileprivate func splitServices(services: [CBUUID]) -> [[CBUUID]] { + var services16bit = [CBUUID]() + var services32bit = [CBUUID]() + var services128bit = [CBUUID]() + + for service in services { + switch service.data.count { + case 2: + services16bit.append(service) + case 4: + services32bit.append(service) + case 16: + services128bit.append(service) + default: + break + } + } + + return [services16bit, services32bit, services128bit] + } + + fileprivate func decodeServicesUsingHex(_ services: [CBUUID]) -> String { + var servicesWithHexString = "" + var isFirst = true + + for service in services { + if isFirst { + isFirst = false + } else { + servicesWithHexString += ", " + } + + servicesWithHexString += "0x" + servicesWithHexString += service.uuidString + } + + return servicesWithHexString + } + + fileprivate func decode128BitServices(_ services: [CBUUID]) -> String { + var services128BitString = "" + var isFirst = true + + for service in services { + if isFirst { + isFirst = false + } else { + services128BitString += ", " + } + + services128BitString += service.uuidString + } + + return services128BitString + } +} + diff --git a/SiliconLabsApp/Models/SILAdTypeEddystoneDecoder.swift b/SiliconLabsApp/Models/SILAdTypeEddystoneDecoder.swift new file mode 100644 index 00000000..3792c1e9 --- /dev/null +++ b/SiliconLabsApp/Models/SILAdTypeEddystoneDecoder.swift @@ -0,0 +1,267 @@ +// +// SILAdTypeEddystoneDecoder.swift +// BlueGecko +// +// Created by Kamil Czajka on 18/05/2020. +// Copyright © 2020 SiliconLabs. All rights reserved. +// + +import Foundation + +class SILAdTypeEddystoneDecoder { + private let eddystoneData: [UInt8] + + private let EddystoneUIDLength = 18 + private let EddystoneURLMinimumLength = 4 + private let EddystoneTLMLength = (encrypted: 18, unencrypted: 14) + private let EddystoneEIDLength = 10 + + init(eddystoneData: Data) { + self.eddystoneData = [UInt8](eddystoneData) + } + + func decode() -> SILAdvertisementDataModel { + let eddystoneDataString: String + + switch self.eddystoneData[0] { + case 0x00: + eddystoneDataString = decodeEddystoneUID() + case 0x10: + eddystoneDataString = decodeEddystoneURL() + case 0x20: + if self.eddystoneData[1] == 0x00 { + eddystoneDataString = decodeEddystoneUnencryptedTLM() + } else if self.eddystoneData[1] == 0x01 { + eddystoneDataString = decodeEddystoneEncryptedTLM() + } else { + eddystoneDataString = "PARSING ERROR: Unknown type of Eddystone-TLM" + } + case 0x30: + eddystoneDataString = decodeEddystoneEID() + default: + eddystoneDataString = "PARSING ERROR: Unknown type of Eddystone: 0x\(self.eddystoneData[0])" + } + + return SILAdvertisementDataModel(value: eddystoneDataString, type: .eddystoneBeacon) + } + + private func decodeEddystoneUID() -> String { + var eddystoneUIDString = "" + + if eddystoneData.count < EddystoneUIDLength { + return "PARSING ERROR: Incomplete data for Eddystone-UID" + } + + eddystoneUIDString += "Calibrated Tx Power at 0 meters: " + eddystoneUIDString += decodeCalibratedTxPowerLevel(self.eddystoneData[1]) + + eddystoneUIDString += "\nUID Namespace: 0x" + let namespaceBytes = eddystoneData[2...11] + eddystoneUIDString += hexEncodedString(data: Array(namespaceBytes)) + + eddystoneUIDString += "\nUID Instance: 0x" + let instanceBytes = eddystoneData[12...17] + eddystoneUIDString += hexEncodedString(data: Array(instanceBytes)) + + return eddystoneUIDString + } + + private func decodeEddystoneURL() -> String { + var eddystoneURLString = "" + + if eddystoneData.count < EddystoneURLMinimumLength { + return "PARSING ERROR: Incomplete data for Eddystone-URL" + } + + eddystoneURLString += "Calibrated Tx Power at 0 meters: " + eddystoneURLString += decodeCalibratedTxPowerLevel(self.eddystoneData[1]) + + eddystoneURLString += "\nURL: " + eddystoneURLString += decodeURLSchemePrefix(self.eddystoneData[2]) + + for eddystoneByte in eddystoneData[3.. String { + var eddystoneUnencryptedTMLString = "" + + if eddystoneData.count < EddystoneTLMLength.unencrypted { + return "PARSING ERROR: Incomplete data for Unencrypted Eddystone-TLM" + } + + eddystoneUnencryptedTMLString += "Version: Unencrypted TLM (0x00)" + + eddystoneUnencryptedTMLString += "\nBattery voltage: " + let batteryVoltage = eddystoneData[2...3] + eddystoneUnencryptedTMLString += decodeBatteryVoltageValue(Array(batteryVoltage)) + eddystoneUnencryptedTMLString += " V" + + eddystoneUnencryptedTMLString += "\nTemperature: " + let beaconTemperature = eddystoneData[4...5] + eddystoneUnencryptedTMLString += decodeBeaconTemperatureValue(Array(beaconTemperature)) + eddystoneUnencryptedTMLString += " °C" + + eddystoneUnencryptedTMLString += "\nAdvertising PDU Count: " + let PDUCount = eddystoneData[6...9] + eddystoneUnencryptedTMLString += decodePDUCountValue(Array(PDUCount)) + + eddystoneUnencryptedTMLString += "\nUptime: " + let uptimeData = eddystoneData[10...13] + let calculatedUpTime = decodeUptime(Array(uptimeData)) + eddystoneUnencryptedTMLString += "\(calculatedUpTime.seconds) seconds (\(calculatedUpTime.days) days)" + + return eddystoneUnencryptedTMLString + } + + private func decodeEddystoneEncryptedTLM() -> String { + var eddystoneEncryptedTMLString = "" + + if eddystoneData.count < EddystoneTLMLength.encrypted { + return "PARSING ERROR: Incomplete data for Encrypted Eddystone-TLM" + } + + eddystoneEncryptedTMLString += "Version: Encrypted TLM (0x01)" + + eddystoneEncryptedTMLString += "\nEncrypted TLM Data: 0x" + let encryptedTMLData = eddystoneData[2...13] + eddystoneEncryptedTMLString += hexEncodedString(data: Array(encryptedTMLData)) + + eddystoneEncryptedTMLString += "\nSalt: 0x" + let saltData = eddystoneData[14...15] + eddystoneEncryptedTMLString += hexEncodedString(data: Array(saltData)) + + eddystoneEncryptedTMLString += "\nMessage Integrity Check: 0x" + let messageIntegrityCheckData = eddystoneData[16...17] + eddystoneEncryptedTMLString += hexEncodedString(data: Array(messageIntegrityCheckData)) + + return eddystoneEncryptedTMLString + } + + private func decodeEddystoneEID() -> String { + var eddystoneEIDString = "" + + if eddystoneData.count < EddystoneEIDLength { + return "PARSING ERROR: Incomplete data for Eddystone-EID" + } + + eddystoneEIDString += "Calibrated Tx Power at 0 meters: " + eddystoneEIDString += decodeCalibratedTxPowerLevel(self.eddystoneData[1]) + + eddystoneEIDString += "\nEphemeral Identifier (EID): 0x" + let ephemeralIDBytes = eddystoneData[2...9] + eddystoneEIDString += hexEncodedString(data: Array(ephemeralIDBytes)) + + return eddystoneEIDString + } + + fileprivate func decodeCalibratedTxPowerLevel(_ txPowerLevelByte: UInt8) -> String { + let DecodingFactorFromU2: UInt8 = 129 + var txPowerLevelString = "" + var txPowerLevel = txPowerLevelByte + if txPowerLevel > 20 { + txPowerLevel = txPowerLevel - DecodingFactorFromU2 + txPowerLevelString += "-" + } + txPowerLevelString += String(txPowerLevel) + txPowerLevelString += " dbm" + return txPowerLevelString + } + + fileprivate func decodeURLSchemePrefix(_ urlSchemePrefixByte: UInt8) -> String { + switch urlSchemePrefixByte { + case 0x00: + return "http://www." + case 0x01: + return "https://www." + case 0x02: + return "http://" + case 0x03: + return "https://" + default: + return "" + } + } + + fileprivate func decodeURLSchemeAppendix(_ urlSchemeAppendixByte: UInt8) -> String { + switch urlSchemeAppendixByte { + case 0x00: + return ".com/" + case 0x01: + return ".org/" + case 0x02: + return ".edu/" + case 0x03: + return ".net/" + case 0x04: + return ".info/" + case 0x05: + return ".biz/" + case 0x06: + return ".gov/" + case 0x07: + return ".com" + case 0x08: + return ".org" + case 0x09: + return ".edu" + case 0x0A: + return ".net" + case 0x0B: + return ".info" + case 0x0C: + return ".biz" + case 0x0D: + return ".gov" + default: + return "" + } + } + + fileprivate func hexEncodedString(data: [UInt8]) -> String { + let format = "%02hhX" + return data.map { String(format: format, $0) }.joined() + } + + fileprivate func decodeBatteryVoltageValue(_ batteryVoltage: [UInt8]) -> String { + let firstByteOfBatteryVoltage = Double(batteryVoltage[0]) + let secondByteOfBatteryVoltage = Double(batteryVoltage[1]) + let batteryVoltage = Double(firstByteOfBatteryVoltage * pow(16, 2) + secondByteOfBatteryVoltage) / pow(10, 3) + return String(batteryVoltage) + } + + fileprivate func decodeBeaconTemperatureValue(_ beaconTemperature: [UInt8]) -> String { + let firstByteOfBeaconTemperature = Double(beaconTemperature[0]) + let secondByteOfBeaconTemperature = Double(beaconTemperature[1]) + let fractorialValue = Double(secondByteOfBeaconTemperature * pow(16, -2)) + return String(firstByteOfBeaconTemperature + fractorialValue) + } + + fileprivate func decodePDUCountValue(_ PDUCount: [UInt8]) -> String { + let firstByteOfPDUCount = Double(PDUCount[0]) * pow(16, 6) + let secondByteOfPDUCount = Double(PDUCount[1]) * pow(16, 4) + let thirdByteOfPDUCount = Double(PDUCount[2]) * pow(16, 2) + let fourthByteOfPDUCount = Double(PDUCount[3]) + let PDUValue = Int(firstByteOfPDUCount + secondByteOfPDUCount + thirdByteOfPDUCount + fourthByteOfPDUCount) + return String(PDUValue) + } + + fileprivate func decodeUptime(_ uptimeData: [UInt8]) -> (seconds: String, days: String) { + let firstByteOfUptimeData = Double(uptimeData[0]) * pow(16, 6) + let secondByteOfUptimeData = Double(uptimeData[1]) * pow(16, 4) + let thirdByteOfUptimeData = Double(uptimeData[2]) * pow(16, 2) + let fourthByteOfUptimeData = Double(uptimeData[3]) + let uptimeDataValue = firstByteOfUptimeData + secondByteOfUptimeData + thirdByteOfUptimeData + fourthByteOfUptimeData + let uptimeDataInSeconds = Int(uptimeDataValue / 10.0) + let OneDayInSeconds = 60.0 * 60.0 * 24.0 + let uptimeDataInDays = Int(uptimeDataValue * ( 1.0 / (OneDayInSeconds * 10.0))) + return (String(uptimeDataInSeconds), String(uptimeDataInDays)) + } +} diff --git a/SiliconLabsApp/Models/SILAdvertisementDataModel.h b/SiliconLabsApp/Models/SILAdvertisementDataModel.h index 8cf102d2..ce0113f8 100644 --- a/SiliconLabsApp/Models/SILAdvertisementDataModel.h +++ b/SiliconLabsApp/Models/SILAdvertisementDataModel.h @@ -9,12 +9,18 @@ #import typedef NS_ENUM(NSInteger, AdModelType) { - AdModelTypeUUID, - AdModelTypeServiceUUID, - AdModelTypeName, - AdModelTypePower, - AdModelTypeMajor, - AdModelTypeMinor + AdModelTypeSolicitedServiceUUIDs16Bit, + AdModelTypeSolicitedServiceUUIDs128Bit, + AdModelTypeAdvertisedServiceUUIDs16Bit, + AdModelTypeAdvertisedServiceUUIDs32Bit, + AdModelTypeAdvertisedServiceUUIDs128Bit, + AdModelTypeCompleteLocalName, + AdModelTypeTXPowerLevel, + AdModelTypeManufacturerData, + AdModelTypeDataServiceData, + AdModelTypeIBeacon, + AdModelTypeAltBeacon, + AdModelTypeEddystoneBeacon }; @interface SILAdvertisementDataModel : NSObject diff --git a/SiliconLabsApp/Models/SILBeacon.h b/SiliconLabsApp/Models/SILBeacon.h index 10702bcf..2424aa07 100644 --- a/SiliconLabsApp/Models/SILBeacon.h +++ b/SiliconLabsApp/Models/SILBeacon.h @@ -17,6 +17,15 @@ typedef NS_ENUM(NSInteger, SILBeaconType) { SILBeaconTypeEddystone }; +typedef NS_ENUM(NSInteger, SILEddystoneBeaconType) { + SILEddystoneBeaconTypeUnspecified, + SILEddystoneBeaconTypeUID, + SILEddystoneBeaconTypeURL, + SILEddystoneBeaconTypeTLMUnencrypted, + SILEddystoneBeaconTypeTLMEncrypted, + SILEddystoneBeaconTypeEID +}; + extern NSString * const SILBeaconUnspecified; extern NSString * const SILBeaconIBeacon; extern NSString * const SILBeaconAltBeacon; @@ -31,15 +40,18 @@ extern NSString * const SILBeaconEddystone; @property (assign, nonatomic) int8_t calibrationPower; @property (strong, nonatomic) NSNumber *txPower; @property (strong, nonatomic) CLBeacon *beacon; //used by iBeacons +@property (strong, nonatomic) NSString *manufacturerID; // used by AltBeacons @property (strong, nonatomic) NSNumber *refRSSI; //used by AltBeacons @property (strong, nonatomic) NSString *beaconNamespace; //used by Eddystone @property (strong, nonatomic) NSString *instance; //used by Eddystone @property (strong, nonatomic) NSURL *url; //used by Eddystone @property (strong, nonatomic) TLMData *tlmData; //used by Eddystone @property (nonatomic) SILBeaconType type; +@property (nonatomic) SILEddystoneBeaconType eddystoneBeaconType; // used by Eddystone +@property (nonatomic) NSData* eddystoneData; // used by Eddystone + (instancetype)beaconWithAdvertisment:(NSDictionary *)advertisement name:(NSString *)name error:(NSError **)error; + (instancetype)beaconWithIBeacon:(CLBeacon *)beacon; -+ (instancetype)beaconWithEddystone:(EddystoneBeacon *)eddystone; ++ (instancetype)beaconWithEddystone:(NSData*)eddystoneServiceData; @end diff --git a/SiliconLabsApp/Models/SILBeacon.m b/SiliconLabsApp/Models/SILBeacon.m index f3a12c3c..d85ae7a9 100644 --- a/SiliconLabsApp/Models/SILBeacon.m +++ b/SiliconLabsApp/Models/SILBeacon.m @@ -14,11 +14,11 @@ uint16_t const kBlueGeckoAltBeaconCode = 0xBEAC; uint16_t const kSilabsMfgId = 0x0047; -uint16_t const kEddystoneBeaconCode = 0xAAFE; +uint16_t const kEddystoneBeaconCode = 0xFEAA; NSString * const SILBeaconUnspecified = @"Unspecified"; NSString * const SILBeaconIBeacon = @"iBeacon"; -NSString * const SILBeaconAltBeacon = @"Alt Beacon"; +NSString * const SILBeaconAltBeacon = @"AltBeacon"; NSString * const SILBeaconEddystone = @"Eddystone"; @implementation SILBeacon @@ -41,9 +41,6 @@ + (instancetype)beaconWithAdvertisment:(NSDictionary *)advertisement name:(NSStr if (beaconCode == kBlueGeckoAltBeaconCode && mfgId == kSilabsMfgId) { beacon = [SILBeacon altBeaconWithManufacturingData:manufacturerData txPower:txPower error:error]; beacon.name = SILBeaconAltBeacon; - } else if (beaconCode == kEddystoneBeaconCode) { - beacon = [SILBeacon eddystoneBeacon]; - beacon.name = SILBeaconEddystone; } else { beacon = [SILBeacon bgBeaconWithManufacturingData:manufacturerData error:error]; beacon.name = SILBeaconUnspecified; @@ -52,14 +49,6 @@ + (instancetype)beaconWithAdvertisment:(NSDictionary *)advertisement name:(NSStr return beacon; } -+ (instancetype)eddystoneBeacon { - SILBeacon* beacon = [[SILBeacon alloc] init]; - beacon.type = SILBeaconTypeEddystone; - beacon.name = SILBeaconEddystone; - - return beacon; -} - + (instancetype)beaconWithIBeacon:(CLBeacon *)iBeacon { if (!iBeacon) { return nil; @@ -77,24 +66,40 @@ + (instancetype)beaconWithIBeacon:(CLBeacon *)iBeacon { return beacon; } -+ (instancetype)beaconWithEddystone:(EddystoneBeacon *)eddystone { - if (!eddystone) { - return nil; - } - - SILBeacon *beacon = [[SILBeacon alloc] init]; - - beacon.UUIDString = [eddystone.namespace stringByAppendingString:eddystone.instance]; - beacon.beaconNamespace = eddystone.namespace; - beacon.instance = eddystone.instance; - beacon.type = SILBeaconTypeEddystone; - beacon.url = eddystone.url; - beacon.tlmData = eddystone.tlmData; - beacon.calibrationPower = eddystone.rssi; - beacon.txPower = @(eddystone.txPower); - // TODO: Replace with actual name. ++ (instancetype)beaconWithEddystone:(NSData *)eddystoneServiceData { + SILBeacon* beacon = [[SILBeacon alloc] init]; + beacon.name = @"Eddystone"; - + beacon.type = SILBeaconTypeEddystone; + + uint8_t *dataPointer = (uint8_t *)eddystoneServiceData.bytes; + + switch (dataPointer[0]) { + case 0x00: + beacon.eddystoneBeaconType = SILEddystoneBeaconTypeUID; + break; + case 0x10: + beacon.eddystoneBeaconType = SILEddystoneBeaconTypeURL; + break; + case 0x20: + if (dataPointer[1] == 0x00) { + beacon.eddystoneBeaconType = SILEddystoneBeaconTypeTLMUnencrypted; + } else if (dataPointer[1] == 0x01) { + beacon.eddystoneBeaconType = SILEddystoneBeaconTypeTLMEncrypted; + } else { + beacon.eddystoneBeaconType = SILEddystoneBeaconTypeUnspecified; + } + break; + case 0x30: + beacon.eddystoneBeaconType = SILEddystoneBeaconTypeEID; + break; + default: + beacon.eddystoneBeaconType = SILEddystoneBeaconTypeUnspecified; + break; + } + + beacon.eddystoneData = eddystoneServiceData; + return beacon; } @@ -165,11 +170,16 @@ + (SILBeacon *)altBeaconWithManufacturingData:(NSData *)data txPower:(NSNumber * } uint8_t *dataPointer = (uint8_t *)data.bytes; - dataPointer += 4; //skip mfg and beacon code + NSMutableString *manufactuterIDString = [[NSMutableString alloc] initWithString:@"0x"]; + [manufactuterIDString appendFormat:@"%02hhX", dataPointer[1]]; + [manufactuterIDString appendFormat:@"%02hhX", dataPointer[0]]; + beacon.manufacturerID = [manufactuterIDString copy]; + + dataPointer += 4; //skip beacon code NSMutableString *mutableUUIDString = [[NSMutableString alloc] initWithString:@"0x"]; for (int i = 0; i < 20; i++) { - [mutableUUIDString appendFormat:@"%02x", dataPointer[0]]; + [mutableUUIDString appendFormat:@"%02hhX", dataPointer[0]]; dataPointer++; } beacon.UUIDString = [mutableUUIDString copy]; diff --git a/SiliconLabsApp/Models/SILBitFieldFieldModel.m b/SiliconLabsApp/Models/SILBitFieldFieldModel.m index cf615fc7..adbf38f1 100644 --- a/SiliconLabsApp/Models/SILBitFieldFieldModel.m +++ b/SiliconLabsApp/Models/SILBitFieldFieldModel.m @@ -69,6 +69,10 @@ - (NSString *)secondaryTitle { return @"Bit field Secondary"; } +- (void)clearValues { + [self consumeValue:[[NSData alloc] init] fromIndex:0]; +} + //Consume a single bit value - (NSInteger)consumeValue:(NSData *)value fromIndex:(NSInteger)index { diff --git a/SiliconLabsApp/Models/SILBrowserConnectionsViewModel.h b/SiliconLabsApp/Models/SILBrowserConnectionsViewModel.h index a4ec19ed..427fe643 100644 --- a/SiliconLabsApp/Models/SILBrowserConnectionsViewModel.h +++ b/SiliconLabsApp/Models/SILBrowserConnectionsViewModel.h @@ -22,6 +22,8 @@ - (void)disconnectAllPeripheral; - (void)updateConnectionsView:(NSInteger)index; - (void)connectionsViewOnDetailsScreen:(BOOL)isDetailsScreen; +- (BOOL)isConnectedPeripheral:(CBPeripheral*)peripheral; +- (void)clearViewModelData; @end diff --git a/SiliconLabsApp/Models/SILBrowserConnectionsViewModel.m b/SiliconLabsApp/Models/SILBrowserConnectionsViewModel.m index 269c21c7..192d8a70 100644 --- a/SiliconLabsApp/Models/SILBrowserConnectionsViewModel.m +++ b/SiliconLabsApp/Models/SILBrowserConnectionsViewModel.m @@ -15,6 +15,8 @@ @interface SILBrowserConnectionsViewModel () @property (strong, nonatomic, readwrite) NSMutableArray* allPeripherals; +@property (strong, nonatomic, readwrite) NSMutableArray* toastsToDisplayList; +@property (strong, nonatomic) NSTimer* toastsToDisplayChecker; @end @@ -36,9 +38,13 @@ - (instancetype)init { if (self) { self.allPeripherals = [[NSMutableArray alloc] init]; self.peripherals = [_allPeripherals copy]; + self.toastsToDisplayList = [[NSMutableArray alloc] init]; + self.toastsToDisplayChecker = [self setupToastToDisplayChecker]; [self addObserverForDisconnectPeripheral]; [self addObserverForDeleteDisconnectedPeripheral]; [self addObserverForDisconnectAllPeripheral]; + [self addObserverForDisplayToastRequest]; + [self addObserverForFailedToConnectPeripheral]; } return self; } @@ -55,6 +61,23 @@ - (void)addObserverForDisconnectAllPeripheral { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(disconnectAllPeripheral) name:SILNotificationDisconnectAllPeripheral object:nil]; } +- (void)addObserverForDisplayToastRequest { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(displayToastIfNeeded) name:SILNotificationDisplayToastRequest object:nil]; +} + +- (void)addObserverForFailedToConnectPeripheral { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(addFailedConnectionToastToDisplay:) name:SILNotificationFailedToConnectPeripheral object:nil]; +} + +- (NSTimer*)setupToastToDisplayChecker { + return [NSTimer scheduledTimerWithTimeInterval:1.0f repeats:YES block:^(NSTimer * _Nonnull timer) { + if (self.toastsToDisplayList.count > 0) { + [self displayToastIfNeeded]; + [self.toastsToDisplayChecker invalidate]; + } + }]; +} + - (void)addNewConnectedPeripheral:(CBPeripheral*)peripheral { if ([self isUniquePeripheral:peripheral]) { SILConnectedPeripheralDataModel* connectedPeripheral = [[SILConnectedPeripheralDataModel alloc] initWithPeripheral:peripheral andIsSelected:NO]; @@ -69,10 +92,6 @@ - (void)disconnectAllPeripheral { for (SILConnectedPeripheralDataModel* connectedPeripheral in _allPeripherals) { [_centralManager disconnectFromPeripheral:connectedPeripheral.peripheral]; } - - _allPeripherals = [[NSMutableArray alloc] init]; - _peripherals = [_allPeripherals copy]; - [self postReloadConnectionTableViewNotification]; } - (void)disconnectPeripheral:(NSNotification*)notification { @@ -82,9 +101,6 @@ - (void)disconnectPeripheral:(NSNotification*)notification { SILConnectedPeripheralDataModel* connectedPeripheral = _peripherals[index]; [_centralManager disconnectFromPeripheral:connectedPeripheral.peripheral]; - [_allPeripherals removeObjectAtIndex:index]; - _peripherals = [_allPeripherals copy]; - [self postReloadConnectionTableViewNotification]; } - (void)postReloadConnectionTableViewNotification { @@ -107,11 +123,16 @@ - (void)updateConnectionsView:(NSInteger)index { - (void)deleteDisconnectedPeripheral:(NSNotification*)notification { NSDictionary* userInfo = notification.userInfo; NSString* uuid = (NSString*)userInfo[SILNotificationKeyUUID]; + NSString* errorCodeString = (NSString*)userInfo[SILNotificationKeyError]; + int errorCode = [errorCodeString intValue]; for (SILConnectedPeripheralDataModel* connectedPeripheral in _peripherals) { if ([connectedPeripheral.peripheral.identifier.UUIDString isEqualToString:uuid]) { - [_allPeripherals removeObject:connectedPeripheral]; - _peripherals = [_allPeripherals copy]; + [self.allPeripherals removeObject:connectedPeripheral]; + self.peripherals = [self.allPeripherals copy]; + if (errorCode != 0) { + [self.toastsToDisplayList addObject:[[SILDisconnectionToastModel alloc] initWithPeripheralName:connectedPeripheral.peripheral.name errorCode:errorCode peripheralWasConnected:YES]]; + } } } @@ -134,4 +155,36 @@ - (void)connectionsViewOnDetailsScreen:(BOOL)isDetailsScreen { } } +- (void)clearViewModelData { + self.allPeripherals = [[NSMutableArray alloc] init]; + self.peripherals = [[NSArray alloc] init]; +} + +- (void)displayToastIfNeeded { + if (self.toastsToDisplayList.count > 0) { + SILDisconnectionToastModel* toastToShow = [self.toastsToDisplayList objectAtIndex:0]; + NSString* ErrorMessage = [toastToShow getErrorMessageForToast]; + [self.toastsToDisplayList removeObjectAtIndex:0]; + [[NSNotificationCenter defaultCenter] postNotificationName:SILNotificationDisplayToastResponse + object:self + userInfo: @{ + SILNotificationKeyDescription : ErrorMessage + }]; + } else { + self.toastsToDisplayChecker = [self setupToastToDisplayChecker]; + } +} + +- (BOOL)isConnectedPeripheral:(CBPeripheral*)peripheral { + return ![self isUniquePeripheral:peripheral]; +} + +- (void)addFailedConnectionToastToDisplay:(NSNotification*)notification { + NSDictionary* userInfo = notification.userInfo; + NSString* peripheralName = (NSString*)userInfo[SILNotificationKeyPeripheralName]; + NSString* errorCodeString = (NSString*)userInfo[SILNotificationKeyError]; + int errorCode = [errorCodeString intValue]; + [self.toastsToDisplayList addObject:[[SILDisconnectionToastModel alloc] initWithPeripheralName:peripheralName errorCode:errorCode peripheralWasConnected:NO]]; +} + @end diff --git a/SiliconLabsApp/Models/SILBrowserSavedSearches.h b/SiliconLabsApp/Models/SILBrowserSavedSearches.h index ee4f6234..8a834c62 100644 --- a/SiliconLabsApp/Models/SILBrowserSavedSearches.h +++ b/SiliconLabsApp/Models/SILBrowserSavedSearches.h @@ -14,14 +14,13 @@ @interface SILBrowserSavedSearches : NSObject @property (strong, nonatomic, readonly) NSString *searchByDeviceNameText; -@property (strong, nonatomic, readonly) NSString *searchByRawAdvetisingDataText; @property (nonatomic, readonly) NSInteger dBmValue; @property (strong, nonatomic, readonly) NSArray* beaconTypes; @property (nonatomic, readonly) BOOL isFavourite; @property (nonatomic, readonly) BOOL isConnectable; @property (nonatomic, readonly) BOOL isSelected; -- (instancetype)initWithSearchByDeviceNameText:(NSString*)searchByDeviceNameText searchByRawAdveritisingDataText:(NSString*)searchByRawAdvetisingDataText dBmValue:(NSInteger)dBmValue beaconTypes:(NSArray*)beaconTypes isFavourite:(BOOL)isFavourite isConnectable:(BOOL)isConnectable andIsSelected:(BOOL)isSelected; +- (instancetype)initWithSearchByDeviceNameText:(NSString*)searchByDeviceNameText dBmValue:(NSInteger)dBmValue beaconTypes:(NSArray*)beaconTypes isFavourite:(BOOL)isFavourite isConnectable:(BOOL)isConnectable andIsSelected:(BOOL)isSelected; - (void)setSelection:(BOOL)isSelected; @end diff --git a/SiliconLabsApp/Models/SILBrowserSavedSearches.m b/SiliconLabsApp/Models/SILBrowserSavedSearches.m index ab5d7540..09a6a603 100644 --- a/SiliconLabsApp/Models/SILBrowserSavedSearches.m +++ b/SiliconLabsApp/Models/SILBrowserSavedSearches.m @@ -12,7 +12,6 @@ @interface SILBrowserSavedSearches () @property (strong, nonatomic, readwrite) NSString *searchByDeviceNameText; -@property (strong, nonatomic, readwrite) NSString *searchByRawAdvetisingDataText; @property (nonatomic, readwrite) NSInteger dBmValue; @property (strong, nonatomic, readwrite) NSArray* beaconTypes; @property (nonatomic, readwrite) BOOL isFavourite; @@ -23,11 +22,10 @@ @interface SILBrowserSavedSearches () @implementation SILBrowserSavedSearches : NSObject -- (instancetype)initWithSearchByDeviceNameText:(NSString*)searchByDeviceNameText searchByRawAdveritisingDataText:(NSString*)searchByRawAdvetisingDataText dBmValue:(NSInteger)dBmValue beaconTypes:(NSArray*)beaconTypes isFavourite:(BOOL)isFavourite isConnectable:(BOOL)isConnectable andIsSelected:(BOOL)isSelected { +- (instancetype)initWithSearchByDeviceNameText:(NSString*)searchByDeviceNameText dBmValue:(NSInteger)dBmValue beaconTypes:(NSArray*)beaconTypes isFavourite:(BOOL)isFavourite isConnectable:(BOOL)isConnectable andIsSelected:(BOOL)isSelected { self = [super init]; if (self) { self.searchByDeviceNameText = searchByDeviceNameText; - self.searchByRawAdvetisingDataText = searchByRawAdvetisingDataText; self.dBmValue = dBmValue; self.beaconTypes = beaconTypes; self.isFavourite = isFavourite; diff --git a/SiliconLabsApp/Models/SILDisconnectionToastModel.swift b/SiliconLabsApp/Models/SILDisconnectionToastModel.swift new file mode 100644 index 00000000..fc3daf9e --- /dev/null +++ b/SiliconLabsApp/Models/SILDisconnectionToastModel.swift @@ -0,0 +1,70 @@ +// +// SILDisconnectionToastModel.swift +// BlueGecko +// +// Created by Kamil Czajka on 11/05/2020. +// Copyright © 2020 SiliconLabs. All rights reserved. +// + +import CoreBluetooth +import Foundation + +@objc +class SILDisconnectionToastModel : NSObject, SILToastModelType { + let peripheralName: String + let errorCode: Int + let peripheralWasConnected: Bool + var errorDescription: String { + guard let CBErrorCode = CBError.Code(rawValue: errorCode) else { + return "Unspecified error" + } + switch CBErrorCode { + case .unknown: + return "0x0085 GATT Error" + case .invalidParameters: + return "0x0002 The specified parameters are invalid" + case .invalidHandle: + return "0x0002 The specified attribute handle is invalid" + case .notConnected: + return "0x0085 The device isn’t currently connected" + case .outOfSpace: + return "0x0007 The device has run out of space to complete the intended operation" + case .operationCancelled: + return "0x0100 The connection canceled" + case .connectionTimeout: + return "0x0008 The connection timed out" + case .peripheralDisconnected: + return "0x0013 The peripheral disconnected" + case .uuidNotAllowed: + return "0x0085 The specified UUID isn’t permitted" + case .alreadyAdvertising: + return "0x0085 The peripheral is already advertising" + case .connectionFailed: + return "0x003E The connection failed" + case .connectionLimitReached: + return "0x0009 The device already has the maximum number of connections" + case .unkownDevice: + return "0x0085 The device is unknown" + case .operationNotSupported: + return "0x0085 The operation isn’t supported" + @unknown default: + return "Unspecified error" + } + } + + @objc + init(peripheralName: String, errorCode: Int, peripheralWasConnected: Bool) { + self.peripheralName = peripheralName + self.errorCode = errorCode + self.peripheralWasConnected = peripheralWasConnected + } + + @objc + func getErrorMessageForToast() -> String { + if peripheralWasConnected { + return "Device \(peripheralName) has disconnected\nReason: \(errorDescription)" + } else { + return "Failed connecting to: \(peripheralName)\nReason: \(errorDescription)" + } + } +} diff --git a/SiliconLabsApp/Models/SILDiscoveredPeripheralDisplayData.h b/SiliconLabsApp/Models/SILDiscoveredPeripheralDisplayData.h index 35c843d7..aba44eff 100644 --- a/SiliconLabsApp/Models/SILDiscoveredPeripheralDisplayData.h +++ b/SiliconLabsApp/Models/SILDiscoveredPeripheralDisplayData.h @@ -15,7 +15,6 @@ @property (strong, nonatomic) SILDiscoveredPeripheral *discoveredPeripheral; @property (strong, nonatomic, readonly) NSArray *advertisementDataModels; -@property (strong, nonatomic, readonly) NSArray *advertisementDataModelsForDevicesTable; @property (strong, nonatomic, readonly) NSArray *advertisementDataModelsForInfoView; - (instancetype)initWithDiscoveredPeripheral:(SILDiscoveredPeripheral *)discoveredPeripheral; diff --git a/SiliconLabsApp/Models/SILDiscoveredPeripheralDisplayData.m b/SiliconLabsApp/Models/SILDiscoveredPeripheralDisplayData.m index 5a0cf04a..5209dcb5 100644 --- a/SiliconLabsApp/Models/SILDiscoveredPeripheralDisplayData.m +++ b/SiliconLabsApp/Models/SILDiscoveredPeripheralDisplayData.m @@ -15,7 +15,6 @@ @interface SILDiscoveredPeripheralDisplayData () @property (strong, nonatomic, readwrite) NSArray *advertisementDataModels; -@property (strong, nonatomic, readwrite) NSArray *advertisementDataModelsForDevicesTable; @property (strong, nonatomic, readwrite) NSArray *advertisementDataModelsForInfoView; @end @@ -35,13 +34,6 @@ - (instancetype)initWithDiscoveredPeripheral:(SILDiscoveredPeripheral *)discover #pragma mark - Properties -- (NSArray *)advertisementDataModelsForDevicesTable { - if (_advertisementDataModelsForDevicesTable == nil) { - _advertisementDataModelsForDevicesTable = [self advertisementDataModelsExcept:@[@(AdModelTypeUUID), @(AdModelTypeName), @(AdModelTypeServiceUUID)]]; - } - return _advertisementDataModelsForDevicesTable; -} - - (NSArray *)advertisementDataModelsForInfoView { if (_advertisementDataModelsForInfoView == nil) { _advertisementDataModelsForInfoView = [self.advertisementDataModels copy]; @@ -61,58 +53,54 @@ - (NSArray *)advertisementDataModelsForPeripheral:(SILDiscoveredPeripheral *)dev } - (NSArray*)adverisementDataModelsForCBPeripheral:(SILDiscoveredPeripheral*)device { - NSMutableArray *mutableAdvModels = [[NSMutableArray alloc] init]; - - SILAdvertisementDataModel *uuidModel = [[SILAdvertisementDataModel alloc] initWithValue:device.peripheral.identifier.UUIDString type:AdModelTypeUUID]; - [mutableAdvModels addObject:uuidModel]; - - for (CBUUID *serviceUUID in device.advertisedServiceUUIDs) { - SILAdvertisementDataModel *uuidModel = [[SILAdvertisementDataModel alloc] initWithValue:serviceUUID.UUIDString type:AdModelTypeServiceUUID]; - [mutableAdvModels addObject:uuidModel]; - } - - if (device.advertisedLocalName) { - SILAdvertisementDataModel *nameModel = [[SILAdvertisementDataModel alloc] initWithValue:device.advertisedLocalName - type:AdModelTypeName]; - [mutableAdvModels addObject:nameModel]; - } - - if (device.txPowerLevel) { - SILAdvertisementDataModel *powerModel = [[SILAdvertisementDataModel alloc] initWithValue:[device.txPowerLevel stringValue] - type:AdModelTypePower]; - [mutableAdvModels addObject:powerModel]; - } - - return mutableAdvModels; + SILAdTypeCBPeripheralDecoder* decoder = [[SILAdTypeCBPeripheralDecoder alloc] initWithPeripheral:device]; + return [decoder decode]; } - (NSArray*)advertisementDataModelsForCLBeacon:(SILDiscoveredPeripheral*)device { NSMutableArray *mutableAdvModels = [[NSMutableArray alloc] init]; + NSMutableString* iBeaconDataString = [[NSMutableString alloc] init]; SILBeacon* beacon = device.beacon; - - SILAdvertisementDataModel *uuidModel = [[SILAdvertisementDataModel alloc] initWithValue:beacon.UUIDString type:AdModelTypeUUID]; - [mutableAdvModels addObject:uuidModel]; - + BOOL isFirst = YES; + if (beacon.minor) { + [iBeaconDataString appendString:@"Minor: "]; + NSString* minorStringValue = [NSString stringWithFormat:@"%hu", beacon.minor]; + [iBeaconDataString appendString:minorStringValue]; + isFirst = NO; + } if (beacon.major) { + if (isFirst == YES) { + isFirst = NO; + } else { + [iBeaconDataString appendString:@"\n"]; + } + [iBeaconDataString appendString:@"Major: "]; NSString* majorStringValue = [NSString stringWithFormat:@"%hu", beacon.major]; - SILAdvertisementDataModel *majorModel = [[SILAdvertisementDataModel alloc] initWithValue:majorStringValue - type:AdModelTypeMajor]; - [mutableAdvModels addObject:majorModel]; + [iBeaconDataString appendString:majorStringValue]; } - - if (beacon.minor) { - NSString* minorStringValue = [NSString stringWithFormat:@"%hu", beacon.minor]; - SILAdvertisementDataModel *minorModel = [[SILAdvertisementDataModel alloc] initWithValue:minorStringValue - type:AdModelTypeMinor]; - [mutableAdvModels addObject:minorModel]; + if (beacon.UUIDString) { + if (isFirst == YES) { + isFirst = NO; + } else { + [iBeaconDataString appendString:@"\n"]; + } + [iBeaconDataString appendString:@"UUID: "]; + [iBeaconDataString appendString:beacon.UUIDString]; } - if (beacon.name) { - SILAdvertisementDataModel *nameModel = [[SILAdvertisementDataModel alloc] initWithValue:device.advertisedLocalName - type:AdModelTypeName]; - [mutableAdvModels addObject:nameModel]; + if (beacon.beacon.proximity && beacon.beacon.accuracy) { + if (isFirst == YES) { + isFirst = NO; + } else { + [iBeaconDataString appendString:@"\n"]; + } + [iBeaconDataString appendFormat:@"Distance from iBeacon: "]; + [iBeaconDataString appendFormat:@"%ld +/- %.2f meters", (long)beacon.beacon.proximity, beacon.beacon.accuracy]; } - + + SILAdvertisementDataModel* iBeaconModel = [[SILAdvertisementDataModel alloc] initWithValue:iBeaconDataString type:AdModelTypeIBeacon]; + [mutableAdvModels addObject:iBeaconModel]; + return mutableAdvModels; } diff --git a/SiliconLabsApp/Models/SILEncodingPseudoFieldRowModel.m b/SiliconLabsApp/Models/SILEncodingPseudoFieldRowModel.m index e628e6a8..c1fd5b26 100644 --- a/SiliconLabsApp/Models/SILEncodingPseudoFieldRowModel.m +++ b/SiliconLabsApp/Models/SILEncodingPseudoFieldRowModel.m @@ -39,4 +39,8 @@ - (NSString *)secondaryTitle { return @""; } +- (void)clearValues { + +} + @end diff --git a/SiliconLabsApp/Models/SILGattPropertiesErrorToastModel.swift b/SiliconLabsApp/Models/SILGattPropertiesErrorToastModel.swift new file mode 100644 index 00000000..c6c5d85a --- /dev/null +++ b/SiliconLabsApp/Models/SILGattPropertiesErrorToastModel.swift @@ -0,0 +1,71 @@ +// +// SILGattPropertiesErrorToastModel.swift +// BlueGecko +// +// Created by Kamil Czajka on 01/06/2020. +// Copyright © 2020 SiliconLabs. All rights reserved. +// + +import Foundation +import CoreBluetooth + +@objc +class SILGattPropertiesErrorToastModel : NSObject, SILToastModelType { + let peripheralName: String + let errorCode: Int + var errorDescription: String { + guard let CBATTErrorCode = CBATTError.Code(rawValue: errorCode) else { + return "Unspecified error" + } + switch CBATTErrorCode { + case .success: + return "The ATT command or request successfully completed" + case .invalidHandle: + return "0x0001 The attribute handle is invalid on this peripheral" + case .readNotPermitted: + return "0x0002 The permissions prohibit reading the attribute’s value" + case .writeNotPermitted: + return "0x0003 The permissions prohibit writing the attribute’s value" + case .invalidPdu: + return "0x0004 The attribute Protocol Data Unit (PDU) is invalid" + case .insufficientAuthentication: + return "0x0005 Failed for lack of authentication." + case .requestNotSupported: + return "0x0006 The attribute server doesn’t support the request received from the client" + case .invalidOffset: + return "0x0007 The specified offset value was past the end of the attribute’s value" + case .insufficientAuthorization: + return "0x0008 Failed for lack of authorization." + case .prepareQueueFull: + return "0x0009 The prepare queue is full, too many write requests in the queue" + case .attributeNotFound: + return "0x000A The attribute wasn’t found within the specified attribute handle range" + case .attributeNotLong: + return "0x000B The ATT read blob request can’t read or write the attribute" + case .insufficientEncryptionKeySize: + return "0x000C The encryption key size used for encryption is insufficient" + case .invalidAttributeValueLength: + return "0x000D The length of the attribute’s value is invalid" + case .unlikelyError: + return "0x000E The ATT request encountered an unlikely error" + case .insufficientEncryption: + return "0x000F Failed for lack of encryption" + case .unsupportedGroupType: + return "0x0010 The attribute type isn’t a supported grouping attribute" + case .insufficientResources: + return "0x0011 Resources are insufficient to complete the ATT request" + @unknown default: + return "Unspecified error" + } + } + + @objc + init(peripheralName: String, errorCode: Int) { + self.peripheralName = peripheralName + self.errorCode = errorCode + } + + func getErrorMessageForToast() -> String { + return "Failed action on \(peripheralName)\nReason: \(errorDescription)" + } +} diff --git a/SiliconLabsApp/Models/SILOTAFirmwareUpdateManager.m b/SiliconLabsApp/Models/SILOTAFirmwareUpdateManager.m index b4070824..fb1c4766 100644 --- a/SiliconLabsApp/Models/SILOTAFirmwareUpdateManager.m +++ b/SiliconLabsApp/Models/SILOTAFirmwareUpdateManager.m @@ -100,11 +100,15 @@ - (void)didConnectToPeripheral:(NSNotification *)notification { } - (void)didDisconnectFromPeripheral:(NSNotification *)notification { - if (self.expectingToDisconnectFromPeripheral) { - self.expectingToDisconnectFromPeripheral = NO; - } else { - NSError *error = [NSError sil_errorWithCode:SILErrorCodeOTADisconnectedFromPeripheral underlyingError:nil]; - [self.delegate firmwareUpdateManagerDidUnexpectedlyDisconnectFromPeripheral:self withError:error]; + NSString* uuid = (NSString*)notification.userInfo[SILNotificationKeyUUID]; + + if ([uuid isEqualToString:self.peripheral.identifier.UUIDString]) { + if (self.expectingToDisconnectFromPeripheral) { + self.expectingToDisconnectFromPeripheral = NO; + } else { + NSError *error = [NSError sil_errorWithCode:SILErrorCodeOTADisconnectedFromPeripheral underlyingError:nil]; + [self.delegate firmwareUpdateManagerDidUnexpectedlyDisconnectFromPeripheral:self withError:error]; + } } } @@ -134,8 +138,7 @@ - (void)cycleDeviceWithInitiationByteSequence:(BOOL)initiatingByteSequence dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kSILDurationBeforeUpdatingDFUStatusToWaiting * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ progress(SILDFUStatusWaiting); - self.didDiscoverOTADevice = NO; - [self.centralManager addScanForPeripheralsObserver:self selector:@selector(searchHandlerForOTADevice)]; + [self reconnectToOTADevice]; }); } @@ -143,20 +146,9 @@ - (void)endCycleDevice { [self.centralManager removeScanForPeripheralsObserver:self]; } -- (void)searchHandlerForOTADevice { - if (self.didDiscoverOTADevice) { return; } - - for (SILDiscoveredPeripheral *discoveredPeripheral in self.centralManager.discoveredPeripherals) { - NSString * const name = discoveredPeripheral.advertisedLocalName; - - if ([@"OTA" isEqualToString:name]) { - self.didDiscoverOTADevice = YES; - self.peripheral = discoveredPeripheral.peripheral; - [self.centralManager removeScanForPeripheralsObserver:self]; - [self.centralManager connectToDiscoveredPeripheral:discoveredPeripheral]; - break; - } - } +- (void)reconnectToOTADevice { + SILDiscoveredPeripheral* discoveredPeripheral = [self.centralManager discoveredPeripheralForPeripheral:self.peripheral]; + [self.centralManager connectToDiscoveredPeripheral:discoveredPeripheral]; } - (void)uploadFile:(SILOTAFirmwareFile *)file diff --git a/SiliconLabsApp/Models/SILSavedSearchesRealmModel.h b/SiliconLabsApp/Models/SILSavedSearchesRealmModel.h index 0003691e..033810c9 100644 --- a/SiliconLabsApp/Models/SILSavedSearchesRealmModel.h +++ b/SiliconLabsApp/Models/SILSavedSearchesRealmModel.h @@ -18,13 +18,12 @@ RLM_ARRAY_TYPE(SILBeaconTypeRealmModel) @interface SILSavedSearchesRealmModel : RLMObject @property NSString* searchByDeviceName; -@property NSString* searchByAdvertisingData; @property NSInteger dBmValue; @property RLMArray* beaconTypes; @property BOOL isFavouriteSetFilter; @property BOOL isConnectableSetFilter; -- (instancetype)initWithSearchByDeviceNameText:(NSString*)searchByDeviceNameText searchByRawAdveritisingDataText:(NSString*)searchByAdvertisingDataText dBmValue:(NSInteger)dBmValue beaconTypes:(RLMArray*)beaconTypes isFavourite:(BOOL)isFavourite andIsConnectable:(BOOL)isConnectable; +- (instancetype)initWithSearchByDeviceNameText:(NSString*)searchByDeviceNameText dBmValue:(NSInteger)dBmValue beaconTypes:(RLMArray*)beaconTypes isFavourite:(BOOL)isFavourite andIsConnectable:(BOOL)isConnectable; @end diff --git a/SiliconLabsApp/Models/SILSavedSearchesRealmModel.m b/SiliconLabsApp/Models/SILSavedSearchesRealmModel.m index efe8eab6..1fcaab72 100644 --- a/SiliconLabsApp/Models/SILSavedSearchesRealmModel.m +++ b/SiliconLabsApp/Models/SILSavedSearchesRealmModel.m @@ -16,12 +16,11 @@ @interface SILSavedSearchesRealmModel () @implementation SILSavedSearchesRealmModel -- (instancetype)initWithSearchByDeviceNameText:(NSString*)searchByDeviceNameText searchByRawAdveritisingDataText:(NSString*)searchByAdvertisingDataText dBmValue:(NSInteger)dBmValue beaconTypes:(RLMArray*)beaconTypes isFavourite:(BOOL)isFavourite andIsConnectable:(BOOL)isConnectable { +- (instancetype)initWithSearchByDeviceNameText:(NSString*)searchByDeviceNameText dBmValue:(NSInteger)dBmValue beaconTypes:(RLMArray*)beaconTypes isFavourite:(BOOL)isFavourite andIsConnectable:(BOOL)isConnectable { self = [super self]; if (self) { self.searchByDeviceName = searchByDeviceNameText; - self.searchByAdvertisingData = searchByAdvertisingDataText; self.dBmValue = dBmValue; self.beaconTypes = beaconTypes; self.isFavouriteSetFilter = isFavourite; diff --git a/SiliconLabsApp/Models/SILToastModelType.swift b/SiliconLabsApp/Models/SILToastModelType.swift new file mode 100644 index 00000000..68706f13 --- /dev/null +++ b/SiliconLabsApp/Models/SILToastModelType.swift @@ -0,0 +1,17 @@ +// +// SILToastModelType.swift +// BlueGecko +// +// Created by Kamil Czajka on 01/06/2020. +// Copyright © 2020 SiliconLabs. All rights reserved. +// + +import Foundation + +@objc +protocol SILToastModelType { + var errorDescription: String { get } + var peripheralName: String { get } + var errorCode: Int { get } + @objc func getErrorMessageForToast() -> String +} diff --git a/SiliconLabsApp/Models/SILUUIDProvider.h b/SiliconLabsApp/Models/SILUUIDProvider.h index f58727e6..743c1f94 100644 --- a/SiliconLabsApp/Models/SILUUIDProvider.h +++ b/SiliconLabsApp/Models/SILUUIDProvider.h @@ -21,13 +21,13 @@ @interface SILUUIDProvider (OTA) extern NSString * const kSILOtaServiceUUIDString; -extern NSString * const kSILOtaCharacteristicDataUUIDString; -extern NSString * const kSILOtaCharacteristicControlUUIDString; -extern NSString * const kSILOtaCharacteristicFirmwareVersionUUIDString; +extern NSString * const kSILOtaCharacteristicOTADataAttributeUUIDString; +extern NSString * const kSILOtaCharacteristicOTAControlAttributeUUIDString; +extern NSString * const kSILOtaCharacteristicAppLoaderVersionUUIDString; extern NSString * const kSILOtaCharacteristicOtaVersionUUIDString; @property (strong, nonatomic, readonly) CBUUID *otaServiceUUID; -@property (strong, nonatomic, readonly) CBUUID *otaCharacteristicDataUUID; -@property (strong, nonatomic, readonly) CBUUID *otaCharacteristicControlUUID; +@property (strong, nonatomic, readonly) CBUUID *otaCharacteristicOTADataAttributeUUID; +@property (strong, nonatomic, readonly) CBUUID *otaCharacteristicOTAControlAttributeUUID; @end diff --git a/SiliconLabsApp/Models/SILUUIDProvider.m b/SiliconLabsApp/Models/SILUUIDProvider.m index 470cbc6f..8fdbb18f 100644 --- a/SiliconLabsApp/Models/SILUUIDProvider.m +++ b/SiliconLabsApp/Models/SILUUIDProvider.m @@ -11,21 +11,23 @@ @implementation SILUUIDProvider (OTA) NSString * const kSILOtaServiceUUIDString = @"1d14d6ee-fd63-4fa1-bfa4-8f47b42119f0"; -NSString * const kSILOtaCharacteristicDataUUIDString = @"984227f3-34fc-4045-a5d0-2c581f81a153"; -NSString * const kSILOtaCharacteristicControlUUIDString = @"f7bf3564-fb6d-4e53-88a4-5e37e0326063"; -NSString * const kSILOtaCharacteristicFirmwareVersionUUIDString = @"4f4a2368-8cca-451e-bfff-cf0e2ee23e9f"; +NSString * const kSILOtaCharacteristicOTADataAttributeUUIDString = @"984227f3-34fc-4045-a5d0-2c581f81a153"; +NSString * const kSILOtaCharacteristicOTAControlAttributeUUIDString = @"f7bf3564-fb6d-4e53-88a4-5e37e0326063"; +NSString * const kSILOtaCharacteristicAppLoaderVersionUUIDString = @"4f4a2368-8cca-451e-bfff-cf0e2ee23e9f"; NSString * const kSILOtaCharacteristicOtaVersionUUIDString = @"4cc07bcf-0868-4b32-9dad-ba4cc41e5316"; +NSString * const kSILOtaCharacteristicGeckoBootloarderVersionUUIDString = @"25f05c0a-e917-46e9-b2a5-aa2be1245afe"; +NSString * const kSILOtaCharacteristicApplicationVersionUUIDString = @"0d77cc11-4ac1-49f2-bfa9-cd96ac7a92f8"; - (CBUUID *)otaServiceUUID { return [CBUUID UUIDWithString:kSILOtaServiceUUIDString]; } -- (CBUUID *)otaCharacteristicDataUUID { - return [CBUUID UUIDWithString:kSILOtaCharacteristicDataUUIDString]; +- (CBUUID *)otaCharacteristicOTADataAttributeUUID { + return [CBUUID UUIDWithString:kSILOtaCharacteristicOTADataAttributeUUIDString]; } -- (CBUUID *)otaCharacteristicControlUUID { - return [CBUUID UUIDWithString:kSILOtaCharacteristicControlUUIDString]; +- (CBUUID *)otaCharacteristicOTAControlAttributeUUID { + return [CBUUID UUIDWithString:kSILOtaCharacteristicOTAControlAttributeUUIDString]; } @end @@ -65,10 +67,12 @@ - (void)preparePredefinedServicesNames { - (void)preparePredefinedCharacteristicsNames { _predefinedCharacteristicsNames = @{ - kSILOtaCharacteristicDataUUIDString : @"OTA Data", - kSILOtaCharacteristicControlUUIDString : @"OTA Control", - kSILOtaCharacteristicFirmwareVersionUUIDString : @"OTA Firmware Version", - kSILOtaCharacteristicOtaVersionUUIDString : @"OTA Version", + kSILOtaCharacteristicOTADataAttributeUUIDString : @"OTA Data Attribute", + kSILOtaCharacteristicOTAControlAttributeUUIDString : @"OTA Control Attribute", + kSILOtaCharacteristicAppLoaderVersionUUIDString : @"AppLoader version", + kSILOtaCharacteristicOtaVersionUUIDString : @"OTA version", + kSILOtaCharacteristicGeckoBootloarderVersionUUIDString : @"Gecko Bootloarder version", + kSILOtaCharacteristicApplicationVersionUUIDString : @"Application version" }; } diff --git a/SiliconLabsApp/Protocols/SILCharacteristicFieldRow.h b/SiliconLabsApp/Protocols/SILCharacteristicFieldRow.h index 4a5c5f83..c97fc28a 100644 --- a/SiliconLabsApp/Protocols/SILCharacteristicFieldRow.h +++ b/SiliconLabsApp/Protocols/SILCharacteristicFieldRow.h @@ -23,5 +23,6 @@ - (NSString *)secondaryTitle; - (NSInteger)consumeValue:(NSData *)value fromIndex:(NSInteger)index; //return read length - (NSData *)dataForFieldWithError:(NSError * __autoreleasing *)error; +- (void)clearValues; @end diff --git a/SiliconLabsApp/SILAppDelegate.m b/SiliconLabsApp/SILAppDelegate.m index d37e8b57..4d8a20cb 100644 --- a/SiliconLabsApp/SILAppDelegate.m +++ b/SiliconLabsApp/SILAppDelegate.m @@ -20,6 +20,7 @@ @implementation SILAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [SILAppearance setupAppearance]; [Fabric with:@[CrashlyticsKit]]; + [SILRealmConfiguration updateRealmConfigurationIfNeeded]; return YES; } diff --git a/SiliconLabsApp/Storyboards/DevelopApps/SILAppBluetoothBrowser.storyboard b/SiliconLabsApp/Storyboards/DevelopApps/SILAppBluetoothBrowser.storyboard index bf5ebb4c..afb4603e 100644 --- a/SiliconLabsApp/Storyboards/DevelopApps/SILAppBluetoothBrowser.storyboard +++ b/SiliconLabsApp/Storyboards/DevelopApps/SILAppBluetoothBrowser.storyboard @@ -49,23 +49,10 @@ - - - @@ -80,9 +67,22 @@ + + + @@ -105,41 +105,33 @@ - + - - - - - - - - @@ -194,7 +186,7 @@ - + @@ -271,7 +263,7 @@ - + @@ -334,7 +326,7 @@ - + @@ -405,15 +397,15 @@ - - + + - + - + @@ -422,16 +414,13 @@ - - - + + @@ -479,7 +469,7 @@ - + @@ -592,7 +582,6 @@ - @@ -753,10 +742,10 @@ - + - + @@ -795,70 +784,16 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - + @@ -965,12 +900,12 @@ - + - + @@ -1013,7 +948,7 @@ - + @@ -1022,7 +957,7 @@ - + - + - + @@ -1124,7 +1059,7 @@ - + @@ -1294,7 +1229,6 @@ - @@ -1302,7 +1236,6 @@ - @@ -1314,8 +1247,6 @@ - - @@ -1637,17 +1568,16 @@ - - - + + - + - + diff --git a/SiliconLabsApp/Storyboards/DevelopApps/SILAppBluetoothBrowserDetails.storyboard b/SiliconLabsApp/Storyboards/DevelopApps/SILAppBluetoothBrowserDetails.storyboard index 241d9636..34c0efed 100644 --- a/SiliconLabsApp/Storyboards/DevelopApps/SILAppBluetoothBrowserDetails.storyboard +++ b/SiliconLabsApp/Storyboards/DevelopApps/SILAppBluetoothBrowserDetails.storyboard @@ -41,28 +41,28 @@ - + - - - - - - - + + + + + + - - + + + - @@ -97,10 +100,10 @@ - + + - @@ -285,7 +288,7 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - star - off-1.imageset/Contents.json b/SiliconLabsApp/SupportingFiles/Images.xcassets/MenuIcon.imageset/Contents.json similarity index 66% rename from SiliconLabsApp/SupportingFiles/Images.xcassets/icon - star - off-1.imageset/Contents.json rename to SiliconLabsApp/SupportingFiles/Images.xcassets/MenuIcon.imageset/Contents.json index 4575bb38..47601f72 100644 --- a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - star - off-1.imageset/Contents.json +++ b/SiliconLabsApp/SupportingFiles/Images.xcassets/MenuIcon.imageset/Contents.json @@ -2,17 +2,17 @@ "images" : [ { "idiom" : "universal", - "filename" : "icon - star - off.png", + "filename" : "MenuIcon.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "icon - star - off@2x.png", + "filename" : "MenuIcon2.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "icon - star - off@3x.png", + "filename" : "MenuIcon2-1.png", "scale" : "3x" } ], diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/MenuIcon.imageset/MenuIcon.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/MenuIcon.imageset/MenuIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..dd6986666bb82f1703b71716f451eea92da4eab7 GIT binary patch literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nC-#^NA%Cx&(BWL^R}Cp}#pLo!(3 zPV?qEY{0|%`K4-2^zSYETo#&&Y$_BxIP=1So-oHwxob8vGiLE{dN?o%O*mlDSh;7* z`afGUd! SwzmL1#o+1c=d#Wzp$Py50&*At literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/MenuIcon.imageset/MenuIcon2-1.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/MenuIcon.imageset/MenuIcon2-1.png new file mode 100644 index 0000000000000000000000000000000000000000..84af0035605a060803f0965507b8d959613ece20 GIT binary patch literal 522 zcmeAS@N?(olHy`uVBq!ia0vp^DIm$i%`Spx}VX zc;MFf^jPG}!2Q=J=W$6*d(G*!xbBZxmh@>G7Na-b3IdHh!V=s1q?INlG9A8F=*(i| zlW@Z-ufw5@lPCH(uiy!T#>Ce<6qzK|402@6T@((pw5@&I#*s3kp%iT20eeA-zgcJY zy_bJpS+_T7@4DATdwsub&b|Fz`}v)#)>Z=VQ}2E?HBcA%(4OdVIat7>zuF>HLDARW zQNi}z!;r=n6I<5CFFV?US%ofr5n;LXSk6z>gGhz7{MyW1#|vxzUEN>6UL&Io_J3m# z<9f!apPuo5&(6QMW{b1J(mg*9vraPCekv+ZLPDmP!^Yn%d9 oEABzMQa*Jop$!XvgO~@L&AS!kRfG?&2SzS~r>mdKI;Vst00Q>GGynhq literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/MenuIcon.imageset/MenuIcon2.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/MenuIcon.imageset/MenuIcon2.png new file mode 100644 index 0000000000000000000000000000000000000000..84af0035605a060803f0965507b8d959613ece20 GIT binary patch literal 522 zcmeAS@N?(olHy`uVBq!ia0vp^DIm$i%`Spx}VX zc;MFf^jPG}!2Q=J=W$6*d(G*!xbBZxmh@>G7Na-b3IdHh!V=s1q?INlG9A8F=*(i| zlW@Z-ufw5@lPCH(uiy!T#>Ce<6qzK|402@6T@((pw5@&I#*s3kp%iT20eeA-zgcJY zy_bJpS+_T7@4DATdwsub&b|Fz`}v)#)>Z=VQ}2E?HBcA%(4OdVIat7>zuF>HLDARW zQNi}z!;r=n6I<5CFFV?US%ofr5n;LXSk6z>gGhz7{MyW1#|vxzUEN>6UL&Io_J3m# z<9f!apPuo5&(6QMW{b1J(mg*9vraPCekv+ZLPDmP!^Yn%d9 oEABzMQa*Jop$!XvgO~@L&AS!kRfG?&2SzS~r>mdKI;Vst00Q>GGynhq literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/beacon.imageset/icon - beacon - light.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/beacon.imageset/icon - beacon - light.png deleted file mode 100644 index 15a425a3eb837f5186d96d454f5336afa0c541db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 480 zcmeAS@N?(olHy`uVBq!ia0vp^{6H+k!3HE-=Cy!0jKx9jP7LeL$-D$|EK(yp(|mmy zw18|52FCVG1{RPKAeI7R1_q`DOmGqY1vjNZ1 z#>fBDT1uKyn$wz@xTpABc+DoM=ySn`p@ex8MUbITa`9nU763g2JRyVYZ}K?}R;f5R;A z==38o`ZkkecDTNJ?|4OUoj{tm)74l;rvA#-_LHTDK_Ttw>gTe~DWM4f DCc?PR diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/beacon.imageset/icon - beacon - light@2x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/beacon.imageset/icon - beacon - light@2x.png deleted file mode 100644 index 5b27396c5a0320070649068d42dacfadd1f977f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 807 zcmV+?1K9kDP)=Z@00001b5ch_0Itp) z=>Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR919iRgM z1ONa40RR91Bme*a08xVOoB#jJNR9FeURqb)&Fc8H|{wF2)I-rBoRS=s7&U8Qt zp@KLS9BBaPAf|#q1#vnc6>#rqw2lz^FoTI^G}?W;de6JE>}=Fytk>%tX^)tNNmpiV zv2yN?$Kz%?oo-<#M;6O~m_qy+a}vw5Vh7kyWm&#h*qP}Sh1df!*O=;TM?auN8nnxu zFa1muf~@Kdlfu`I8HATE$%*K|f}(XM+=-f&ejsy*3Pn)XKd877XDkL*p5ZM7DS?{w zdqR7pMFA+-oVOY*W-jEqy%2o|FcC1e;y!M(ENhqpn|l=ed-^@bFSNP6gJ6#2q#y+L zATRI%Xl-)Swi?)98xPn8@S5$V5gkTQ@hROzUu$A#s%aRHD+q+t=HrV`_+Hp;s zt;HuaCkNX*f?&<{mIE43!mlf(gK8TD^k+(fL#=j|nFFWB{o18Aki73AoNE-5`T7XloSEqf_UO|Uwv#+hfe^RM{sJ6JK z&D={4;d587C|YvBCgmpg^(n}s3gvr6nQYEt7TOrWXV}|B;@|hwmxRqr;Fk!$m_0ta z4hvQ)8HQvbeq?<`?mQCjLf*Hxi&_T$FiW_hpELz4D?MuV7A1@MZ~k)_FB0V5WP5D^ l)9Mme;?rEI#U&LA*MAP@=@E{J4AlSt002ovPDHLkV1nIFT?_yK diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/beacon.imageset/icon - beacon - light@3x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/beacon.imageset/icon - beacon - light@3x.png deleted file mode 100644 index 482c419fbe6be79ad8fb1630c593fad3ddd6245e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1217 zcmV;y1U~zTP)Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91E1&}a z1ONa40RR91HUIzs0M%qR@c;k>H%UZ6RA>dwSnYA6Fc3X^?tkbYbZ}e+p@Z0IAf$qG zDhOA>xq~czdymsHVDy0i2Wv(nX+PdRtro#B`!=9$x7!HrSLiW% zphgT;D==fpK3xgEexM4Bff=O$%iY$nR(D1T9mlmqzReUP4kfi8SJWv%G&Ex@uD^4X@mDO8~ zv6l$1(GT=D)!)6G&BiNHeOHr8APUd%3HEEe?{{@Sm6Zj^r}&7utm{1HEX|bm<<$ky zznF&<_%x8%gTw;&VuR!{h9bR&yzl8D#4?)$C;~`$9Kf|!L?ahb;=tyBo?yxo=+hQ- zV$H^!_%@Z%gO48xvz~o?l-b>@Xm=#WKy$T~1Psf$il zO{21AnwM|j~d>7tIi7#-QJUA{NCt zjNtSE9vk6OlI5bl15r*rk`oen5FQm-2shZtUKD3CvOKF+k);GXNh{6tliw`QI*kU| zbIGo%)kaoHxt?-{%#GB{j|(N~VhYIUz3$8BYn1|YgpM{#U_ z^0n<`wPab?Bz$sa?ErkJc5U&0iM9;H_$$i+C;5igYAmsJqc*c*$Rh#B$?sm4obC99 zkCiN+NPsNF_*5f_vh-Z|RVhA*Fd{($x( z$~|^(}@gm0;m2r!M zR^DmO`P_Q^v7sDadvI!*l`Q+yo7>(ltj{-_-LBE~?8eSHf?aE3^!hu_ZM(g;eeJuv zpDrphox~i!IO#n4BYNkoW+rF4UIPnF1C`H3v~N@-w4cMzoT&u+hQy=3rFF@c{CN)zLz75Cat zoipL`_q%z^muCBOray0qko_T0`GTXqTix_%-W0E;Ei&xZQ-hrzXG=X+_WkrndH=_` z_daP~y>d0eo6SINXS&#WmYla1$JYDLXz^PbFFO56ixSh5N8KB%S6@A~_WEo6t65tQ z^0qO(PHfKXnDOzy!?k5QzFCCDOzMx(5pzCJ6>s=2a%~v5N6yUW3LE|ze!BS7_;}~H z_I;sxR#WE$bcS5=P~LU9{)boWi`sw9QPXlB<%`VIoqoD{-f3gY^#8Bt1bCJ_VBYpp zZ`rE;*Li#6&hswJIV5E);1=<8#*QzN3uT%Q9&q&iqO)S%_j0vQg4KsDPi{#6$nE4B tYP$PntKQL+ly6Npwtt^1dFJ``dPeizHd6e~y#=6D<>~6@vd$@?2>_dsH6{Q6 literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/beaconDark.imageset/icon - beacon@2x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/beaconDark.imageset/icon - beacon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..953ec7712715067020919dc511fa6ff3fc24b78e GIT binary patch literal 1669 zcmV;027394P)Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91D4+uX z1ONa40RR91C;$Ke09h_XmH+?+`$uP&{aLR z``o|Nx4UPgO69RVR^Z20p!$E(Z*6UD%*@Qh-@bjTSF6>CSn6sySPs^?xw*+ceE4vB z3qSn%V1nD*+tKy)^;=RkAW)koC1iWpwpq-<;o)IEjgQ+rh~VqjuSG0zXJnV;89fz7 zr7}q>8%sh;i_mycb8E@FcklLmGD%KF(5iMqvbq97=#<`aLX(n+Fun*!Eq5Bjmayim^ zhH^koRe;>rA#z>xdsKGaJM&GFAJ8x4FIz~RYpX?43sp?%kVo{fZEOW0lHjFF(I;S} zz*MwJP!HIJ(2j5SxC~klL|gbR$cFJtwzaKYfOHNtPv7I>B*;Q5C8)DS@~>vSiF0b_ z=ftpMu%<^d;@YyhFvw(x9Ghd!?MS5HYno%Xq_a01lULC5vP)A+kc3+C8v=bIP;8gs z986m>AZH2o!6JHD%K>+_Kq-l<%OjFGyRu}v5-@TdkZVVF!>w=zwOR~b$OxxIl4>SK zPG>O$QPohXq7l7MSGD%kwJ@YwL_aax3KylpT(%M08{!bR#a!)$<-VQSvwCmF#LG^S z3sw}#iD3W8PM1Ju&TY*1Sfo`An+IXXBxSNFfOuhBMHW&=%zMOtCLLG~w!knPCt=w^?1}beQZ8yNzwvApDUC zII#eZ&hIRXqXNMaLb=?$QeHXiV&PONmn><0zf|t)Hbe8CW0EDgD+Pksm9W9c^}bz2GilA+#j-omdQP3&TXR}B}aEe!h*ix?fv!DC5U9-c#w0zoH)jAci# zOwtLdDz?&yc)? zx4=cHY0eF#-epaHmkAds>^o*t;RUZxrjqyhfx08yzaWs(hrn&aMMRy-X1Emy`gfDS zwIM<+a|*8x1t!6cl1#WHA`rG))zfz4^TuK&lFFf64Evs2xVJeUjL2i=FK~Q|Z1X~H z@kLRf1>_mqv~HDckNDl2l28i^i}URk8A$3!Nbt2B@=Ns?ZG{TTfJ2*@VZ;{tceVkh zZLJ&0n%PVgs+@e}s~Bu|Q%;JFi0Jywsd1sr77&2XpFj7OmzSRr@E4}PGCtL|E-fwn z1-40#Sj6_q%F0h)zI^#q7DJe-wz|6d8}@(De+KGPy_m}WEz)2o=KK4{k01ZE@jX#m zL0LdZEAkKhnF87kj)fDvhD@vq42w;d1T>LG+e(!pEmX09&VQWRBim)GI4UYz`k+_AJ?km6pkw(!7}FahCS$y zx5trWQMz=A#le)}RQ{AN7esG_9V{ZV z8vIKv_qs!;BIxE+AYrU3ga%2G+x6`;cp$KEU~^>K>G;@xaBeUx$6k_QBOmK9b7r2C z^Z-n+oEklLeTj^WoVN2ozTdkLCI0^%($?vZ_@oHa*i4G?sDG@$kF3DIRxeVR>eyRu P00000NkvXXu0mjfuLlp& literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/beaconDark.imageset/icon - beacon@3x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/beaconDark.imageset/icon - beacon@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6492b77d6f585cdb7fe0d66c05284d71194974e1 GIT binary patch literal 2604 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 O00006IS9Bl zKmNar`8=P7Vwgf%gQy4lEykA&RtB;Rq6J|BYZ%rr+)sX|^w6F0&6(MMH~oHFG(AAX zT>AH4y8|LoCoU?jY0VbjZJnE-C%Z`Qp?Dm-<`sj68Di64#C;Ll)$@*Np27h(n~Pyr zHncHRiDj}?DrQ`}kkMpXF-ed|_!Y0tf)=({+^f5q+TQ1|ODUh3Ja1yGo_xand*PSP zwd^UGzhxo_E*f6IC__0OVy0mT&csu@|TJ6pM)1*H2=*ZiedwqR%D%w5JRiM(PD zl+t?&URm2*UZFVu+)~|=G}D}esr?JS|0y|s+we$ANd?PY&8sWZ&ZmB?2&?@z^=C8R z%JmMPx#zTP6$?9AeQW=pGwZ9nZq1RMp0%^w`X|4Y;H%fiZADT+G2`j#=d#Wzp$P!f CNziBj literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/beaconWhite.imageset/icon - beacon-white@2x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/beaconWhite.imageset/icon - beacon-white@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0d89126d4ede0c05222ed54e050cb6e14969bcec GIT binary patch literal 1200 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 B`K15= diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/connectable.imageset/icon - bluetooth.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/connectable.imageset/icon - bluetooth.png new file mode 100644 index 0000000000000000000000000000000000000000..eb29c81e0114d3861d9a067f83d55156d6a527e9 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^fN^5+IfWVg?4L1x#=e!3E54wg^Zvb+?^2Q1LWR7sn6_!KoAd{SE~P z9Mko>Bx~Drui*e|74zQ4e%6+y_Z<8a?wUGWDq`_hurBAlb6~A@BDd%3?^DeuUw_qX zaq{(_O5^?KX7Ai%p*Z>ElRN{78lLvU>lbNU+jph!{h?#O9R*x0ZdTZs?YmPfu-2M& zmEy4(9QD7Bo!qi2^UaH24!1akG>$QQ9f;uS*t|$L$oZ4a{jg9`zBXc>7lDk4sSN{Cd@l`guNi)^v#@U7W-y#UG%toBWgOsgTe~DWM4f-Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91BA^2R z1ONa40RR91CjbBd0O?W;9smFXK1oDDR9FeMm``pRF%-r-6i}CR*IknvAaQ^syY3Qp z-E|_7h+T7orYDHlMF~=e8_*L#mR&^R2;>B%+a!X1-^j1BJsyw8LoE^8&+pG~o}WFC z!Fg3f&rj5%f8ju434o_~0e zA=RfCZl0f?CuO&(D>JlrH6H~0oMHVG@N2}Kv33%>xR_#G?T1ZZDj==^&eurZBwhidKQP!J3F>4}Uz>k3;M2J-s)dY~I?4l-NrnpZ%0eHa-5 z$O`075=1?P*MaCF10OLiWCF79zTy^p1BACAZo@u~<_W$h9ELa$N9eh&#&$q9L*ZU) zI;F$HPfm3uFH0@L7YLDtsv8*zH44zH1wnuR1Ee-YBM|(@XiW!Ji-Mq!=#20SFHRL- qba}&gQl}=wZf`w*0jT?K3$ literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/connectable.imageset/icon - bluetooth@3x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/connectable.imageset/icon - bluetooth@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..ed2a38519b8ebf1b58b389420078d78f564017c1 GIT binary patch literal 1270 zcmVPx#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91GoS+i z1ONa40RR91IsgCw0O8dQ6951NY)M2xRA>dwn!ipPF&xK}YeDT))^2$L)JQx4$s2^N z8;1mljqnEa2?`qmB$BNgFm@u0UC{LnVCx85B_hK28_tnqY@hFJC$V(0{j>f1{XVwO ze=e!lB8Huvo&9>f-i3+AXf*0QKR+MzdcDV3ChM^j@9yq)p!-tR51{;(F@WX<69Z_jFrk6w4iggS zBbDjG_V#uQ^ZF;}HkR1|>KvS%on^YZA!O1!RCyoLj?CWPUIWT6X@HI*ZXvqCjy}#) z<`Mpy+Dbs$W!={K#hHXv@kI6s2X8D6wvAEX$p10 zfwVA5l3c;q6P|Zrk%6=@CnqQOc}NCmUHD=5TKUiuQh3eVXIjI zq0>fIFC7c5FtY%{a({vMFg5uL&SO)VDldd#gIFU}1On52T;>kxgG+FNp`y@0E+AaO z%iKy!Uz>Rfz_b{E?+YQ-fN8-!s*UFe_14vLY?^JzH}RxunUwG6dyyn6PL_tDN*5qF zOQ;6x%K@+%%9^rFNml})j!H0WSOmm66+&eY+Nuo0#({ynK&D`LQXUS6-!KIlD9G@_ z-?Bnc2n6H|LO=1|@m=J%xPk2Fcx2#EibCF9R)q7ch-GoZWi{~xat0~#Gs!Pu+_)wn z1VX;Gyu5rN;B0ZxvTbSW3}W*W-{QvSw**cfT8wqr>z49KQ3W7p5E`-h$&M#1VczWp z6A#~%wB)=kEdkl8)i8yx%kCpZe&X8FOrZF|YPnXsFDO@w_f>%=zwSEXqOFUcJkDH+ z2AcQ@#F<8}6~RI4p;0tKFZ2B@dj&7_GszEf+K&wAjV>>0!USNeqUQ#`+awJ_fIXC^Dcgl z_ZhiS*>qJ9*$z7H5&1S35LsR}?FAD8L^vD*s4`4QkWfI*mos6Iu>(jSJ$);;kg*rZkKTh;2i}A+g8BMRi2veSk{A_<{(B@uEioa)t>75);S?CNxMHAWS>| zFPg$&ED)ieQx53^k=MixjZ7=Dj!bM2dc^+(U_sc_diN&Oe(%=RH^R&cgnx3tRBGy+ zqYR*3Q<*B){tTG^RU}m_OZie4E2-Ti$q{h?*~+Kkq@68c8X03xLD@_|V28h<_H53p zWm*^v?Y&wt&wGR5bY3_juthsZ1|N)KElj?tT`R4;L8uQM#p^^}T1P6%?lhJ@CL;<0lXsa1N+F^rykYSKN4z*v&dPO?gK0^cfVX}k$rU8&E gY{aI~68?C_ulHAFVDY_B761SM07*qoM6N<$f*6!7dH?_b literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/Contents.json b/SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/Contents.json index ddaaacdc..4575bb38 100644 --- a/SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/Contents.json +++ b/SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/Contents.json @@ -2,17 +2,17 @@ "images" : [ { "idiom" : "universal", - "filename" : "favourite.png", + "filename" : "icon - star - off.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "favourite@2x.png", + "filename" : "icon - star - off@2x.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "favourite@3x.png", + "filename" : "icon - star - off@3x.png", "scale" : "3x" } ], diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/favourite.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/favourite.png deleted file mode 100644 index e8278d99658811ec4c8b0d265d29a9b701410ce0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1007 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 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/favourite@3x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/favourite@3x.png deleted file mode 100644 index 395d40cb10a025c17b72b9851bc49b1ab5d75580..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3070 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 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/icon - star - off.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/icon - star - off.png new file mode 100644 index 0000000000000000000000000000000000000000..7ce854e3fd7a54e5a9d66ec0801d92f2cc8b6061 GIT binary patch literal 482 zcmeAS@N?(olHy`uVBq!ia0vp^LO?9Y!3HERME&^*q!^2X+?^QKos)S9Q%xf_a z=y6*!jr(GrG_8rQMi@fJK)D@TpOpuS+HKm4ONwrg5!Aijiku4JsTE>@X zA3V1%-93AK?){hz8eLlVzu(=pPFwh@k*=EOBco$-2P(}zW}MZyQW|T(x#LBT{}2Ck z5vQYmD}}hOJ&06PJi>PS)+sL0({o;(=d$cm4pG`5-IloF)LF&u$3I$Ll%0!VY)kyH zFa?aH5~ZtCAF=gD8$V`e(x#fRRs+uM#^Y}v5&z>21$``(vl_o=ZQ znpHAK&G@>~wR`uRILfl0K%pCQJ7U2-s+zR&`w=svDiRAZ&H0 z`IOfOSf|cAWN>Ab#zFO6az>R09~mUt9%k^YYkqw1`2Dw%kK|if*st=`Ot4+F*=kl{ z)#0MOarV_cr+GOJaoqLp^S`TbiebK<$g&a!AqIwOwj0e7^B7ya)#2=$ zRzGL;IRCzE_tq`GrrVefIXyqS_x9UaS`AyyavVAHVGHZa*2?1nKhLH03Z5`C4mMCr z4K`DrkejBaaBsV(p74ofF$~Xc9@1~KYfdwr-L5QrLTRz)xoHjWmFDf?s1Rk=+8MA- z`m%epn!udbW{q(168KgNrY256IZ8Bz5Q}WlF zewroJOjp3Da|hG8XFs;A@l~5#-q&rScBtjS>+bz;%XVM*`IAxmD$Ay&Rhtw+Nz>ES K&t;ucLK6VO$TW@s literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/icon - star - off@3x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/favouriteOff.imageset/icon - star - off@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..f10f06060201eaca8cc7b9a70ddfd8c128ccc955 GIT binary patch literal 1138 zcmV-&1daQNP)Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91HlPCl z1ONa40RR91GXMYp02ph7?f?J-=t)FDRA>d=nomv}F%-s`sS>HH(p`7WvYV)>#G*Tf z6Eryig9H)Fh=05+%z5@9xle?aV<%bJKt_Y-wqBB4O!GV&6c^&dj+EmAwSvdY&IC6qyQ*in8l5nF%)Bbtbp2*Z7Qnvrgh$COeI4GiTZ7ya`XbGAww|%-sI>73{l4- za#iY6j$Xi4$xy)hl%sPn0~rcFFLHDahFOS2#5$=jIXVYJp!+&Nq7CIZI{}CyC#h4g z?CX>l81o=VHY+2Q(ot>TAtE;QQvIeH*0Tz3VPN$TF zY3DUNyzAcx_Kk?z^EvE77OGRbt7ZWo?-@Q(!(-$@ZaU;^%Q6fS9ohV!L zv^N@!8rg4^3t)tpJ(Cj5-tHPh-D9R2n59G(qZCXu#O$?j|6SzQC>>zgRL{@P|1671 z3C2#dxAi_|@8dEfIhUVcgJ`Ytxe|;9_~jNKIVnx=pUo5j-%qF0DtB>3n|YNMm}t${ zFTkB4pXZ>0`nFa&Jn$QG2F zl#LvX!Sa-$7z`UZ8i85LkROX`cr6NWAbgL3xfB5#q{b>S0z)814hVsBQw30cLhvOP z2g3-rP3Cnk8#x+*8Ord45VFG{Uut3N3;u?>aHew92Gf-xgXPOBf^VsU2J{V%A{Dgh zsoJILjMSnHhJd4#6X`A5@Z40y?twZUjuO7m36pqiM4s>xwc z*h9aBYZ3W@D3hi52eU|4E-TXln|=2v$qb-uz}e)wSyHc6%(Vy`71;Wr=%6h}EilZM z)F6Yv79MfED1tGnf~^MEC^{`j6aG}HdFDq83_b3i^?GtzV-Pmd%h+1Ta*blZN&vac zR4%|~>gQQ4FgQt4PI~EQ)7+BdoAfgezK1YRsFtpqe}lAdP?j$4{r~^~07*qoM6N<$ Eg7YW;Q2+n{ literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOff.imageset/Contents.json b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOff.imageset/Contents.json new file mode 100644 index 00000000..ceb9be94 --- /dev/null +++ b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOff.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icon - filter - off.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icon - filter - off@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "icon - filter - off@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOff.imageset/icon - filter - off.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOff.imageset/icon - filter - off.png new file mode 100644 index 0000000000000000000000000000000000000000..ef6dfe661f3491b3767da155a047d24a3ad20610 GIT binary patch literal 333 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VV{wqX6T`Z5GB1G~i_{3uG+$o^ zEg+kNfw4W4fd!-lh^2s-fq`iO6I?`O0W*RPlAI`d={S%&?CIhdVj-BEAW_&Lc=PzN zW5?thYU)x9lWzP6f`WH|$U4?@!Z`B~P5)+>SA% z6xQS{P!?)j7tIjdoTT6}LB#nYFZ0Xe1`{HjH%hi$c5rHYWW=++(W9-7rEMj92S1a; zVXnqRGv-5wRRWVFgJ(59yuZI*e8SO-KTi}Xm^m_EG!=N%FV$h%BFErrpEUKLl7|70 ze$0*!58mJ3&v!)1>w?1s<%YI62P2UXCEi2^h8W(sq>9H~dqCduboFyt=akR{0DS;x Ai2wiq literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOff.imageset/icon - filter - off@2x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOff.imageset/icon - filter - off@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c7943e626bdfe10b9948b5cf0cbaa2f3c8825ba5 GIT binary patch literal 438 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|EK(yp(|mmy zw18|52FCVG1{RPKAeI7R1_q`DOmGp61`9#WQETE|lRmzMsT9D?PfMMW*c`+u6Cbj)(TlHc1zH zqI_%}lhXOm<~sf})O*&w|9zVM(6s`lq|a;R|~98lr~iF-Pip8k;3aO1$SFcDCgb&#;K$~ zJs`)IMd0Ye?gPwqM--1T1Uq&yEo%^A-=grb*sZl%>eYcb*&mF57fcD(Q_GD9g`cOZ KpUXO@geCy_1ETBz literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOff.imageset/icon - filter - off@3x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOff.imageset/icon - filter - off@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..dceb8630f407ba9f0dd405825f7722e24bc2183f GIT binary patch literal 582 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$;i8jKx9jP7LeL$-D$|EK(yp(|mmy zw18|52FCVG1{RPKAeI7R1_q`DOmGpK1dpD?6=0HGwmN2~26I26(Mw=XpGt?J*4wa+T;zSP$5Gwc5q^>|nBg5}W% z^@{D+@xI&0;nTTyH+SBXKM!Xw=dkg;EWB`U^A9%nEuU>F_wBp2=(<7too#RTt$WRF zyH&oXdEssGH;)Z??Dy^LIB4T=GJ|s(uj7NDi$QPH3Nl{^yh$t2d=Zc%BGG%Vsh4f> zh3Ji*2X4KP$T8X=W+gUTWRCP)rr9!cxaV@sW}PE7S7^N@@@940YypIdHpV(*lRra7$`5?e)=-JiO5|II9iSYA_uw@G_;E;_MU zYDSr%-j>;g4oN$=NzLf~7~gpzBeZ}sqtrl(mr?nm!3O@hzaMjRm1S7jUw%9N-i3WH pAHSY@`|So69Y*g1LO=RB|1fIZkdw)_Q#%ccPfu4rmvv4FO#q5#?biSR literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark-1.imageset/Contents.json b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOffSelected.imageset/Contents.json similarity index 65% rename from SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark-1.imageset/Contents.json rename to SiliconLabsApp/SupportingFiles/Images.xcassets/filterOffSelected.imageset/Contents.json index b36b848c..2be0fa63 100644 --- a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark-1.imageset/Contents.json +++ b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOffSelected.imageset/Contents.json @@ -2,17 +2,17 @@ "images" : [ { "idiom" : "universal", - "filename" : "icon - wifi - dark.png", + "filename" : "filterOffSelected.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "icon - wifi - dark@2x.png", + "filename" : "filterOffSelected@2x.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "icon - wifi - dark@3x.png", + "filename" : "filterOffSelected@3x.png", "scale" : "3x" } ], diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOffSelected.imageset/filterOffSelected.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOffSelected.imageset/filterOffSelected.png new file mode 100644 index 0000000000000000000000000000000000000000..84099b3a9fa956a62b3f6302d302a3c80693dd82 GIT binary patch literal 237 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VV{wqX6T`Z5GB1Ig<(@8%Ar^wk z2@-`3f=7?%n*U$?q4UX~^&!6xoH(Ju5p=F)4ZFs~jx*|wu^hqOubdXV`2YXEu*B{B zv+bG+F3mEGC3l%-Sn)7B3LTN^%9HTW4`c2$6q%h^!FwcKL+`Ld6$jIT@7xj&8~KkW z*vZ9eZ0vEq%W6_sV%KG2$hIg$;fSQEKsJZ%0j)0Q2?-KRo3An_>@q&GaDGiPx$AW1|)R9Fe^*t-n^F$@6Egh{BV?3N{F~L=lCH^RW|w z%^8JLe{Qe6C~rE!N$BHCb!7e(Z%m37&^w-eWq3NhshZ zJltXal{4C6#6X(|X!ETD%s_ibXzxADMFM*vV>ry81hz&}xNKl6WC~XVlt$*@ih)wd zTwE29i!8xa18b3`I2N!JS%YH(bCI<;j89#4xSt&O>koi)>>%#V-4LO0F<>jCD6kb0Gg{)grzLdH?_b07*qoM6N<$ Ef)d@2W&i*H literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOffSelected.imageset/filterOffSelected@3x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOffSelected.imageset/filterOffSelected@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..bac7572554a92b2e38dd2cecca2eed3258ec39a0 GIT binary patch literal 503 zcmVPx$u}MThRA>e5+RaVFKoA9BFBL!&T%iO?a0#Rg8h|5|kWxYk9O0A_(t(+ge2P5L zcz6B|&k_e#>+yVVVj-JM6AmPR1dsp{Kmter2?Qn3G}l>{<(2k*wvWGi&Hn5?%iH6j zwRYUYDxR^v_Bl(} z)_tVuY`Ty6{yous{3I32USF{<^l`pOy{N%lpfXy6Wy3CTRkRk{1+IqPgY5=aLGQ&5 zfwR#)*kN!Mx)*B#jz*7Q&A?IUQLHK08*PC#2YaEdur6S0v?bOJY=ySQx`Id19$0s9 zFWL*s{b-+ud2iRTKB|e&r$W*B)+3&9j%wK*%w16PXPTPotqJer{i^OeHidvY`f@u3 zV7F+C6vl*$48pAqOxP{jB84&GB7<;i0~2C;OY$LOX z1b;=sq-%wN=`F^I-ew%6MxxfkzzibvF&iBUu7C~)XQNZVndmg|4Rnb=U)-PDL0^ZO t`Rk2zoayJ;?LsJw1dsp{Kmx@R_y-wl#Syl1Wn=&V002ovPDHLkV1jL<=WGA~ literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark-1.imageset/Contents.json b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActive.imageset/Contents.json similarity index 62% rename from SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark-1.imageset/Contents.json rename to SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActive.imageset/Contents.json index 8ae95f88..9bfd3793 100644 --- a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark-1.imageset/Contents.json +++ b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActive.imageset/Contents.json @@ -2,17 +2,17 @@ "images" : [ { "idiom" : "universal", - "filename" : "icon - beacon - dark.png", + "filename" : "icon_-_filter_-_active3x_20x20.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "icon - beacon - dark@2x.png", + "filename" : "icon - filter - active@2x.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "icon - beacon - dark@3x.png", + "filename" : "icon - filter - active@3x.png", "scale" : "3x" } ], diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActive.imageset/icon - filter - active@2x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActive.imageset/icon - filter - active@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d8bae4ca15dbd57e5b17d45e926e7b057f7cf381 GIT binary patch literal 561 zcmV-10?z%3P)Px$>q$gGR9Fe^mOn}ZK@`SgLhJ;+fvH7ESOiNwfQ7oXAb5#jlSaIR;9?zYygZ=iJ~(fx`K-adABcCs@?a0ZrnGxPrZ_M4eaTCLE60zrZ8R$!+LKZ>H?vNfOc zX`1Gk+CGc9$XyE9zIqwBC}5MLsAf)1o$Tb{qrZN=ndy0(aaQ#9{r)H^is_FQ`<=J= z3u}PO{N~!P5)jT!AXgXTgZZfU&7$&L>$655-5UWBun8pLq)NIt#wO6xGbocm`l9n8 zeWVRvC})C0j8s+Tpj}HrI@eW_s+fv1FM7tvIoP!nWKbO?JJ-*b@HfHX^59|a)5qB% zZP{A^BG3f6T<%R|fg&4VS)K$B*MG)$3)-@`21IZbq`b8()QyvK+JwW{q%wRq0z_aH zB-YsgG#wsKaI3NXRILsia#&pw7L~oeINkWEHysW>B|rw5`v}W5nXBlcY^)#?LrL1` zSL@v~^m1GAYlIiUd>I@R#=35e6+}W^luGD%U0jYCBj;c@R!}Fzm|$f^bxf6?7r?rZ zI0jX4$m3;;r9vhE>04xbQ;@~D-5lHY-T`faw0{89ir9{Wg0_lj|No(2({G(tO^%%@ z=nK*rAl}*sq*Fj2kj?=$r2Joqwa|hB+or%D7S6+tPLw*D00000NkvXXu0mjf^kD(g literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActive.imageset/icon - filter - active@3x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActive.imageset/icon - filter - active@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..d1895a9ede1e522559f40eb3a2598ca88e84952a GIT binary patch literal 1040 zcmV+r1n>KaP)Px&%1J~)RA>e5Sj|gRQ54ttNEab}K$#k7ZNkx1#6~lSHtnM3BNKv5%SFo;7CJ-B zqDG`ee}FbKj=*#An(Y7bF9h#Hsu6s}GanDT{0-F1tmn;o;E@H3|(&bMae;TTOz>D1!S=g~W ze(D?e7x;$CC&QH={o?wahX`O31RUNK<^G@{d) zOlDIqwUPO4&hg-POq7|-F+KG|vGm(lP zxz0d4*HapuNjVj+UEJckT}JBEtXEp^ZzbcDkcBvy0+9-Sh#hz0>Ja&aCp zSyDH`Z~*)cW?vkNS%;DOscz3(a?iUa$5zSH8S!-`44~P!q%TT3jfv3G9ZVdfbtGA^ zDV+?6(VZ}WLZ=;Pl6zpS=7Ld8byq)f_>*-QuLoJcGK4Syr>}TEtLd~W5W9?{nhtqp z;EhTzvW5}{aOgCPv^Vmm2z=;kb+qGF!;M$xQBQyQs2O9#0%=%>@sPjc9gOeC{f2Ze zd2vmDZ?BJ?i<6|$Pb*o#(dESh6Oxls$&#ZgWzXKj?b)$T0IrmsK99tsZMZkQ!+4OC zB;SGQE`vXInun|uzLd_=4nsm@SF{i#aioZ%&e9G;5)Hj``!E^aq|qOxk~qr=uaC69 zI~0ArlT2P}ywOfeC1fGi@{vXYQXlfHe?6ce^v@OIBWOa82v*v#@8{9T+^qla!G@rV$1bsh+9{t4W(m*bbykCu}uoO9)#@+A_jO z+Tn{Ig|%6irG$ZGI(+ak`5<%plV{B$FU}Da5ET#=5EZan1^xhr`88>VYhUaD0000< KMNUMnLSTa1!S##) literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActive.imageset/icon_-_filter_-_active3x_20x20.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActive.imageset/icon_-_filter_-_active3x_20x20.png new file mode 100644 index 0000000000000000000000000000000000000000..f7046c92e5af0d0010972095e596906e3c0b3dbf GIT binary patch literal 1164 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc3?z4jzqJQaEa{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$Ysfr0UNfKP}kP=T?Lky+(l zV^cE|M?VwiKofgUQ?EEv?<7;tIAe2b6OUMslzW7+v9T$bG`4m!HZ}rsjcr^l+OFsW zO)xeFkruWl{#iiNKvGa(WNhmOG2GbL1SW`sHnRjez%(G!*uuux!~}#aY>iFLL8{HI zjZI8};wGNaW^s*B1we5VGmsR-WHSq(d&~lJ&0=5*fF@Wug3N&epqZvX5!2{Ks4f#T z6FU#EBv1`lnW-5_y{WlrR1Mftm|mbpTQ`usNWh|MvoX+8pp=aZ$elK>CO)Z1@?a)V z+{_ZB#>@&`3TPwHgSPG zsH#&r%*>e|->3P`T9mKE0%ZQLjeh_C+e|Lzj1MmyPW}CNI_If7bH=BK)^|H=9cTQF zVrqQvvT39Ctbbo!q9*x1dHnm|OwOne*%o~Z;sif=L`CUdwYe5$d(?wbh|MEkc{9*a zj7i?^E-L~!%q9Uj>?NMQuIx`(#khsERr-WVfkN**T^vIsE+;1_$QgK@K5NeQgT&dADq{o>Wj%?1V=q$H$#XIkan+Vb+4+Oo7~V$;HM9R9w!X12|E zncv*Z^!E>5JbClz6rD+gjRM-Cf*VkAHB8 zJs>SDK7YcDDRU;xnl|qsd$D+ZT3DH1UR<4HV&F!GJ6oAlKYN`%rLDDkRrUmy?C007 znwyxJZdW+c&=f4bWgF+Bs;~uRw+^|!bz0GMNVa>r&uUQzQIVH#qIU7b2868HonE=# zB8sES>&sXF2`>EM7dt1bc|YVVX9#z*S6IF*S_9~F)e_f;l9a@fRIB8oR3OD*WME{V zYhbEtWE5g(Xk}<&Wn!#tU|?ln;P&FJ4~mA|{FKbJO57UEw}yTIYGCkm^>bP0l+XkK D6fkCs literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/beacon.imageset/Contents.json b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActiveSelected.imageset/Contents.json similarity index 63% rename from SiliconLabsApp/SupportingFiles/Images.xcassets/beacon.imageset/Contents.json rename to SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActiveSelected.imageset/Contents.json index e8959893..28c98ed6 100644 --- a/SiliconLabsApp/SupportingFiles/Images.xcassets/beacon.imageset/Contents.json +++ b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActiveSelected.imageset/Contents.json @@ -2,17 +2,17 @@ "images" : [ { "idiom" : "universal", - "filename" : "icon - beacon - light.png", + "filename" : "icon - filter - active.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "icon - beacon - light@2x.png", + "filename" : "icon - filter - active@2x.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "icon - beacon - light@3x.png", + "filename" : "icon - filter - active@3x.png", "scale" : "3x" } ], diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActiveSelected.imageset/icon - filter - active.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActiveSelected.imageset/icon - filter - active.png new file mode 100644 index 0000000000000000000000000000000000000000..320d96b274121dba3d84264d1b9e0a9c8f8b7838 GIT binary patch literal 497 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE-VV{wqX6T`Z5GB1G~i_{3uG+$o^ zEg+kNfw4W4fd!-lh^2s-fq`iO6I?`O0W*RPlAI`d={Qhvx~Gd{h=t(OVDF$q4kB+j z1*RY3Ji>c$u6u*<3*H0LEvjjXdMVoT9Qqchf8Z)yo_9pLWvS!mH`Dp2MmQciwW%ib z!dUPvEeay?xWq+GmIV|e0}ETqnlE*WY|QqFDriU z&2!%EzLE2KWk=~F&z!OuhRghOj>iNm6mrNUq<&9qHx6Pm^u7M2xu=Tf&le+8d7;L) zyEZ79=WP_+w`s}DDHjX1mi;U{xTH(1uyMt<1FJ5!{k3~i7BHdxiDjMJP2Yf^LgQuNb588&J%Q*POnniZheJ&_5K*wi*K8<&c6QeYRRIBEL?x@{$NV0 V&v_bCp?(Y$^q#JMF6*2UngGoS#ufko literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActiveSelected.imageset/icon - filter - active@2x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/filterOnActiveSelected.imageset/icon - filter - active@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..174d73b6bb29f88723fc8b83e8dad3ac49c75fd9 GIT binary patch literal 834 zcmV-I1HJr-P)Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91D4+uX z1ONa40RR91C;$Ke09h_XmH+?(xJg7oR9Fe^l|fe9Kny@Nres@=Ko?+^;RH1&Ae^Ad z31Ew}XjmAQh+^B%AEVk~Ji(tny64M6?X z6{sulu`8ff;s=u(WgqK8wChd?%dK900IH&oo-eUF(o+-#ykTXDa0o}uanRcP|{_H{HwN2m`0@pnp zr~R@4*?V)yCzv-p=zW>$q&L4kfL(I6vh95{mR32IRz_0!g?!}iq z8n4`{GT_*WVg|qZEZM&G0eLl7B@a@;KoSMdZA$)CWZQtML$NwCrPMjx{M@&>6$9i5 zd64tQh~4P+d-Asp48}KGZZ|%P3#yjQwKyP0$%53k7WWqkXP_KDV^5+-aI9QU2D-|q z_GCUaYzD|dvLKU5xDQ?)jHKlNoP|{*2Z4icNmrY^IUq-~K}Kf@VH_sJTj>B?S>GLs z%?hVpLVt7I4v=FBBo1`ge>Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91JfH&r z1ONa40RR91JOBUy0H_zsW&i*MGf6~2RA>e5Sx;zOWe}fvyKf6B8e0&l1vjDH-Qpz| zvGxz7lnUyl7cUAnRzU=z9&Af?m$Jnsp$JwloE|5TeSWO zLaS1xNwee3d)Y7V@4mPDcHge}zQDfuX6Boj-^};#y#ORF$v~2UBm+qXk_;pnNHP#p z1~8PL>DtRcaGG`R38mjxCAtoT$Dx#e$14oYi|OJKRhai504tH>z5FA^@oXUh!}p$} z2bM`yztGZt0vlhWBRoyMV%|0zztLXj+hA>tL1We>>pFwJWRq_|vG9?v>8Xo*xCqa3BA(%x?lklWu7TFr3llr* ze&fOe77J%CZiU-$p0oR)pTUUypBTguDCIs4ZKv|%X3%fWaOoa~MrIe3N3OOS+a6%C zB$dA1m}3XStqiv>qxT^NFTshzU+s_O5wBmm4~}hL4jS7&V6oWkN)x`uRXu47wsK_% z^jn8{HjM$$sIim?ZtxVY@>-q+jXrEOb#1mRoz4J@fz!1IK!Z7^MlAN)CyCyLQsH^q zz{y&L`$u^e4cYjv4!#L1`YEUk-*Ab17@Y$ag9d2&y5o(E=qhA}9)e@{-&FBU)n=GJ ztolI&K^0ar`@@0`cMkhGE$6TCy5rrF1Mj3^>eWbK#V5ij=qsOxbsr6|csWt{nRiwO z&1Ys?jV?fO#|LW2Vt(9Mvn!b~ zwqb&n4UX`Tvgt!~53o3pbCC4iyr!3Y{F!WmKVf3~r41bl40K)B1lt^F?#|U_mjRC{ z+K1>aU~y6!`3*AZy-fL68sT?GJTg74Z5~kD&x}7TOgmT+ICpfUA&b5z*?qv`Mk<}o z?BNT;qNi7)H5=a^=sd_&o!Ifkdc_2EFj&JNIumDeBc0v=7R$$ne+50gk12Ph5jKm8 zYI2sUMl`zx$PD60|MB9f!8;sdOZ*@!Ys zFODcqDL2EQpX0v0(n`UDAUMUZm!YaFqvHcieA9L|#JhNHe*#krTa-vZ%TXj}Z_CDO zUM?r4e5IwDL+F@h4rw>LleLd|Qa|ZwlIS~r891QM-=>nRaeeg=ulFpE&2q}>yqU>Y zkJ(PXc8J-*04$LZEbte{CprZpCc?D%rgTs=2s?qaD}0MO8KfFT9v@)WF6|RG!2ush zSJ?3Z?n=1PjU<4h=WwtsBo!X)`b3W|sc`fh4z`8(DlA_r!!#uKojc69kUDV!KFcxv z6Ed!zsl^Alod1?D%m?}H=Zfcp%( zKY;rPx_^M1phrA%6qfg}S-29gZ?FEj8j X>i&RTifp(V00000NkvXXu0mjfOpB&S literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark-1.imageset/icon - beacon - dark.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark-1.imageset/icon - beacon - dark.png deleted file mode 100644 index 36d21b5273a3a665a047133570e0bcb49aebd158..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 524 zcmeAS@N?(olHy`uVBq!ia0vp^{6H+k!3HE-=Cy!0jKx9jP7LeL$-D$|EK(yp(|mmy zw18|52FCVG1{RPKAeI7R1_q`DOmGqY18M9iuqn9+}QW{Cr5!D-!+hRcnY8N@_c{0dr_8unbRn7LIb?Ltpl z`TzggYr}%B7%HFM6gNplc|p@m(<#<-{jPqjm}B)kjQ5%El7k5bCj@KV0kN<1 z`)9&S^v~#fZfM@uqx#N>yRgJV>A6%^kFI4{XKffy@Au@g+JFrkH!DmtQWv}ImCW76 zzmePUk^PMKqN+N~8}Afnd&EaEF!J6$Ia7O?uI=-lV!5L&t>0DpRy^bFmEG4F>!Ny> z`{bM@cUG-Ezi8=^f307C|LwXuf6aqQ{*Pwe-XVO?yyxz5p%~)}Vyix%FW!B3T}9dU zH~&@~sh<|Ryfn66qx_!)Q^3At#@jYmOFd$O^2*o>qQ3ihItxZN{wVT$=(K05PI yu07_5_9h&XJhOZ4rl~n~QLcBgh2|Dr`^R*%Yq|V|mAQeSSn+iAb6Mw<&;$U`6wt;1 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark-1.imageset/icon - beacon - dark@2x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark-1.imageset/icon - beacon - dark@2x.png deleted file mode 100644 index 64f248b21f3166d73cf39ea4fc52998dd49de8d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 887 zcmV--1Bm>IP)=Z@00001b5ch_0Itp) z=>Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR919iRgM z1ONa40RR91Bme*a08xVOoB#j4 zT}m&3bV+4-2cb)6b`W_1fLKA~3cBD00J&FZMxH_Qfkc{fbl#i$K6l=m86kp;6Gjw8 z!(Oj<1J^{j$CQV|p$vkcge>sPmdoXnuscQIvVtrI**$zBm!GtOc#B0$=JWZ3#7xpi z3bIR(Jt81B1^F;`V87pgDlkmG4N?$BVHp0xiZ4w8=U^beT`U$qj$?W%&?X`CV&O@@ z-+x}O*ILDOrEDoG{M|-{Y9Z8jyWK~0I+u7T74Bgdgv^g(9$A#+sz?FE0Qht=SPY2o zWb&%L7jKw{>e6StY zw1-ff`@ualRcJ|`S@0U>cCyl_M$AfXhMOolQZ&&9Qjs>$u7Qw;#6=>mh0CS3;ZP3i ziCrn#UgrV9qON%@>^WX7SfKaX@Ops#qyMPcnH*#U7>zh^;}20k6ULXm26kyNO>eZArCC=Bz5Ef_<7$b~Gkx-nvqPT~v!I zSJ=x)C9NwZh-y=a7wT;dkyA^jeGP6ln=QWH-ePZK&Fdd0sPZem`hOmWYweZBHri}z ztIV&pSAu9s0{ws5{33G5l$tb>;-#_0-HkQRGbV^lkfXGP7)j}jApR4(wFTO6#vnAe z*@DZBf2l%%D2aE0Y4iG-zdVKRaI!PHUh{5YU(Nhq9@e$JQC8ZOH^TA+n1y#G` zw23hlpP_Q@lqZog1BhpqR-Z-4TqlzX;sO<~uy-HWZ^-Xx=^bt7jK4GFKs$!lezX7p N002ovPDHLkV1lzte~AD9 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark-1.imageset/icon - beacon - dark@3x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark-1.imageset/icon - beacon - dark@3x.png deleted file mode 100644 index a61f8969bc7a82fd030acb7b3a4f6c1f80f59db6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1318 zcmV+>1=;$EP)Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91E1&}a z1ONa40RR91HUIzs0M%qR@c;k>oJmAMRA>dwnK5?TMi7Qg$VED{jMSJuff1=mXWqa_ zm(J7)ES*5i38FHcq!WZGQ<*ALNDu1L8LXs|?BYU_zaPfp0q_FY1sIb19yz>jA$}5Ck9b ziy$)c^~Zkp`1p7U>A!q^oyle-&mcJQ4Pvv;vVMAVb2GEe+jLH34-XH|a6W;kmU`W7 zPNt8^bkYXgNS164smVV98MguTnJ3Fj5^>y?<-NKf5Y^@p(Vy#V)}@E&4`BVezrVk@ zzrT;`aIIuTluh&rkWroE|Dh*d)I|6@vhYloe$zzF#WxWSjFFMq(uZce4GG~3t!_1T zi7dN*iov*Qw2d)ksQprEc^PmaBV7?675mm3JciWofDJb{Wb(|i{8Jg2iCvKJIny38 zT++-ctEBzHD0|2iXXZcnFZiqo_G1sM)D?^`Cc7`Cf#^w3&@1jj=iG(jB95{Gc7#r= z_PRLd(bsdxRQBl|jE(gfu#pr#jVvyBnu@9iD)Sqe~w@5oR^6eSBn#&pXM0td7<3Y*#KBwPA9RQ=7g? zraB zZLmuk=B!ift`2;FlUuW49bixDBg{!ZKa)F;*?JIUOjR*Q7XhQJU>UU^*NpV_tqky9x z6F-7K?SP%Sfd7;54cT+R^xW8|#VpqhoJ8j+atzprPei}-=>g{5?d@#{&WrTn8}M_U zH~W%RJ*J>!QrPQimj)YE}E7|Pa72%DxY*WQ`L9JzAj5k>h z5WQxDk4AQVtV9HgWc9_fJgRI@w58I}(tn&UuacN%c&%VKeP87w`SH5>H1R5_*W$vy~~cs>QwF6 znXC|0zY0=|<~RX{-s_IFCmdZ;GcU3NSCF2BweuriqGH>4&emNuzpF<4vWS^~RcBM9 zU$OMcsC>PfS7r@5u@VJk>~l-*MO3jnWmb-A`gu);=XBdL+?%N4b;_(9)wG;sIBcom zwGdUjUYQkuTJ~;W&ad0I-!%F6+K39#>86l}uIW5HJp9Lv;}89PiOCyCOa4vPBa;7b cjhnanE7PYHLXh+Q=Kufz07*qoM6N<$f+2%-P5=M^ diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark.imageset/Contents.json b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark.imageset/Contents.json deleted file mode 100644 index 8ae95f88..00000000 --- a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "icon - beacon - dark.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "icon - beacon - dark@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "icon - beacon - dark@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark.imageset/icon - beacon - dark.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark.imageset/icon - beacon - dark.png deleted file mode 100644 index 36d21b5273a3a665a047133570e0bcb49aebd158..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 524 zcmeAS@N?(olHy`uVBq!ia0vp^{6H+k!3HE-=Cy!0jKx9jP7LeL$-D$|EK(yp(|mmy zw18|52FCVG1{RPKAeI7R1_q`DOmGqY18M9iuqn9+}QW{Cr5!D-!+hRcnY8N@_c{0dr_8unbRn7LIb?Ltpl z`TzggYr}%B7%HFM6gNplc|p@m(<#<-{jPqjm}B)kjQ5%El7k5bCj@KV0kN<1 z`)9&S^v~#fZfM@uqx#N>yRgJV>A6%^kFI4{XKffy@Au@g+JFrkH!DmtQWv}ImCW76 zzmePUk^PMKqN+N~8}Afnd&EaEF!J6$Ia7O?uI=-lV!5L&t>0DpRy^bFmEG4F>!Ny> z`{bM@cUG-Ezi8=^f307C|LwXuf6aqQ{*Pwe-XVO?yyxz5p%~)}Vyix%FW!B3T}9dU zH~&@~sh<|Ryfn66qx_!)Q^3At#@jYmOFd$O^2*o>qQ3ihItxZN{wVT$=(K05PI yu07_5_9h&XJhOZ4rl~n~QLcBgh2|Dr`^R*%Yq|V|mAQeSSn+iAb6Mw<&;$U`6wt;1 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark.imageset/icon - beacon - dark@2x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark.imageset/icon - beacon - dark@2x.png deleted file mode 100644 index 64f248b21f3166d73cf39ea4fc52998dd49de8d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 887 zcmV--1Bm>IP)=Z@00001b5ch_0Itp) z=>Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR919iRgM z1ONa40RR91Bme*a08xVOoB#j4 zT}m&3bV+4-2cb)6b`W_1fLKA~3cBD00J&FZMxH_Qfkc{fbl#i$K6l=m86kp;6Gjw8 z!(Oj<1J^{j$CQV|p$vkcge>sPmdoXnuscQIvVtrI**$zBm!GtOc#B0$=JWZ3#7xpi z3bIR(Jt81B1^F;`V87pgDlkmG4N?$BVHp0xiZ4w8=U^beT`U$qj$?W%&?X`CV&O@@ z-+x}O*ILDOrEDoG{M|-{Y9Z8jyWK~0I+u7T74Bgdgv^g(9$A#+sz?FE0Qht=SPY2o zWb&%L7jKw{>e6StY zw1-ff`@ualRcJ|`S@0U>cCyl_M$AfXhMOolQZ&&9Qjs>$u7Qw;#6=>mh0CS3;ZP3i ziCrn#UgrV9qON%@>^WX7SfKaX@Ops#qyMPcnH*#U7>zh^;}20k6ULXm26kyNO>eZArCC=Bz5Ef_<7$b~Gkx-nvqPT~v!I zSJ=x)C9NwZh-y=a7wT;dkyA^jeGP6ln=QWH-ePZK&Fdd0sPZem`hOmWYweZBHri}z ztIV&pSAu9s0{ws5{33G5l$tb>;-#_0-HkQRGbV^lkfXGP7)j}jApR4(wFTO6#vnAe z*@DZBf2l%%D2aE0Y4iG-zdVKRaI!PHUh{5YU(Nhq9@e$JQC8ZOH^TA+n1y#G` zw23hlpP_Q@lqZog1BhpqR-Z-4TqlzX;sO<~uy-HWZ^-Xx=^bt7jK4GFKs$!lezX7p N002ovPDHLkV1lzte~AD9 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark.imageset/icon - beacon - dark@3x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - dark.imageset/icon - beacon - dark@3x.png deleted file mode 100644 index a61f8969bc7a82fd030acb7b3a4f6c1f80f59db6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1318 zcmV+>1=;$EP)Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91E1&}a z1ONa40RR91HUIzs0M%qR@c;k>oJmAMRA>dwnK5?TMi7Qg$VED{jMSJuff1=mXWqa_ zm(J7)ES*5i38FHcq!WZGQ<*ALNDu1L8LXs|?BYU_zaPfp0q_FY1sIb19yz>jA$}5Ck9b ziy$)c^~Zkp`1p7U>A!q^oyle-&mcJQ4Pvv;vVMAVb2GEe+jLH34-XH|a6W;kmU`W7 zPNt8^bkYXgNS164smVV98MguTnJ3Fj5^>y?<-NKf5Y^@p(Vy#V)}@E&4`BVezrVk@ zzrT;`aIIuTluh&rkWroE|Dh*d)I|6@vhYloe$zzF#WxWSjFFMq(uZce4GG~3t!_1T zi7dN*iov*Qw2d)ksQprEc^PmaBV7?675mm3JciWofDJb{Wb(|i{8Jg2iCvKJIny38 zT++-ctEBzHD0|2iXXZcnFZiqo_G1sM)D?^`Cc7`Cf#^w3&@1jj=iG(jB95{Gc7#r= z_PRLd(bsdxRQBl|jE(gfu#pr#jVvyBnu@9iD)Sqe~w@5oR^6eSBn#&pXM0td7<3Y*#KBwPA9RQ=7g? zraB zZLmuk=B!ift`2;FlUuW49bixDBg{!ZKa)F;*?JIUOjR*Q7XhQJU>UU^*NpV_tqky9x z6F-7K?SP%Sfd7;54cT+R^xW8|#VpqhoJ8j+atzprPei}-=>g{5?d@#{&WrTn8}M_U zH~W%RJ*J>!QrPQimj)YE}E7|Pa72%DxY*WQ`L9JzAj5k>h z5WQxDk4AQVtV9HgWc9_fJgRI@w58I}(tn&UuacN%c&%VKeP87w`SH5>H1R5_*W$vy~~cs>QwF6 znXC|0zY0=|<~RX{-s_IFCmdZ;GcU3NSCF2BweuriqGH>4&emNuzpF<4vWS^~RcBM9 zU$OMcsC>PfS7r@5u@VJk>~l-*MO3jnWmb-A`gu);=XBdL+?%N4b;_(9)wG;sIBcom zwGdUjUYQkuTJ~;W&ad0I-!%F6+K39#>86l}uIW5HJp9Lv;}89PiOCyCOa4vPBa;7b cjhnanE7PYHLXh+Q=Kufz07*qoM6N<$f+2%-P5=M^ diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - light.imageset/Contents.json b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - light.imageset/Contents.json deleted file mode 100644 index e8959893..00000000 --- a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - light.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "icon - beacon - light.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "icon - beacon - light@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "icon - beacon - light@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - light.imageset/icon - beacon - light.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - light.imageset/icon - beacon - light.png deleted file mode 100644 index 15a425a3eb837f5186d96d454f5336afa0c541db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 480 zcmeAS@N?(olHy`uVBq!ia0vp^{6H+k!3HE-=Cy!0jKx9jP7LeL$-D$|EK(yp(|mmy zw18|52FCVG1{RPKAeI7R1_q`DOmGqY1vjNZ1 z#>fBDT1uKyn$wz@xTpABc+DoM=ySn`p@ex8MUbITa`9nU763g2JRyVYZ}K?}R;f5R;A z==38o`ZkkecDTNJ?|4OUoj{tm)74l;rvA#-_LHTDK_Ttw>gTe~DWM4f DCc?PR diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - light.imageset/icon - beacon - light@2x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - light.imageset/icon - beacon - light@2x.png deleted file mode 100644 index 5b27396c5a0320070649068d42dacfadd1f977f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 807 zcmV+?1K9kDP)=Z@00001b5ch_0Itp) z=>Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR919iRgM z1ONa40RR91Bme*a08xVOoB#jJNR9FeURqb)&Fc8H|{wF2)I-rBoRS=s7&U8Qt zp@KLS9BBaPAf|#q1#vnc6>#rqw2lz^FoTI^G}?W;de6JE>}=Fytk>%tX^)tNNmpiV zv2yN?$Kz%?oo-<#M;6O~m_qy+a}vw5Vh7kyWm&#h*qP}Sh1df!*O=;TM?auN8nnxu zFa1muf~@Kdlfu`I8HATE$%*K|f}(XM+=-f&ejsy*3Pn)XKd877XDkL*p5ZM7DS?{w zdqR7pMFA+-oVOY*W-jEqy%2o|FcC1e;y!M(ENhqpn|l=ed-^@bFSNP6gJ6#2q#y+L zATRI%Xl-)Swi?)98xPn8@S5$V5gkTQ@hROzUu$A#s%aRHD+q+t=HrV`_+Hp;s zt;HuaCkNX*f?&<{mIE43!mlf(gK8TD^k+(fL#=j|nFFWB{o18Aki73AoNE-5`T7XloSEqf_UO|Uwv#+hfe^RM{sJ6JK z&D={4;d587C|YvBCgmpg^(n}s3gvr6nQYEt7TOrWXV}|B;@|hwmxRqr;Fk!$m_0ta z4hvQ)8HQvbeq?<`?mQCjLf*Hxi&_T$FiW_hpELz4D?MuV7A1@MZ~k)_FB0V5WP5D^ l)9Mme;?rEI#U&LA*MAP@=@E{J4AlSt002ovPDHLkV1nIFT?_yK diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - light.imageset/icon - beacon - light@3x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - beacon - light.imageset/icon - beacon - light@3x.png deleted file mode 100644 index 482c419fbe6be79ad8fb1630c593fad3ddd6245e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1217 zcmV;y1U~zTP)Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91E1&}a z1ONa40RR91HUIzs0M%qR@c;k>H%UZ6RA>dwSnYA6Fc3X^?tkbYbZ}e+p@Z0IAf$qG zDhOA>xq~czdymsHVDy0i2Wv(nX+PdRtro#B`!=9$x7!HrSLiW% zphgT;D==fpK3xgEexM4Bff=O$%iY$nR(D1T9mlmqzReUP4kfi8SJWv%G&Ex@uD^4X@mDO8~ zv6l$1(GT=D)!)6G&BiNHeOHr8APUd%3HEEe?{{@Sm6Zj^r}&7utm{1HEX|bm<<$ky zznF&<_%x8%gTw;&VuR!{h9bR&yzl8D#4?)$C;~`$9Kf|!L?ahb;=tyBo?yxo=+hQ- zV$H^!_%@Z%gO48xvz~o?l-b>@Xm=#WKy$T~1Psf$il zO{21AnwM|j~d>7tIi7#-QJUA{NCt zjNtSE9vk6OlI5bl15r*rk`oen5FQm-2shZtUKD3CvOKF+k);GXNh{6tliw`QI*kU| zbIGo%)kaoHxt?-{%#GB{j|(N~VhYIUz3$8BYn1|YgpM{#U_ z^0n<`wPab?Bz$sa?ErkJc5U&0iM9;H_$$i+C;5igYAmsJqc*c*$Rh#B$?sm4obC99 zkCiN+NPsNF_*5f_vh-Z|RVhA*Fd{($x( z$~|Kt)k)o^6Zx)3PBh-?w)`K*!h1Mi#ipp66KAqq zJUA=Wb|qKt>JLYKnmn#IMort!n>0DwV(L8o`@R#UdA2>sW=-k;SnRx4d*L^p#fFbg zb_)DZDEKeGpv1{Hc3SFthh)P8bP0l+XkKVv?rF diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - star - off-1.imageset/icon - star - off@2x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - star - off-1.imageset/icon - star - off@2x.png deleted file mode 100644 index b04999df215b8b20224adce6174e7b25e67a9a7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 732 zcmeAS@N?(olHy`uVBq!ia0vp^DnP8n!3HGFBdc416k~CayA#8@b22Z19E;Ql&op0O z1}z|)gMqOKcJNrF8YjE=I+qd7# z%vGlI3LQ_|tjY25OuqeGzvG<`*$!V}IR5zKGzrW7LOZlf%n4ny^SoA0SS@sEo6~ge=-`H$sWaM_ zmiGHO%qxo&m0$BUaQ)ndjwe~uMI}pfZ=ZDFn7ZOv$OE?cw+pvlcx9S=nOVEIbZh)$ z#+^qe^3$YzO8x=~DDAC|n%WNZsx^3M3;WOjkWQae7+`*PbzyHC&Cu#WL& z=z(`T_iy{Wf1g*n#vz#*Yp-T8bPBFG74rIw)!(C{z2XU434C9F*f*pIu9(9cZ5DHF zZvDr9d=sK|=W(UTGEVk2FPm4Tda^ut>R#<>;vQy(^i zC}8S_t>*KM*j2o=zg-TTr?l>8(94Nw`L~lA>mEOxrp4|qa3Up)MY&aW-66fg^j9m+ zC>`mVv5i%`B_JeC(BYAg&pB?9%X*dD6;#yQtOLLB)_C9FwWhK$fZ=qK}J`_lY}Ple1lPPi3ydx-!PkS*tc(b7{2V z(8%UyzE^Pg8gGZjtz%bn%qENNH4T}Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91HlPCl z1ONa40RR91GXMYp02ph7?f?J-lSxEDRA>d=n2&YZFc60W3zSY!HYlAS&IAD#z?q;h zLCFLm8-xV{-GFRBHh>B0Opt!}s>^d6|Iw+IU%>Ay_=|>FgMlRsyh_rO<8BMUx_hk0^3jQ|M`e0hHE2$x!8PHq7%H zj<#bSg%4uNF$$Q640Y9`$uSBTRi|2cgN-J~SYW;dN0VdgUtIxyXvLn^ zcXvI*{d0=}HZxUlaUpU{jB^^@TLq)QAsOYtY&}|+V{2dzYGWmG{-zT?g>YD?VCC<4&8qVEUb$KqJc zwP{hk!F*-tij^O)%bFr(_vAFLmv5U-`4|Kt)k)o^6Zx)3PBh-?w)`K*!h1Mi#ipp66KAqq zJUA=Wb|qKt>JLYKnmn#IMort!n>0DwV(L8o`@R#UdA2>sW=-k;SnRx4d*L^p#fFbg zb_)DZDEKeGpv1{Hc3SFthh)P8bP0l+XkKVv?rF diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - star - off.imageset/icon - star - off@2x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - star - off.imageset/icon - star - off@2x.png deleted file mode 100644 index b04999df215b8b20224adce6174e7b25e67a9a7f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 732 zcmeAS@N?(olHy`uVBq!ia0vp^DnP8n!3HGFBdc416k~CayA#8@b22Z19E;Ql&op0O z1}z|)gMqOKcJNrF8YjE=I+qd7# z%vGlI3LQ_|tjY25OuqeGzvG<`*$!V}IR5zKGzrW7LOZlf%n4ny^SoA0SS@sEo6~ge=-`H$sWaM_ zmiGHO%qxo&m0$BUaQ)ndjwe~uMI}pfZ=ZDFn7ZOv$OE?cw+pvlcx9S=nOVEIbZh)$ z#+^qe^3$YzO8x=~DDAC|n%WNZsx^3M3;WOjkWQae7+`*PbzyHC&Cu#WL& z=z(`T_iy{Wf1g*n#vz#*Yp-T8bPBFG74rIw)!(C{z2XU434C9F*f*pIu9(9cZ5DHF zZvDr9d=sK|=W(UTGEVk2FPm4Tda^ut>R#<>;vQy(^i zC}8S_t>*KM*j2o=zg-TTr?l>8(94Nw`L~lA>mEOxrp4|qa3Up)MY&aW-66fg^j9m+ zC>`mVv5i%`B_JeC(BYAg&pB?9%X*dD6;#yQtOLLB)_C9FwWhK$fZ=qK}J`_lY}Ple1lPPi3ydx-!PkS*tc(b7{2V z(8%UyzE^Pg8gGZjtz%bn%qENNH4T}Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91HlPCl z1ONa40RR91GXMYp02ph7?f?J-lSxEDRA>d=n2&YZFc60W3zSY!HYlAS&IAD#z?q;h zLCFLm8-xV{-GFRBHh>B0Opt!}s>^d6|Iw+IU%>Ay_=|>FgMlRsyh_rO<8BMUx_hk0^3jQ|M`e0hHE2$x!8PHq7%H zj<#bSg%4uNF$$Q640Y9`$uSBTRi|2cgN-J~SYW;dN0VdgUtIxyXvLn^ zcXvI*{d0=}HZxUlaUpU{jB^^@TLq)QAsOYtY&}|+V{2dzYGWmG{-zT?g>YD?VCC<4&8qVEUb$KqJc zwP{hk!F*-tij^O)%bFr(_vAFLmv5U-`4|N^5+IfWVg?4L1x#=e(FM$Kwg5=-vGINvpyFyz7sn6_!L5@Y<{on3 zY2A6@|67in02XEuH4PtzxeIzG@Nx;8;qucg=FL+3`jxqg<8u8%)(x%x~} znOS&3=*6`^PePyg+^w)#$NYi0fYm?mM!vP{S_S`3<4@)j(-^q@ZaU0xibynHba6ro zr){aD&rOyE0!9lyo#{`%w@ZWji{mbX*gJC{ZgXC9J6@izy*Ke;o!cr&;SWN}+`>z{ z=e&Csa&OjeU-JObhSs<5HckzBDDAMM@Q${>*?UXZxhKsmm5**r){Tgs*O3;pXnV`Y z7vF+Cz8={$?eqSg%`?mRKZV)`U7c{PIBiF7sQj~|xA_0srhUs}IFeP}WoxLjP2F%q sbFOvSwTzopr032$~2><{9 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark-1.imageset/icon - wifi - dark@2x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark-1.imageset/icon - wifi - dark@2x.png deleted file mode 100644 index e34ca3bacb7b24e3131f583f34c4e67f3b50b087..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 935 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 sbFOvSwTzopr032$~2><{9 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark.imageset/icon - wifi - dark@2x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark.imageset/icon - wifi - dark@2x.png deleted file mode 100644 index e34ca3bacb7b24e3131f583f34c4e67f3b50b087..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 935 zcmV;Y16cftP)Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91Dxd=Z z1ONa40RR919{>OV0G<=D#{d8W9Z5t%R9Fe^m_c&dKoCVO$UbaggK~l>Z~*HD$R`Ls z0nQ16PY`?pbAyl*1a{c~$|}p?6+r#1DI*#K<5U2T!h<$~970uy(zYmwogj&P1(iMtXFkPpjjelBZBV}tJ#*k)V(Li#_4oAeFC53z-iYccwv1AxrqYPd~DYs z?$h4-_=SIKCx3rU_}R5y=XH81!6-IPe`m&Xj35W z=vs(qbQtKga*HO4`$KpPVVy#&_`2!Zil|-(3aW*9Ca@wl$V$}_wfSfhmrE3m>UlqH zz%KNe(E9H3Vzujy1Crm24&{x3#K?nrm*;@}gTY5#Voc5In-3-Ty5>Y6_8m60M+zG; z8ipK4Q|_zh%N+@*xvG>EB1=PT;H0j#=yR5y25V zb_8(Y79-q9Wxa(qSm!<|+y8M*1#)ZDmJF{{$7h?(<5C*><+-IiLfiN}ztTh@sKekZ ziGpM@$gWr$+cx!NeEhy1zq#bP)oT4gNiQvy^#&im*=%mt>-DcRAKSIuhHnCrnj&NJ ziEOTgkI|(RQPm)?cV^6hk(wRn`Q+J45fwNj0?7W(N`y^6&Ps2p(wj*HsQ~?>GNlY& z9g`wiPx#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91KcE8u z1ONa40RR91E&u=k0O74v00004t4TybRA>e5nmtzAKoG|*lOi2XNrlUFvEcxyOc!$k zOqVXs2{0#!a{|l>FkMP<0I)bgOvOuQ9{|Yj@3-<3LPop#u+4kSd$Lx$Gqdxbot^yv zuT~MYpslT~FHcWTO}c1$o~O6SpZ~u62~5=#nGpWz%<-|Og*`1~H5l8jI^z7;Gey_&_4V~As-J`3+uPH! zrKFaZG_pf<1aakgm8Hp)~V`xKk|0zXE9Ld%*2nvm|htf|(@9T-hC42XmS2eBIVPXYf);Gb*-5xN6( zcio_q_L}_AcX0v{g|BZCX-dnR5Qc$0B;Cg?uvp}Y@WUJP-P1ETVeZW z`v1<6^&dypFi$_uBX7c7TjEWk!#pdKHaSkj{O&smtbmz3pq%4OA6zBqNcZ!uV&b1) zXz&j7ktB4ssPlQYlWkew$TqWh(%PNDqvVdKk)|wJhoi(hAUg*nCaZbLw9?hMkDT`D zUwBtWdB6u)lV#K;nRozR@j4MQ69k5OOn&K6)~4w-%C+`8$FEu9LPljbX^lpjQ7-++ zOP{*ZT6Sce;pmoeTGFIfzn#HLn;c-y0?~+QKI?j@|7vdq8+F}=3OONiydtcZ2Y5kK z-o@WN@a};(=z&}bFAq*FTV!k%E1dXE5jKE3Unsn zgwl?zS)El19^5+i{$;cqS%$>haln))Xji?2IUj7|Mp~3xuE3>JGB~Ycp}!DI)!>U5 zH!UJ;BU>qW!d8jw*2a(okFJ+TiMrB1D+MoWwb$o0Q`wTVSJ&SPDj)eu6J7+?Wc+~d zth0IqJd9+ke6Iw&09ZI+rNCv=%D`thVCBGNPvvu7okYYu*8j%%@gzw3&dl+ZgnDF) r-YXsXc*jfw-+ap_l_$4&-%N^5+IfWVg?4L1x#=e(FM$Kwg5=-vGINvpyE_d7sn6_!M&5;2R1th z9JM|EKFY~z0-uwX!G(s1M$JY(6;Tf@g)oJe4AwhJ1hfy_4}RD7xZ{`W{WItORe#^K z+vm(to>|L0HS{-%L?@h*jbgg}y*9hYG9@xC_HgB6rphB+ca+V<6#t4!MFf-5w(Ot88y!E|p)OLhh8p4Wl@UG_}M zc;7YQox@kJ80i?B8#QX`9;JYp!Fu=zsXJvdn8vmIASYtST#i zu^J6`kRG!!@@apz+5UqeXR;U1JE7Ih&*D`t`YyL$vU0`UM9ynT zyI)>kE~XG=|DsNH{o+~I;wCO%93>LxHg(5>+f{#_wb`%JH}wm6{N>}9`(c0n|6u3| V?kRFTlDioc@Sd)IF6*2UngEVG!D;{i diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - light.imageset/icon - wifi - light@2x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - light.imageset/icon - wifi - light@2x.png deleted file mode 100644 index a5f3ff2e0c9ff68a19434a66745c49555c9b171f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 888 zcmV-;1Bd*HP)Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91Dxd=Z z1ONa40RR919{>OV0G<=D#{d8V?ny*JR9Fe^m+x`fKoG@|C*MN{zEluW0ZIiCG+^|9`W7P6PY}*r$!m$85S$V$vbT_oBJ(D~uZTiY9h{-JB1ow-QHQ@lxHz>Q ziE4x3SfPVcKRJugv6#w7X`lB^|W4cU<|_Ra^u|04j?1(MiiICH#ndU3MzKCts@ zVfb`Pq&PD)udX>uAO@F!YdRwd^N2P!sW^jb$4~Xr zMLH3+-RuVtFaa1Rq!UrCLDuEQS>c~E4Zt`doro&4&Oyljc2v=yv)thia&_KJs=^ZJ zLpFnS^_mpPG>C}@)VZU|51A5@@u9Pfy1}kS((?`N+;lfqhucLrNBso_Kr|Px#IAvH#W=%~1DgXcg2mk?xX#fNO00031000^Q000000-yo_1ONa40RR91KcE8u z1ONa40RR91E&u=k0O74v00004Wl2OqRA>e5nd?#8Koo@$lm9pspbjtba&Hx(WFXyIQ%a4kYP>h6PJT~RH z7I*+(8NdS)>NS;A1wS!`h>87padDAV(6_2MgJ(L2c=`zs;VuCa?i~qu4F9GG={xC; zj>_Nx9pUQ+R=baskkl61<%SYw!T?qcDR**qaQ{1ON({xNJb1LCGiLT{-EpG{E1{rrhH>!$!*> z&TUuV2{fcbBkn~4Omb6<1$OP+%h?tqGdM$2h&nT4LXOu86X0|}s}Xl55_uA6oK2mL zYZxeJVh@RZ?&(IS!C2s*anN^hVnB~^khs`2t*hBUE)$Ay=IA>k_wNF~tZGQ01%ZYi z4Z4^w7$R~TI~L(I;8>euRAmjVrb;pJKD|?fktxE?&5;2N4gp@eLE-ELfTt*r_X(&i z7jo1R(2@CMljnW8T$h|NFN<7IXzy_r;WRDu@9itOW_RG{4{o z?tU?Cnr{=hB}?WlTbqJ(xgcI5xoh3 zrRzkj-8lwRvDsfnJnR0jl@xEQuteaq;q?fn>3dxCOaFrY#y@!}f@Z=P-|v$_cTC)7H41Nsg$0$qjXC^hoQx+_?#;LVZvN)o+Yi4^1+ z@25Uzcfn8TtMi9Vskiq+jkYUrd6CUP4{cK0;Opd`)iJJO+bVdbZNe=~zxz9pZj2bj zPDUQet%6rRy6YP-mC}m%RlhL@w&jt?mPW?uTOz5zVVZQE=+0WSc%3eYuh z<S>v5d^*k4~osRrwV3t8--qS8Nx6Ro3 Z^Iv`$+XsGx&WZp4002ovPDHLkV1hmxKDz(_ diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark.imageset/Contents.json b/SiliconLabsApp/SupportingFiles/Images.xcassets/loading pulse.imageset/Contents.json similarity index 65% rename from SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark.imageset/Contents.json rename to SiliconLabsApp/SupportingFiles/Images.xcassets/loading pulse.imageset/Contents.json index b36b848c..45a838f1 100644 --- a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - dark.imageset/Contents.json +++ b/SiliconLabsApp/SupportingFiles/Images.xcassets/loading pulse.imageset/Contents.json @@ -2,17 +2,17 @@ "images" : [ { "idiom" : "universal", - "filename" : "icon - wifi - dark.png", + "filename" : "loading pulse.png", "scale" : "1x" }, { "idiom" : "universal", - "filename" : "icon - wifi - dark@2x.png", + "filename" : "loading pulse@2x.png", "scale" : "2x" }, { "idiom" : "universal", - "filename" : "icon - wifi - dark@3x.png", + "filename" : "loading pulse@3x.png", "scale" : "3x" } ], diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/loading pulse.imageset/loading pulse.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/loading pulse.imageset/loading pulse.png new file mode 100644 index 0000000000000000000000000000000000000000..caca380b8533a48a726861f3e27b49d5ba63ef48 GIT binary patch literal 13108 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 literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/loading pulse.imageset/loading pulse@3x.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/loading pulse.imageset/loading pulse@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..14b5ab80d5463b5b77371d752b35adcd09c75758 GIT binary patch literal 54013 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 delta 1162 zcmV;51a61<%SYw!T?qcDR**qaQ{1ON({xNJb1LCGiLT{-EpG{E1{rrhH>!$!*>&TUuV z2{fcbBkn~4Omb6<1$OP+%h?tqGdM$2h&nT4LXOu86Mx`zK&ugVCK7oPXq-)*jcXVv zXJQYDeeUT-r@>g@pK;K4abiG^agey!HLa`JKrR!CaOUVcBlqtDzpQFVpap@39}T*g zFBl?n8#@-^G~ig9V^n1gt)@yb@IJj$gpn!2&drel3=RQaxCsFO ztj<%~7ZkLi#gf;djqm(EeiSD(OtYprPhwF*eBBdX1ms_r-(RyAjPP|X(DE(d_?^q+ z3cMbgB2&mK{0-o!rr#9%7{1RFuIcZMd1K&JWq&*i^bl@?Vn%PeH^1ZvA)xy-zu*Y& zg60I#H8rUl%zNa`^u8g1QQagw?X?_Kgv`_4Z_NXIKtPRl=o`2pI|09@08<}-&LeN8 zxvs?fMBsNnnRL!O5u47~$)Fo$%|d7T=4!z?dFE;(eRIyH*e5o7U?Cnr{=hB}?WlTbqJ(xgcI5xoh3 zrRzkj-8lwRvDsfnJnR0jl@xEQuteaq;q?fn>3dxCOaFrY#y@!}f@Z=P-|v8taHO{ur{LXEa7aCwobeuU1700000NkvXXt^-0~g2bIPga7~l diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - light.imageset/Contents.json b/SiliconLabsApp/SupportingFiles/Images.xcassets/rssiWhite.imageset/Contents.json similarity index 100% rename from SiliconLabsApp/SupportingFiles/Images.xcassets/icon - wifi - light.imageset/Contents.json rename to SiliconLabsApp/SupportingFiles/Images.xcassets/rssiWhite.imageset/Contents.json diff --git a/SiliconLabsApp/SupportingFiles/Images.xcassets/rssiWhite.imageset/icon - wifi - light.png b/SiliconLabsApp/SupportingFiles/Images.xcassets/rssiWhite.imageset/icon - wifi - light.png new file mode 100644 index 0000000000000000000000000000000000000000..d1d13efdc999eb274a81076b6f625d2e63a8bd96 GIT binary patch literal 210 zcmeAS@N?(olHy`uVBq!ia0vp^qChOb!3HFM-ZD`IQjEnx?oJHr&dIz4a(X;n978Mw zw@wV?Yf#{EK3XBrzx{W-!wk-pDQclp{&GbBzL9pzBSzI@*0LZ@osv%T4U8fW%)fB= z9GuLw>En`iP6-a{Rpqaat1xYRw?Tc@uZ-WbZoV`SJPx$N=ZaPR9Fe^mQfCZFbsylcmTbD-au~z4-mNj0hDSmV|HEtbsI6UFSxbq|E+Ar zrb)1Anl6t-OOV5sByx$%Xu}vqAiC?ilcY34v-MkT|5$O>B3FaB8duxFyh#K)YDTS7JWI zGSBn1;d&+l5ql%#N6z&y3^zX~!n29>6Cq;+dLxPq5TIBrol8Vpbp}ZkNs7d3??Du_ zB;>jw4eVhJ-t(y`#xWTUOwIvf35k z{#evz5cRlkbBQw;ZTKSP;v}e84Ed=8XyQUKruIbD3?fJtecg ub}2XH<30-Og@O{*20)+jEP)Px%HAzH4RA>e5SkZCAAPluTLAyb_K|4XaK|6wTm*kTD2Wb7;DCg?nK=6cdF8p8| zA-(qyhKdRPx4`pDo*Af=tR}C=9vDZ4Q!m0=02#9||J(E8 zJhumFv|M|Z-CxvkJRXm4RX(AjwN(Xln7rTbKdc-Mhr_EW?bldki&hXI*B)H5(jMv6 zJ8_T(#@%lBlqwLO+L_&wWMM}{#z^8N}nCcxF8;z5F0`guRTGei%a1*t1<1=V4Q z327m(ZCwFZvAs+VsjNxVj^`Mk46HOCC>gb+tn(o>jX;SbkMy>Rz&IDo+~?BwWd-VG zKWE-#CQv$IjWkaQhs7x2W4@OINJ${?IVG2{tQ(#;@U{FmAurcstWK((&*zu5#7M?X z?LYaQQI0IJrIi&Ro?olqAiyAA5nrvv?qJ)|M*jN+*i@ua05@^orL7mt1UC`SyNc^aBB# V+Kl7930wdG002ovPDHLkV1j}zB+>u? literal 0 HcmV?d00001 diff --git a/SiliconLabsApp/SupportingFiles/Localizable.strings b/SiliconLabsApp/SupportingFiles/Localizable.strings index 2317068c..38d719e0 100644 --- a/SiliconLabsApp/SupportingFiles/Localizable.strings +++ b/SiliconLabsApp/SupportingFiles/Localizable.strings @@ -6,7 +6,5 @@ Copyright © 2020 SiliconLabs. All rights reserved. */ -"app_info" = "For more information visit:\nsilabs.com/products/wireless\n\nSupport:\nsilabs.com/support\n\nSource code:\ngithub.com/SiliconLabs/EFRConnect-ios\n\nAdditional documentation:\ndocs.silabs.com/bluetooth/latest\n\nAdditional Apps released by Silicon Labs:\napps.apple.com/us/developer/silicon-labs/id555074469"; -"device_is_not_responding" = "Device is not responding."; -"error" = "Error"; -"dismiss" = "Dismiss"; +"app_info" = "For more information visit:\nsilabs.com/products/wireless\n\nSupport:\nsilabs.com/support\n\nSource code:\ngithub.com/SiliconLabs/EFRConnect-ios\n\nEFR Connect User's Guide:\ndocs.silabs.com/bluetooth/latest/miscellaneous/mobile/efr-connect-mobile-app\n\nAdditional documentation:\ndocs.silabs.com/bluetooth/latest\n\nAdditional Apps released by Silicon Labs:\napps.apple.com/us/developer/silicon-labs/id555074469"; +"device_is_not_responding" = "Device is not responding"; diff --git a/SiliconLabsApp/ViewControllers/AppSelection/Info/SILAppSelectionInfoViewController.m b/SiliconLabsApp/ViewControllers/AppSelection/Info/SILAppSelectionInfoViewController.m index 9674e68e..89342533 100644 --- a/SiliconLabsApp/ViewControllers/AppSelection/Info/SILAppSelectionInfoViewController.m +++ b/SiliconLabsApp/ViewControllers/AppSelection/Info/SILAppSelectionInfoViewController.m @@ -40,7 +40,7 @@ - (CGSize)preferredContentSize { if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { return CGSizeMake(600, 600); } else { - return CGSizeMake(300, 500); + return CGSizeMake(300, 520); } } diff --git a/SiliconLabsApp/ViewControllers/AppSelection/SILAppSelectionViewController.h b/SiliconLabsApp/ViewControllers/AppSelection/SILAppSelectionViewController.h index 91df2661..d6d33c20 100644 --- a/SiliconLabsApp/ViewControllers/AppSelection/SILAppSelectionViewController.h +++ b/SiliconLabsApp/ViewControllers/AppSelection/SILAppSelectionViewController.h @@ -13,6 +13,7 @@ NS_ASSUME_NONNULL_BEGIN @interface SILAppSelectionViewController : UIViewController @property (strong, nonatomic) NSArray *appsArray; +@property BOOL isDisconnectedIntentionally; @end diff --git a/SiliconLabsApp/ViewControllers/AppSelection/SILAppSelectionViewController.m b/SiliconLabsApp/ViewControllers/AppSelection/SILAppSelectionViewController.m index dae12167..42269e03 100644 --- a/SiliconLabsApp/ViewControllers/AppSelection/SILAppSelectionViewController.m +++ b/SiliconLabsApp/ViewControllers/AppSelection/SILAppSelectionViewController.m @@ -49,7 +49,6 @@ @interface SILAppSelectionViewController () + + + + + + + + + + + Roboto-Regular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/Menu/SILDebugServicesMenuViewController.swift b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/Menu/SILDebugServicesMenuViewController.swift new file mode 100644 index 00000000..12915800 --- /dev/null +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/Menu/SILDebugServicesMenuViewController.swift @@ -0,0 +1,60 @@ +// +// SILDebugServicesMenuViewController.swift +// BlueGecko +// +// Created by Kamil Czajka on 19/05/2020. +// Copyright © 2020 SiliconLabs. All rights reserved. +// + +import Foundation + +class SILDebugServicesMenuViewController : UIViewController, UITableViewDataSource, UITableViewDelegate { + @IBOutlet weak var menuOptionTableView: UITableView! + @objc var delegate: SILDebugServicesMenuViewControllerDelegate! + var menuOption = [Int : [String : () -> ()]]() + + let ReuseIdentifier = "DebugServicesMenuCell" + let NibName = "SILDebugServicesMenuTableViewCell" + let ServicesMenuCellHeight: CGFloat = 30.0 + + override func viewDidLoad() { + super.viewDidLoad() + setupMenuOptionTableView() + } + + @objc func addMenuOption(title: String, completion: @escaping () -> ()) { + menuOption[menuOption.count] = [title : completion] + } + + @objc func getMenuOptionHeight() -> CGFloat { + return CGFloat(menuOption.count) * ServicesMenuCellHeight + } + + private func setupMenuOptionTableView() { + self.menuOptionTableView.delegate = self + self.menuOptionTableView.dataSource = self + self.menuOptionTableView.register(UINib(nibName: NibName, bundle: nil), forCellReuseIdentifier: ReuseIdentifier) + self.menuOptionTableView.separatorStyle = .none + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return menuOption.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: ReuseIdentifier, for: indexPath) as! SILDebugServicesMenuTableViewCell + cell.menuOptionLabel.text = menuOption[indexPath.row]?.keys.first + cell.selectionStyle = .none + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + if let completion = menuOption[indexPath.row]?.values.first { + delegate!.performActionForMenuOption(using: completion) + } + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return ServicesMenuCellHeight + } +} diff --git a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/Menu/SILDebugServicesMenuViewControllerDelegate.swift b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/Menu/SILDebugServicesMenuViewControllerDelegate.swift new file mode 100644 index 00000000..bbb18a0b --- /dev/null +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/Menu/SILDebugServicesMenuViewControllerDelegate.swift @@ -0,0 +1,15 @@ +// +// SILDebugServicesMenuViewControllerDelegate.swift +// BlueGecko +// +// Created by Kamil Czajka on 19/05/2020. +// Copyright © 2020 SiliconLabs. All rights reserved. +// + +import Foundation + +@objc +protocol SILDebugServicesMenuViewControllerDelegate { + @objc + func performActionForMenuOption(using completion: () -> ()) +} diff --git a/SiliconLabsApp/Views/SILOTAHUDView.h b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAHUDView.h similarity index 87% rename from SiliconLabsApp/Views/SILOTAHUDView.h rename to SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAHUDView.h index a2eb0cf6..8a73a79e 100644 --- a/SiliconLabsApp/Views/SILOTAHUDView.h +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAHUDView.h @@ -18,8 +18,10 @@ @property (weak, nonatomic) IBOutlet UILabel *fileNameLabel; @property (weak, nonatomic) IBOutlet UILabel *uploadTypeLabel; @property (weak, nonatomic) IBOutlet UILabel *fileTotalBytesLabel; -@property (weak, nonatomic) IBOutlet UILabel *peripheralNameLabel; +@property (weak, nonatomic) IBOutlet UILabel *informationLabel; @property (weak, nonatomic) IBOutlet UILabel *peripheralIdentifierLabel; +@property (weak, nonatomic) IBOutlet UILabel *otaStatusLabel; +@property (weak, nonatomic) IBOutlet UIView *mtuView; @property (weak, nonatomic) IBOutlet UILabel *mtuValueLabel; @property (weak, nonatomic) IBOutlet UILabel *fileCountLabel; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *constrainBottomSeparatorBelowFile; diff --git a/SiliconLabsApp/Views/SILOTAHUDView.m b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAHUDView.m similarity index 100% rename from SiliconLabsApp/Views/SILOTAHUDView.m rename to SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAHUDView.m diff --git a/SiliconLabsApp/Views/SILOTAHUDView.xib b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAHUDView.xib similarity index 95% rename from SiliconLabsApp/Views/SILOTAHUDView.xib rename to SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAHUDView.xib index a3894a21..68f2391c 100644 --- a/SiliconLabsApp/Views/SILOTAHUDView.xib +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAHUDView.xib @@ -1,11 +1,9 @@ - - - - + + - + @@ -20,9 +18,11 @@ + + + - @@ -30,21 +30,21 @@ - - + + - + - + - - + - + - + - + - + - + - + diff --git a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAProgressViewController.m b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAProgressViewController.m index 2a078ccf..520721a3 100644 --- a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAProgressViewController.m +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAProgressViewController.m @@ -63,7 +63,7 @@ - (void)viewDidLoad { self.doneButton.enabled = NO; self.timerLabel = [[MZTimerLabel alloc] initWithLabel:self.timerDisplayLabel]; self.timerLabel.timeFormat = kSILTimerFormat; - _hudView.peripheralNameLabel.text = [_hudPeripheralViewModel peripheralName]; + _hudView.informationLabel.text = @"OTA Device Firmware Update"; _hudView.peripheralIdentifierLabel.text = [_hudPeripheralViewModel peripheralIdentifier]; _hudView.mtuValueLabel.text = [_hudPeripheralViewModel peripheralMaximumWriteValueLength]; } diff --git a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTASetupViewController.m b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTASetupViewController.m index 509f31af..ef4701ea 100644 --- a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTASetupViewController.m +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTASetupViewController.m @@ -62,9 +62,10 @@ - (void)viewDidLoad { [self registerNibs]; [self configureUIForFirmwareUpdateViewModel:self.firmwareUpdateViewModel]; [_hudView stateDependentHidden:YES]; - _hudView.peripheralNameLabel.text = [_hudPeripheralViewModel peripheralName]; + _hudView.informationLabel.text = [_hudPeripheralViewModel peripheralName]; + _hudView.otaStatusLabel.text = @"OTA Device Firmware Update"; _hudView.peripheralIdentifierLabel.text = [_hudPeripheralViewModel peripheralIdentifier]; - _hudView.mtuValueLabel.text = [_hudPeripheralViewModel peripheralMaximumWriteValueLength]; + [_hudView.mtuView setHidden:YES]; } - (void)viewWillDisappear:(BOOL)animated { diff --git a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAUICoordinator.m b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAUICoordinator.m index aaca8d0e..5403fa1d 100644 --- a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAUICoordinator.m +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Details/OTA/SILOTAUICoordinator.m @@ -18,8 +18,6 @@ #import "SILPopoverViewController.h" #import "SILOTAProgressViewController.h" -#define OTA_TTL 30 - static NSString * const kSILDFUStatusRebootingString = @"Rebooting..."; static NSString * const kSILDFUStatusWaitingString = @"Waiting..."; static NSString * const kSILDFUStatusConnectingString = @"Attempting Connection..."; @@ -33,6 +31,8 @@ static NSString * const kSILFirmwareUpdateBGErrInvalidFileFormat = @"Invalid file format"; static NSString * const kSILFirmwareUpdateBGErrUnspecified = @"Unspecified error"; static NSString * const kSILFirmwareUpdateBGErrUnknown = @"Unspecified error"; +static NSString * const kSILDeviceIsNotResponding = @"Device is not responding"; +static NSString * const kSILCouldNotFindDeviceAdvertising = @"Could not find device advertising"; static NSString * const kSILDeviceInOTAModeName = @"OTA"; @interface SILOTAUICoordinator () @property (weak, nonatomic) IBOutlet UIButton *searchButton; @property (weak, nonatomic) IBOutlet UIButton *saveButton; @property (weak, nonatomic) IBOutlet UIButton *resetButton; -@property (weak, nonatomic) IBOutlet UITextView *searchByRawAdvertisementDataTextView; @property (weak, nonatomic) IBOutlet UITextView *searchByDeviceNameTextView; @property (weak, nonatomic) IBOutlet UIImageView *clearTextImageByDeviceName; -@property (weak, nonatomic) IBOutlet UIImageView *clearTextImageByAdvertisementData; @property (weak, nonatomic) IBOutlet UILabel *rssiLabel; @property (weak, nonatomic) IBOutlet UILabel *beaconTypeLabel; @property (weak, nonatomic) IBOutlet UILabel *minRangeLabel; @@ -29,8 +27,6 @@ @interface SILBrowserFilterViewController () @property (weak, nonatomic) IBOutlet UILabel *savedSearchesLabel; @property (weak, nonatomic) IBOutlet UISlider *dBmSlider; @property (weak, nonatomic) IBOutlet UIView *seachByDeviceNameView; -@property (weak, nonatomic) IBOutlet UIView *searchByRawAdvertisementDataView; -@property (weak, nonatomic) IBOutlet UITextView *hexTextView; @property (weak, nonatomic) IBOutlet UILabel *favouriteAreaTitleLabel; @property (weak, nonatomic) IBOutlet UISwitch *favouriteSwitch; @property (weak, nonatomic) IBOutlet UIButton *beaconTypeAreaButton; @@ -46,9 +42,7 @@ @interface SILBrowserFilterViewController () @implementation SILBrowserFilterViewController -NSString* const HexTextView = @"0x"; NSString* const SearchByDeviceNamePlaceholder = @"Search by device name"; -NSString* const SearchByRawAdvertisementDataPlaceholder = @"Search by raw advertising data"; NSString* const DiscardKeybordText = @"\n"; NSInteger const ASCIICodeFor0 = 48; NSInteger const ASCIICodeFor9 = 57; @@ -99,36 +93,12 @@ - (void)addObserverForKeyboardHide { #pragma mark - Appearance for Searching View - (void)setAppearanceForSeachingView { - [self setAppearanceForSearchingByRawAdvertisement]; [self setAppearanceForSearchingByDeviceName]; [self setBackgroundAppearance]; [self setupGesturesForClearImages]; [self hideClearButtons]; } -- (void)setAppearanceForSearchingByRawAdvertisement { - [self setAppearanceForRawAdvertisementDataTextView]; - [self setAppearanceForHexTextView]; -} - -- (void)setAppearanceForRawAdvertisementDataTextView { - _searchByRawAdvertisementDataTextView.textContainer.maximumNumberOfLines = 1; - _searchByRawAdvertisementDataTextView.textContainer.lineBreakMode = NSLineBreakByTruncatingTail; - _searchByRawAdvertisementDataTextView.delegate = self; - [_searchByRawAdvertisementDataTextView setFont:[UIFont robotoMediumWithSize:[UIFont getMiddleFontSize]]]; - _searchByRawAdvertisementDataTextView.textColor = [UIColor sil_subtleTextColor]; - _searchByRawAdvertisementDataTextView.text = SearchByRawAdvertisementDataPlaceholder; - _searchByRawAdvertisementDataTextView.autocorrectionType = UITextAutocorrectionTypeNo; -} - -- (void)setAppearanceForHexTextView { - _hexTextView.textContainer.maximumNumberOfLines = 1; - _hexTextView.textContainer.lineBreakMode = NSLineBreakByTruncatingTail; - [_hexTextView setFont:[UIFont robotoBoldWithSize:[UIFont getMiddleFontSize]]]; - _hexTextView.textColor = [UIColor sil_primaryTextColor]; - _hexTextView.text = HexTextView; -} - - (void)setAppearanceForSearchingByDeviceName { _searchByDeviceNameTextView.textContainer.maximumNumberOfLines = 1; _searchByDeviceNameTextView.textContainer.lineBreakMode = NSLineBreakByTruncatingTail; @@ -142,13 +112,10 @@ - (void)setAppearanceForSearchingByDeviceName { - (void)setBackgroundAppearance { _seachByDeviceNameView.backgroundColor = [UIColor sil_backgroundColor]; _seachByDeviceNameView.layer.cornerRadius = 10.0; - _searchByRawAdvertisementDataView.backgroundColor = [UIColor sil_backgroundColor]; - _searchByRawAdvertisementDataView.layer.cornerRadius = 10.0; } - (void)setupGesturesForClearImages { [self setupGestureForClearImageDeviceName]; - [self setupGestureForClearImageAdvertisementData]; } - (void)setupGestureForClearImageDeviceName { @@ -165,41 +132,18 @@ - (void)performClearActionForDeviceNameView:(UIGestureRecognizer*)gestureRecogni [self hideClearImageForDeviceName]; } -- (void)setupGestureForClearImageAdvertisementData { - UITapGestureRecognizer* tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(performClearActionForAdvertisementDataView:)]; - [_clearTextImageByAdvertisementData addGestureRecognizer:tap]; -} - -- (void)performClearActionForAdvertisementDataView:(UIGestureRecognizer*)gestureRecognizer { - _searchByRawAdvertisementDataTextView.text = EmptyText; - _viewModel.searchByRawAdvertisingData = EmptyText; - if ([_searchByRawAdvertisementDataTextView isFirstResponder] == false) { - [self textViewDidEndEditing:_searchByRawAdvertisementDataTextView]; - } - [self hideClearImageForAdvertisementData]; -} - - (void)hideClearButtons { [self hideClearImageForDeviceName]; - [self hideClearImageForAdvertisementData]; } - (void)hideClearImageForDeviceName { [_clearTextImageByDeviceName setHidden:YES]; } -- (void)hideClearImageForAdvertisementData { - [_clearTextImageByAdvertisementData setHidden:YES]; -} - - (void)showClearImageForDeviceName { [_clearTextImageByDeviceName setHidden:NO]; } -- (void)showClearImageForAdvertisementData { - [_clearTextImageByAdvertisementData setHidden:NO]; -} - # pragma mark - TextViewDelegate - (void)textViewDidChange:(UITextView *)textView { @@ -217,10 +161,6 @@ -(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range re return NO; } - if ([textView isEqual:_searchByRawAdvertisementDataTextView]) { - return [self isHexNumberInString:text]; - } - return YES; } @@ -228,9 +168,6 @@ - (void)textViewDidBeginEditing:(UITextView *)textView { if ([textView isEqual:_searchByDeviceNameTextView]) { [self discardPlaceholderIfNeededForDeviceNameTextView]; } - if ([textView isEqual:_searchByRawAdvertisementDataTextView]) { - [self discardPlaceholderIfNeededForRawAdvertisementDataTextView]; - } [self manageClearImageState:textView]; [textView becomeFirstResponder]; @@ -240,9 +177,6 @@ - (void)manageClearImageState:(UITextView*)textView { if ([textView isEqual:_searchByDeviceNameTextView]) { [self manageClearImageStateForDeviceNameTextView]; } - if ([textView isEqual:_searchByRawAdvertisementDataTextView]) { - [self manageClearImageStateForAdvertisementData]; - } } - (void)manageClearImageStateForDeviceNameTextView { @@ -253,33 +187,16 @@ - (void)manageClearImageStateForDeviceNameTextView { } } -- (void)manageClearImageStateForAdvertisementData { - if (([_searchByRawAdvertisementDataTextView.text isEqualToString:EmptyText] || [_searchByRawAdvertisementDataTextView.text isEqualToString:SearchByRawAdvertisementDataPlaceholder]) == false) { - [self showClearImageForAdvertisementData]; - } else { - [self hideClearImageForAdvertisementData]; - } -} - - (void)discardPlaceholderIfNeededForDeviceNameTextView { if ([_searchByDeviceNameTextView.text isEqualToString:SearchByDeviceNamePlaceholder]) { _searchByDeviceNameTextView.text = EmptyText; } } -- (void)discardPlaceholderIfNeededForRawAdvertisementDataTextView { - if ([_searchByRawAdvertisementDataTextView.text isEqualToString:SearchByRawAdvertisementDataPlaceholder]) { - _searchByRawAdvertisementDataTextView.text = EmptyText; - } -} - - (void)textViewDidEndEditing:(UITextView *)textView { if ([textView isEqual:_searchByDeviceNameTextView]) { [self setPlaceholderIfNeededForDeviceNameTextView]; } - if ([textView isEqual:_searchByRawAdvertisementDataTextView]) { - [self setPlaceholderIfNeededForRawAdvertisementDataTextView]; - } [textView resignFirstResponder]; } @@ -290,24 +207,12 @@ - (void)setPlaceholderIfNeededForDeviceNameTextView { } } -- (void)setPlaceholderIfNeededForRawAdvertisementDataTextView { - if ([_searchByRawAdvertisementDataTextView.text isEqualToString:EmptyText]) { - _searchByRawAdvertisementDataTextView.text = SearchByRawAdvertisementDataPlaceholder; - } -} - - (void)updateSearchTextViewModels { if ([self.searchByDeviceNameTextView.text isEqualToString:SearchByDeviceNamePlaceholder]) { self.viewModel.searchByDeviceName = EmptyText; } else { self.viewModel.searchByDeviceName = self.searchByDeviceNameTextView.text; } - - if ([self.searchByRawAdvertisementDataTextView.text isEqualToString:SearchByRawAdvertisementDataPlaceholder]) { - self.viewModel.searchByRawAdvertisingData = EmptyText; - } else { - self.viewModel.searchByRawAdvertisingData = self.searchByRawAdvertisementDataTextView.text; - } } - (void)keyboardDidHide:(NSNotification*)notification { @@ -549,22 +454,11 @@ - (void)updateFilterView { - (void)updateFilterViewValues { _searchByDeviceNameTextView.text = _viewModel.searchByDeviceName; - _searchByRawAdvertisementDataTextView.text = _viewModel.searchByRawAdvertisingData; _dBmSlider.value = _viewModel.dBmValue; _favouriteSwitch.on = _viewModel.isFavouriteFilterSet; _connectableSwitch.on = _viewModel.isConnectableFilterSet; [self textViewDidEndEditing:_searchByDeviceNameTextView]; - [self textViewDidEndEditing:_searchByRawAdvertisementDataTextView]; [self manageClearImageState:_searchByDeviceNameTextView]; - [self manageClearImageState:_searchByRawAdvertisementDataTextView]; -} - -# pragma mark - Hex Number Checker - -- (BOOL)isHexNumberInString:(NSString*)text { - NSString* const HexsNumbersPattern = @"[0-9A-F]"; - NSRange range = [text rangeOfString:HexsNumbersPattern options:NSRegularExpressionSearch]; - return range.location != NSNotFound; } @end diff --git a/SiliconLabsApp/ViewControllers/BluetoothBrowser/KeyChains/Info/SILKeychainInfoViewController.swift b/SiliconLabsApp/ViewControllers/BluetoothBrowser/KeyChains/Info/SILKeychainInfoViewController.swift new file mode 100644 index 00000000..f101537e --- /dev/null +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/KeyChains/Info/SILKeychainInfoViewController.swift @@ -0,0 +1,38 @@ +// +// SILKeychainInfoViewController.swift +// BlueGecko +// +// Created by Kamil Czajka on 21/05/2020. +// Copyright © 2020 SiliconLabs. All rights reserved. +// + +import UIKit + +class SILKeychainInfoViewController: UIViewController { + @IBOutlet weak var informationsLabel: UILabel! + var delegate: SILKeychainInfoViewContollerDelegate! + + let InfoText = #"The mappings dictionary contains the 128-bit UUIDs for services and characteristics that have been renamed by the user. The main purpose is simple consultation but it is also possible to change or delete them from this view. When deleted, the 128-bit UUIDs will again be shown as "Unknown Service" or "Unknown Characteristic" in the GATT."# + + override func viewDidLoad() { + super.viewDidLoad() + informationsLabel.text = InfoText + } + + override var preferredContentSize: CGSize { + get { + if UIDevice.current.userInterfaceIdiom == .pad { + return CGSize(width: 400, height: 400) + } else { + return CGSize(width: 300, height: 350) + } + } + set { + super.preferredContentSize = newValue + } + } + + @IBAction func didTapOKButton(_ sender: Any) { + delegate!.shouldCloseInfoViewController(self) + } +} diff --git a/SiliconLabsApp/ViewControllers/BluetoothBrowser/KeyChains/Info/SILKeychainInfoViewControllerDelegate.swift b/SiliconLabsApp/ViewControllers/BluetoothBrowser/KeyChains/Info/SILKeychainInfoViewControllerDelegate.swift new file mode 100644 index 00000000..a82bdadf --- /dev/null +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/KeyChains/Info/SILKeychainInfoViewControllerDelegate.swift @@ -0,0 +1,13 @@ +// +// SILKeychainInfoViewControllerDelegate.swift +// BlueGecko +// +// Created by Kamil Czajka on 22/05/2020. +// Copyright © 2020 SiliconLabs. All rights reserved. +// + +import Foundation + +protocol SILKeychainInfoViewContollerDelegate { + func shouldCloseInfoViewController(_ infoViewController: SILKeychainInfoViewController) +} diff --git a/SiliconLabsApp/ViewControllers/BluetoothBrowser/KeyChains/SILKeychainViewController.swift b/SiliconLabsApp/ViewControllers/BluetoothBrowser/KeyChains/SILKeychainViewController.swift index fd049bee..0258d2af 100644 --- a/SiliconLabsApp/ViewControllers/BluetoothBrowser/KeyChains/SILKeychainViewController.swift +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/KeyChains/SILKeychainViewController.swift @@ -18,6 +18,7 @@ class SILKeychainViewController: UIViewController { @IBOutlet weak var tableView: UITableView! @IBOutlet weak var tableLeftInset: NSLayoutConstraint! @IBOutlet weak var tableRightInset: NSLayoutConstraint! + @IBOutlet weak var infoImage: UIImageView! var popoverController: WYPopoverController? var realmCharacteristicsNotificationToken: NotificationToken? = nil @@ -55,8 +56,19 @@ class SILKeychainViewController: UIViewController { super.viewDidLoad() setupNotificationToken() setupTableViewInsets() + setupInfoImage() } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + addObserverForDisplayToastResponse() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + NotificationCenter.default.removeObserver(self) + } + private func setupNotificationToken() { if self.characteristics != nil { setupCharacteristicsNotificationTokenIfNeeded() @@ -104,6 +116,30 @@ class SILKeychainViewController: UIViewController { } } + private func setupInfoImage() { + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tappedInfoImage(_:))) + self.infoImage.addGestureRecognizer(tapGesture) + } + + @objc private func tappedInfoImage(_ regognizer: UIGestureRecognizer) { + let storyboard = UIStoryboard(name: "SILKeychain", bundle: nil) + if let infoViewController = storyboard.instantiateViewController(withIdentifier: "KeychainInfo") as? SILKeychainInfoViewController { + infoViewController.delegate = self + self.popoverController = WYPopoverController.sil_presentCenterPopover(withContentViewController: infoViewController, presenting:self, delegate:self as? WYPopoverControllerDelegate, animated:true) + } + } + + private func addObserverForDisplayToastResponse() { + NotificationCenter.default.addObserver(self, selector:#selector(displayToast(_:)), name: NSNotification.Name(rawValue: SILNotificationDisplayToastResponse), object: nil) + } + + @objc private func displayToast(_ notification: Notification) { + let ErrorMessage = notification.userInfo?[SILNotificationKeyDescription] as? String ?? "" + self.showToast(message: ErrorMessage, toastType: .disconnectionError) { + NotificationCenter.default.post(name: NSNotification.Name(rawValue: SILNotificationDisplayToastRequest), object: nil) + } + } + @IBAction func back(_ sender: UIButton) { self.navigationController?.popViewController(animated: true) } @@ -209,3 +245,11 @@ extension SILKeychainViewController : SILDebugPopoverViewControllerDelegate { } } } + +extension SILKeychainViewController : SILKeychainInfoViewContollerDelegate { + func shouldCloseInfoViewController(_ infoViewController: SILKeychainInfoViewController) { + self.popoverController?.dismissPopover(animated: true) { + self.popoverController = nil + } + } +} diff --git a/SiliconLabsApp/ViewControllers/BluetoothBrowser/SILBluetoothBrowserViewController.m b/SiliconLabsApp/ViewControllers/BluetoothBrowser/SILBluetoothBrowserViewController.m index 09970303..f7cdeec5 100644 --- a/SiliconLabsApp/ViewControllers/BluetoothBrowser/SILBluetoothBrowserViewController.m +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/SILBluetoothBrowserViewController.m @@ -43,7 +43,6 @@ @interface SILBluetoothBrowserViewController () * expandSections; @@ -77,20 +77,20 @@ - (void)viewDidLoad { [self setupBrowserExpandableViewManager]; [self setupButtonsTabBar]; [self setupScanningButton]; - [self registerForApplicationWillResignActiveNotification]; [self setupBrowserViewModel]; [self installViewModelsForExpandableViews]; [self setupBackgroundForScanning:YES]; [self setScanningButtonAppearanceWithScanning:_isScanning]; - [self addObservers]; - [self updateConnectionsButtonTitle]; [self setupBrowserTableViewAppearance]; + [self clearViewModelsForExpandableViews]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; self.browserViewModel.observing = YES; [self manageScannerState]; + [self addObservers]; + [self updateConnectionsButtonTitle]; } - (void)viewDidAppear:(BOOL)animated { @@ -105,6 +105,7 @@ - (void)viewWillDisappear:(BOOL)animated { self.browserViewModel.observing = NO; [self setScannerStateWhenControllerIsDisappeared]; [self.browserViewModel clearIsConnectingDirectory]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; } #pragma mark - Setup @@ -149,7 +150,7 @@ - (void)setupBrowserExpandableViewManager { } - (void)setupButtonsTabBar { - [self.browserExpandableViewManager setupButtonsTabBarWithLog:self.logButton connections:self.connectionsButton filter:self.filterButton andActiveFilterImage:self.activeFilterImage]; + [self.browserExpandableViewManager setupButtonsTabBarWithLog:self.logButton connections:self.connectionsButton filter:self.filterButton andFilterIsActive:[self.filterViewModel isFilterActive]]; } - (void)setupScanningButton { @@ -170,11 +171,9 @@ - (void)setupBrowserViewModel { } - (void)installViewModelsForExpandableViews { - SILBrowserLogViewModel* browserLog = [SILBrowserLogViewModel sharedInstance]; self.connectionsViewModel = [SILBrowserConnectionsViewModel sharedInstance]; self.connectionsViewModel.centralManager = self.browserViewModel.centralManager; - [self.connectionsViewModel disconnectAllPeripheral]; - [browserLog clearLogs]; + self.filterViewModel = [SILBrowserFilterViewModel sharedInstance]; } - (void)setupBackgroundForScanning:(BOOL)scanning { @@ -189,6 +188,8 @@ - (void)setupBackgroundForScanning:(BOOL)scanning { - (void)addObservers { [self addObserversForReloadBrowserTableView]; [self addObserverForReloadConnectionsButtonTitle]; + [self addObserverForDisplayToastResponse]; + [self registerForApplicationWillResignActiveNotification]; } - (void)addObserversForReloadBrowserTableView { @@ -210,6 +211,17 @@ - (void)updateConnectionsButtonTitle { [self.browserExpandableViewManager updateConnectionsButtonTitle:connections]; } +- (void)addObserverForDisplayToastResponse { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(displayToast:) name:SILNotificationDisplayToastResponse object:nil]; +} + +- (void)displayToast:(NSNotification*)notification { + NSString* ErrorMessage = notification.userInfo[SILNotificationKeyDescription]; + [self showToastWithMessage:ErrorMessage toastType:ToastTypeDisconnectionError completion:^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SILNotificationDisplayToastRequest object:nil]; + }]; +} + #pragma mark - SILDebugDeviceViewModelDelegate - (void)didConnectToPeripheral:(CBPeripheral *)peripheral { @@ -266,10 +278,10 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N } return cell; } else { - SILBrowserServiceViewCell *cell = [tableView + SILBrowserDeviceAdTypeViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SILClassBrowserServiceViewCell forIndexPath:indexPath]; - [self configureServiceCell:cell atIndexPath:indexPath]; + [self configureAdTypeCell:cell atIndexPath:indexPath]; if (@available(iOS 13, *)) {} else { if ([tableView numberOfRowsInSection:indexPath.section]-1 == indexPath.row) { cell.isRounded = YES; @@ -328,11 +340,11 @@ - (void)configureDeviceCell:(SILBrowserDeviceViewCell*)cell atIndexPath:(NSIndex } } -- (void)configureServiceCell:(SILBrowserServiceViewCell*)cell atIndexPath:(NSIndexPath *)indexPath { +- (void)configureAdTypeCell:(SILBrowserDeviceAdTypeViewCell*)cell atIndexPath:(NSIndexPath *)indexPath { SILDiscoveredPeripheralDisplayDataViewModel *discoveredPeripheralViewModel = [self.browserViewModel peripheralViewModelAt:indexPath.section]; SILAdvertisementDataViewModel *detailModel = discoveredPeripheralViewModel.advertisementDataViewModelsForInfoView[indexPath.row-1]; - cell.serviceNameLabel.text = detailModel.typeString; - cell.serviceUUIDLabel.text = detailModel.valueString; + cell.adTypeNameLabel.text = detailModel.typeString; + cell.adTypeValueLabel.text = detailModel.valueString; } - (BOOL)isConnectedPeripheral:(SILDiscoveredPeripheral*)peripheral { @@ -363,7 +375,7 @@ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPa if (indexPath.row == 0) { return 121.0; } - return 76.0; + return UITableViewAutomaticDimension; } - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { @@ -584,11 +596,7 @@ - (void)searchButtonWasTapped:(SILBrowserFilterViewModel *)filterViewModel { } - (void)manageAppearanceOfActiveFilterImage:(SILBrowserFilterViewModel*)filterViewModel { - if ([filterViewModel isFilterActive]) { - [self.activeFilterImage setHidden:NO]; - } else { - [self.activeFilterImage setHidden:YES]; - } + [self.browserExpandableViewManager updateFilterIsActiveFilter:[filterViewModel isFilterActive]]; } - (void)filterBrowser:(SILBrowserFilterViewModel*)filterViewModel { @@ -605,11 +613,6 @@ - (void)setFilters:(SILBrowserFilterViewModel*)filterViewModel { } else { self.browserViewModel.searchByDeviceName = nil; } - if (![filterViewModel.searchByRawAdvertisingData isEqualToString:EmptyText]) { - self.browserViewModel.searchByAdvertisingData = filterViewModel.searchByRawAdvertisingData; - } else { - self.browserViewModel.searchByAdvertisingData = nil; - } self.browserViewModel.currentMinRSSI = [NSNumber numberWithInteger:filterViewModel.dBmValue]; self.browserViewModel.beaconTypes = filterViewModel.beaconTypes; self.browserViewModel.isFavourite = filterViewModel.isFavouriteFilterSet; @@ -639,6 +642,7 @@ - (void)presentDetailsViewControllerForIndex:(NSInteger)index { detailsVC.centralManager = self.browserViewModel.centralManager; [self updateCellsWithConnecting:connectedPeripheral.peripheral]; [self removeUnfiredTimers]; + [self.browserExpandableViewManager removeExpandingControllerIfNeeded]; [self.navigationController pushViewController:detailsVC animated:YES]; } @@ -650,9 +654,17 @@ - (void)logViewBackButtonPressed { - (IBAction)backToDevelopWasTapped:(id)sender { [self stopScanningAction]; + [self.connectionsViewModel disconnectAllPeripheral]; [self.navigationController popViewControllerAnimated:NO]; } +- (void)clearViewModelsForExpandableViews { + [self.connectionsViewModel clearViewModelData]; + [self.filterViewModel clearViewModelData]; + SILBrowserLogViewModel* browserLog = [SILBrowserLogViewModel sharedInstance]; + [browserLog clearLogs]; +} + - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if ([self isSignificatantScrollToTop:scrollView]) { [self.browserViewModel stopScanningWithVisibleCellsCount:0]; diff --git a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Utils/SILAnimatedUIButton.swift b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Utils/SILAnimatedUIButton.swift new file mode 100644 index 00000000..7e104a9b --- /dev/null +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Utils/SILAnimatedUIButton.swift @@ -0,0 +1,28 @@ +// +// SILAnimatedUIButton.swift +// BlueGecko +// +// Created by Kamil Czajka on 01/06/2020. +// Copyright © 2020 SiliconLabs. All rights reserved. +// + +import UIKit + +class SILAnimatedUIButton: UIButton { + override func awakeFromNib() { + super.awakeFromNib() + self.addTarget(self, action: #selector(buttonClicked(sender:)), for: .touchUpInside) + } + + @objc private func buttonClicked(sender: UIButton) { + UIView.animate(withDuration: 0.1, animations: {() -> Void in + sender.imageView?.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) + sender.alpha = 0.5 + }, completion: {(_ finished: Bool) -> Void in + UIView.animate(withDuration: 0.2, animations: {() -> Void in + sender.imageView?.transform = CGAffineTransform(scaleX: 1, y: 1) + sender.alpha = 1.0 + }) + }) + } +} diff --git a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Utils/SILBluetoothBrowserExpandableViewManager.h b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Utils/SILBluetoothBrowserExpandableViewManager.h index 11b2a296..153c836e 100644 --- a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Utils/SILBluetoothBrowserExpandableViewManager.h +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Utils/SILBluetoothBrowserExpandableViewManager.h @@ -20,12 +20,13 @@ - (void)setReferenceForPresentationView:(UIView*)presentationView andDiscoveredDevicesView:(UIView*)discoveredDevicesView; - (void)setReferenceForExpandableControllerView:(UIView*)expandableControllerView andExpandableControllerHeight:(NSLayoutConstraint*)expandableControllerHeight; - (void)setupButtonsTabBarWithLog:(UIButton*)logButton connections:(UIButton*)connectionsButton; -- (void)setupButtonsTabBarWithLog:(UIButton*)logButton connections:(UIButton*)connectionsButton filter:(UIButton*)filterButton andActiveFilterImage:(UIImageView*)activeImageView; +- (void)setupButtonsTabBarWithLog:(UIButton*)logButton connections:(UIButton*)connectionsButton filter:(UIButton*)filterButton andFilterIsActive:(BOOL)isActive; - (SILBrowserLogViewController*)logButtonWasTappedAction; - (SILBrowserConnectionsViewController*)connectionsButtonWasTappedAction; - (SILBrowserFilterViewController*)filterButtonWasTappedAction; - (void)removeExpandingControllerIfNeeded; - (void)updateConnectionsButtonTitle:(NSUInteger)connections; +- (void)updateFilterIsActiveFilter:(BOOL)isActiveFilter; @end diff --git a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Utils/SILBluetoothBrowserExpandableViewManager.m b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Utils/SILBluetoothBrowserExpandableViewManager.m index 48a03f4a..1f50bb04 100644 --- a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Utils/SILBluetoothBrowserExpandableViewManager.m +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Utils/SILBluetoothBrowserExpandableViewManager.m @@ -21,7 +21,6 @@ @interface SILBluetoothBrowserExpandableViewManager () @property (strong, nonatomic) UIButton* logButton; @property (strong, nonatomic) UIButton* connectionsButton; @property (strong, nonatomic) UIButton* filterButton; -@property (strong, nonatomic) UIImageView* activeFilterImageView; @property (strong, nonatomic) NSLayoutConstraint* expandableControllerHeight; @property (strong, nonatomic) UIView* expandableControllerView; @property (strong, nonatomic) UIViewController* expandingViewController; @@ -51,14 +50,13 @@ - (void)setupButtonsTabBarWithLog:(UIButton*)logButton connections:(UIButton*)co [self setupConnectionButton]; } -- (void)setupButtonsTabBarWithLog:(UIButton*)logButton connections:(UIButton*)connectionsButton filter:(UIButton*)filterButton andActiveFilterImage:(UIImageView*)activeImageView { +- (void)setupButtonsTabBarWithLog:(UIButton*)logButton connections:(UIButton*)connectionsButton filter:(UIButton*)filterButton andFilterIsActive:(BOOL)isActive { self.logButton = logButton; self.connectionsButton = connectionsButton; self.filterButton = filterButton; - self.activeFilterImageView = activeImageView; [self setupLogButton]; [self setupConnectionButton]; - [self setupFilterButton]; + [self setupFilterButtonWhereIsFilterActive:isActive]; } - (void)setReferenceForPresentationView:(UIView*)presentationView andDiscoveredDevicesView:(UIView*)discoveredDevicesView { @@ -152,6 +150,14 @@ - (void)updateConnectionsButtonTitle:(NSUInteger)connections { [self.connectionsButton setTitle:connectionsText forState:UIControlStateSelected]; } +- (void)updateFilterIsActiveFilter:(BOOL)isActiveFiter { + if (isActiveFiter) { + [self setupImagesForActiveFilter]; + } else { + [self setupImagesForInactiveFilter]; + } +} + #pragma mark - Private Functions - (void)setupLogButton { @@ -186,13 +192,12 @@ - (void)setupConnectionButton { [self.connectionsButton setTitleColor:[UIColor sil_regularBlueColor] forState:UIControlStateSelected]; } -- (void)setupFilterButton { +- (void)setupFilterButtonWhereIsFilterActive:(BOOL)isFilterActive { UIEdgeInsets const ImageInsetsForFilterButton = {0, 8, 0 ,12}; UIEdgeInsets const TitleEdgeInsetsForFilterButton = {0, 8, 0, 8}; [self.filterButton setTintColor:[UIColor clearColor]]; - [self.filterButton setImage:[[UIImage imageNamed:SILImageSearchOff] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateNormal]; - [self.filterButton setImage:[[UIImage imageNamed:SILImageSearchOn] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateSelected]; + [self updateFilterIsActiveFilter:isFilterActive]; self.filterButton.imageView.contentMode = UIViewContentModeScaleAspectFit; self.filterButton.imageEdgeInsets = ImageInsetsForFilterButton; [self.filterButton setTitleEdgeInsets:TitleEdgeInsetsForFilterButton]; @@ -200,7 +205,17 @@ - (void)setupFilterButton { self.filterButton.titleLabel.font = [UIFont robotoMediumWithSize:[UIFont getMiddleFontSize]]; [self.filterButton setTitleColor:[UIColor sil_primaryTextColor] forState:UIControlStateNormal]; [self.filterButton setTitleColor:[UIColor sil_regularBlueColor] forState:UIControlStateSelected]; - [self.activeFilterImageView setHidden:YES]; + +} + +- (void)setupImagesForInactiveFilter { + [self.filterButton setImage:[[UIImage imageNamed:SILImageFilterOff] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateNormal]; + [self.filterButton setImage:[[UIImage imageNamed:SILImageFilterOffSelected] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateSelected]; +} + +- (void)setupImagesForActiveFilter { + [self.filterButton setImage:[[UIImage imageNamed:SILImageFilterOn] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateNormal]; + [self.filterButton setImage:[[UIImage imageNamed:SILImageFilterOnSelected] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateSelected]; } - (BOOL)isAnyButtonSelected { diff --git a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Views/SILBrowserDeviceAdTypeViewCell.swift b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Views/SILBrowserDeviceAdTypeViewCell.swift new file mode 100644 index 00000000..0bce5c5b --- /dev/null +++ b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Views/SILBrowserDeviceAdTypeViewCell.swift @@ -0,0 +1,22 @@ +// +// SILBrowserDeviceAdTypeViewCell.swift +// BlueGecko +// +// Created by Jan Wisniewski on 03/02/2020. +// Copyright © 2020 SiliconLabs. All rights reserved. +// + +import UIKit + +class SILBrowserDeviceAdTypeViewCell: SILCell { + + @IBOutlet weak var adTypeNameLabel: UILabel! + @IBOutlet weak var adTypeValueLabel: UILabel! + + override func prepareForReuse() { + super.prepareForReuse() + adTypeNameLabel.text = "" + adTypeValueLabel.text = "" + } + +} diff --git a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Views/SILBrowserServiceViewCell.swift b/SiliconLabsApp/ViewControllers/BluetoothBrowser/Views/SILBrowserServiceViewCell.swift deleted file mode 100644 index c36ca32c..00000000 --- a/SiliconLabsApp/ViewControllers/BluetoothBrowser/Views/SILBrowserServiceViewCell.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// SILBrowserServiceViewCell.swift -// BlueGecko -// -// Created by Jan Wisniewski on 03/02/2020. -// Copyright © 2020 SiliconLabs. All rights reserved. -// - -import UIKit - -class SILBrowserServiceViewCell: SILCell { - - @IBOutlet weak var serviceNameLabel: UILabel! - @IBOutlet weak var serviceUUIDLabel: UILabel! - - override func prepareForReuse() { - super.prepareForReuse() - serviceNameLabel.text = "" - serviceUUIDLabel.text = "" - } - -} diff --git a/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugCharacteristicEncodingFieldTableViewCell.h b/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugCharacteristicEncodingFieldTableViewCell.h index 334f059e..13321080 100644 --- a/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugCharacteristicEncodingFieldTableViewCell.h +++ b/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugCharacteristicEncodingFieldTableViewCell.h @@ -13,4 +13,5 @@ @property(weak, nonatomic) IBOutlet UILabel *asciiValueLabel; @property(weak, nonatomic) IBOutlet UILabel *decimalValueLabel; @property(weak, nonatomic) IBOutlet UILabel* editLabel; +- (void)clearValues; @end diff --git a/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugCharacteristicEncodingFieldTableViewCell.m b/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugCharacteristicEncodingFieldTableViewCell.m index 499a392a..cfc28dd6 100644 --- a/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugCharacteristicEncodingFieldTableViewCell.m +++ b/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugCharacteristicEncodingFieldTableViewCell.m @@ -30,4 +30,10 @@ - (void)awakeFromNib { } } +- (void)clearValues { + self.hexValueLabel.text = @""; + self.asciiValueLabel.text = @""; + self.decimalValueLabel.text = @""; +} + @end diff --git a/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugCharacteristicTableViewCell.m b/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugCharacteristicTableViewCell.m index 989ac979..40f42877 100644 --- a/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugCharacteristicTableViewCell.m +++ b/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugCharacteristicTableViewCell.m @@ -24,7 +24,6 @@ @interface SILDebugCharacteristicTableViewCell() @property (weak, nonatomic) IBOutlet UIStackView *propertyButtonsStackView; @property (weak, nonatomic) IBOutlet UILabel *characteristicNameLabel; @property (weak, nonatomic) IBOutlet UILabel *characteristicUuidLabel; -@property (weak, nonatomic) IBOutlet UIImageView *viewMoreChevron; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *iPadBottomDividerLeadingConstraint; @property (weak, nonatomic) IBOutlet UILabel *descriptorsTextLabel; @property (weak, nonatomic) IBOutlet UIView *descriptorsView; @@ -43,8 +42,6 @@ @interface SILDebugCharacteristicTableViewCell() @implementation SILDebugCharacteristicTableViewCell -NSString * const KVONotifyingName = @"notifying"; - - (void)awakeFromNib { [super awakeFromNib]; self.selectionStyle = UITableViewCellSelectionStyleNone; @@ -52,7 +49,6 @@ - (void)awakeFromNib { } - (void)configureWithCharacteristicModel:(SILCharacteristicTableModel *)characteristicModel { - [self updateChevronImageForExpanded:characteristicModel.isExpanded]; self.characteristicTableModel = characteristicModel; self.characteristic = characteristicModel.characteristic; [self.nameEditButton setHidden:!characteristicModel.isMappable]; @@ -65,10 +61,9 @@ - (void)configureWithCharacteristicModel:(SILCharacteristicTableModel *)characte self.descriptorsTextLabel.text = [self getDescriptorsText:characteristicModel.descriptorModels]; } self.topSeparatorView.hidden = characteristicModel.hideTopSeparator; - [self configureAsExpandable:[characteristicModel canExpand] || [characteristicModel isUnknown]]; self.allActiveProperties = [SILDebugProperty getActivePropertiesFrom:characteristicModel.characteristic.properties]; [self configurePropertyViewsForProperties:self.allActiveProperties]; - [self togglePropertyEnabledIfExpanded]; + [self toggleProperties]; [self layoutIfNeeded]; } @@ -107,10 +102,6 @@ - (void)configureWithHomeKitCharacteristicModel:(SILHomeKitCharacteristicTableMo } #endif -- (void)configureAsExpandable:(BOOL)canExpand { - self.viewMoreChevron.hidden = !canExpand; -} - - (void)configurePropertyViewsForProperties:(NSArray *)properties { [self layoutIfNeeded]; @@ -137,70 +128,48 @@ - (void)configurePropertyViewsForProperties:(NSArray *)properties { #pragma mark - SILGenericAttributeTableCell - (void)expandIfAllowed:(BOOL)isExpanding { - [self togglePropertyEnabledIfExpanded]; - [self updateChevronImageForExpanded:isExpanding]; + [self toggleProperties]; } -- (void)updateChevronImageForExpanded:(BOOL)expanded { - self.viewMoreChevron.image = [UIImage imageNamed: expanded ? SILImageChevronExpanded : SILImageChevronCollapsed]; -} - -- (void)togglePropertyEnabledIfExpanded { - BOOL expanded = self.characteristicTableModel.isExpanded; - BOOL isNotifying = [[self.characteristic valueForKey:KVONotifyingName] boolValue]; +- (void)toggleProperties { + BOOL isNotifying = [self.characteristic isNotifying]; for (SILDebugProperty *property in self.allActiveProperties) { if ([property.keysForActivation.firstObject isEqual:@(CBCharacteristicPropertyRead)]) { - [self.readPropertyButton setUserInteractionEnabled:expanded]; - [self readButtonAppearanceWithCondition:expanded]; + [self readButtonAppearance]; } else if ([property.keysForActivation.firstObject isEqual:@(CBCharacteristicPropertyWrite)]) { - [self.writePropertyButton setUserInteractionEnabled:expanded]; - [self writeButtonAppearanceWithCondition:expanded]; + [self writeButtonAppearance]; } else if ([property.keysForActivation.firstObject isEqual:@(CBCharacteristicPropertyWriteWithoutResponse)]) { - [self.writeNoResponsePropertyButton setUserInteractionEnabled:expanded]; - [self writeNoResponseButtonAppearanceWithCondition:expanded]; + [self writeNoResponseButtonAppearance]; } else if ([property.keysForActivation.firstObject isEqual:@(CBCharacteristicPropertyIndicate)]) { - [self.indicatePropertyButton setUserInteractionEnabled:expanded]; - [self indicateButtonAppearanceWithCondition:(expanded && isNotifying)]; + [self indicateButtonAppearanceWithCondition:isNotifying]; } else if ([property.keysForActivation.firstObject isEqual:@(CBCharacteristicPropertyNotify)]) { - [self.notifyPropertyButton setUserInteractionEnabled:expanded]; - [self notifyButtonAppearanceWithCondition:(expanded && isNotifying)]; + [self notifyButtonAppearanceWithCondition:isNotifying]; } } } #pragma mark - Buttons Appearance -- (void)readButtonAppearanceWithCondition:(BOOL)condition { - NSString *readImageString = condition ? SILImageNamePropertyRead : SILImageNamePropertyReadDisabled; + +- (void)readButtonAppearance { + NSString *readImageString = SILImageNamePropertyReadDisabled; self.readPropertyButton.imageView.contentMode = UIViewContentModeScaleAspectFit; [self.readPropertyButton setImage:[UIImage imageNamed:readImageString] forState:UIControlStateNormal]; - if (condition) { - [self.readPropertyButton setTitleColor:[UIColor sil_regularBlueColor] forState:UIControlStateNormal]; - } else { - [self.readPropertyButton setTitleColor:[UIColor sil_primaryTextColor] forState:UIControlStateNormal]; - } + [self.readPropertyButton setTitleColor:[UIColor sil_primaryTextColor] forState:UIControlStateNormal]; } -- (void)writeButtonAppearanceWithCondition:(BOOL)condition { - NSString *writeImageString = condition ? SILImageNamePropertyWrite : SILImageNamePropertyWriteDisabled; +- (void)writeButtonAppearance { + NSString *writeImageString = SILImageNamePropertyWriteDisabled; self.writePropertyButton.imageView.contentMode = UIViewContentModeScaleAspectFit; [self.writePropertyButton setImage:[UIImage imageNamed:writeImageString] forState:UIControlStateNormal]; - if (condition) { - [self.writePropertyButton setTitleColor:[UIColor sil_regularBlueColor] forState:UIControlStateNormal]; - } else { - [self.writePropertyButton setTitleColor:[UIColor sil_primaryTextColor] forState:UIControlStateNormal]; - } + [self.writePropertyButton setTitleColor:[UIColor sil_primaryTextColor] forState:UIControlStateNormal]; } -- (void)writeNoResponseButtonAppearanceWithCondition:(BOOL)condition { - NSString *writeImageString = condition ? SILImageNamePropertyWriteNoResponse : SILImageNamePropertyWriteNoResponseDisabled; +- (void)writeNoResponseButtonAppearance { + NSString *writeImageString = SILImageNamePropertyWriteNoResponseDisabled; self.writeNoResponsePropertyButton.imageView.contentMode = UIViewContentModeScaleAspectFit; [self.writeNoResponsePropertyButton setImage:[UIImage imageNamed:writeImageString] forState:UIControlStateNormal]; - if (condition) { - [self.writeNoResponsePropertyButton setTitleColor:[UIColor sil_regularBlueColor] forState:UIControlStateNormal]; - } else { - [self.writeNoResponsePropertyButton setTitleColor:[UIColor sil_primaryTextColor] forState:UIControlStateNormal]; - } + [self.writeNoResponsePropertyButton setTitleColor:[UIColor sil_primaryTextColor] forState:UIControlStateNormal]; } - (void)indicateButtonAppearanceWithCondition:(BOOL)condition { @@ -228,27 +197,36 @@ - (void)notifyButtonAppearanceWithCondition:(BOOL)condition { #pragma mark - Button Actions - (IBAction)handleReadViewTap:(id)sender { + [self.characteristicTableModel expandFieldIfNeeded]; [self.delegate cell:self didRequestReadForCharacteristic:self.characteristic]; } - (IBAction)handleWriteViewTap:(id)sender { + [self.characteristicTableModel expandFieldIfNeeded]; [self.delegate cell:self didRequestWriteForCharacteristic:self.characteristic]; } - (IBAction)handleWriteNoResponseViewTap:(id)sender { + [self.characteristicTableModel expandFieldIfNeeded]; [self.delegate cell:self didRequestWriteNoResponseForCharacteristic:self.characteristic]; } - (IBAction)handleIndicateViewTap:(id)sender { - BOOL newNotifyingValue = ![[self.characteristic valueForKey:KVONotifyingName] boolValue]; - [self.delegate cell:self didRequestIndicateForCharacteristic:self.characteristic withValue:newNotifyingValue]; + BOOL newNotifyingValue = ![self.characteristic isNotifying]; [self indicateButtonAppearanceWithCondition:newNotifyingValue]; + if (newNotifyingValue) { + [self.characteristicTableModel expandFieldIfNeeded]; + } + [self.delegate cell:self didRequestIndicateForCharacteristic:self.characteristic withValue:newNotifyingValue]; } - (IBAction)handleNotifyViewTap:(id)sender { - BOOL newNotifyingValue = ![[self.characteristic valueForKey:KVONotifyingName] boolValue]; - [self.delegate cell:self didRequestNotifyForCharacteristic:self.characteristic withValue:newNotifyingValue]; + BOOL newNotifyingValue = ![self.characteristic isNotifying]; [self notifyButtonAppearanceWithCondition:newNotifyingValue]; + if (newNotifyingValue) { + [self.characteristicTableModel expandFieldIfNeeded]; + } + [self.delegate cell:self didRequestNotifyForCharacteristic:self.characteristic withValue:newNotifyingValue]; } - (IBAction)editName:(UIButton *)sender { diff --git a/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugServicesViewController.m b/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugServicesViewController.m index 34c4c0bb..45748d38 100644 --- a/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugServicesViewController.m +++ b/SiliconLabsApp/ViewControllers/DebugApp/ServicesTable/SILDebugServicesViewController.m @@ -59,7 +59,7 @@ static float kTableRefreshInterval = 1; -@interface SILDebugServicesViewController () +@interface SILDebugServicesViewController () @property (weak, nonatomic) IBOutlet UILabel *deviceNameLabel; @property (weak, nonatomic) IBOutlet UILabel *rssiLabel; @@ -77,7 +77,7 @@ @interface SILDebugServicesViewController () *)touches withEvent:(UIEvent *)event { + [super touchesBegan:touches withEvent:event]; + [self hideMenu]; +} + +- (void)showMenu { + [self.menuContainer setHidden:NO]; + self.tableView.userInteractionEnabled = NO; +} + +- (void)hideMenu { + [self.menuContainer setHidden:YES]; + self.tableView.userInteractionEnabled = YES; +} + #pragma mark - Expandable Controllers - (IBAction)connectionsButtonTapped:(id)sender { @@ -253,6 +306,11 @@ - (void)handleRefreshServices: (UIRefreshControl *)sender { - (void)otaUICoordinatorDidFishishOTAFlow:(SILOTAUICoordinator *)coordinator { [self.navigationController popViewControllerAnimated:YES]; + self.isUpdatingFirmware = NO; +} + +- (void)otaUICoordinatorDidCancelOTAFlow:(SILOTAUICoordinator *)coordinator { + self.isUpdatingFirmware = NO; } #pragma mark - Notifications @@ -268,11 +326,26 @@ - (void)addObserverForUpdateConnectionsButtonTitle { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateConnectionsButtonTitle) name:SILNotificationReloadConnectionsTableView object:nil]; } +- (void)addObserverForDisplayToastResponse { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(displayToast:) name:SILNotificationDisplayToastResponse object:nil]; +} + +- (void)displayToast:(NSNotification*)notification { + NSString* ErrorMessage = notification.userInfo[SILNotificationKeyDescription]; + [self showToastWithMessage:ErrorMessage toastType:ToastTypeDisconnectionError completion:^{ + [[NSNotificationCenter defaultCenter] postNotificationName:SILNotificationDisplayToastRequest object:nil]; + }]; +} + #pragma mark - Notification Methods - (void)didDisconnectPeripheralNotifcation:(NSNotification *)notification { - if (!self.isUpdatingFirmware) { - [self.navigationController popViewControllerAnimated:YES]; + NSString* uuid = (NSString*)notification.userInfo[SILNotificationKeyUUID]; + + if ([uuid isEqualToString:self.peripheral.identifier.UUIDString]) { + if (!self.isUpdatingFirmware) { + [self.navigationController popViewControllerAnimated:YES]; + } } } @@ -352,7 +425,6 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if ([[tableView cellForRowAtIndexPath:indexPath] isKindOfClass:[SILDebugCharacteristicTableViewCell class]]) { SILCharacteristicTableModel *characteristicModel = self.modelsToDisplay[indexPath.row]; - [characteristicModel readCharacteristicIfAllowed]; if ([characteristicModel isUnknown]) { [characteristicModel toggleExpansionIfAllowed]; id cell = [tableView cellForRowAtIndexPath:indexPath]; @@ -625,10 +697,33 @@ - (void)editNameForCharacteristic:(SILCharacteristicTableModel*)model { #pragma mark - SILDebugCharacteristicCellDelegate - (void)cell:(SILDebugCharacteristicTableViewCell *)cell didRequestReadForCharacteristic:(CBCharacteristic *)characteristic { + BOOL isPerformed = [cell.characteristicTableModel clearModel]; + if (!isPerformed) { + [self performManualClearingValuesIntoEncodingFieldTableViewCell:characteristic]; + } else { + [self refreshTable]; + } [self.peripheral readValueForCharacteristic:characteristic]; } +- (void)performManualClearingValuesIntoEncodingFieldTableViewCell:(CBCharacteristic*)characteristic { + for (id model in self.modelsToDisplay) { + if ([model isKindOfClass:[SILCharacteristicTableModel class]]) { + SILCharacteristicTableModel* characteristicModel = (SILCharacteristicTableModel*)model; + if ([characteristicModel isUnknown] && characteristicModel.characteristic == characteristic) { + NSUInteger index = [self.modelsToDisplay indexOfObject:model] + 1; + SILDebugCharacteristicTableViewCell* cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]]; + if ([cell isKindOfClass:[SILDebugCharacteristicEncodingFieldTableViewCell class]]) { + SILDebugCharacteristicEncodingFieldTableViewCell* encodingCell = (SILDebugCharacteristicEncodingFieldTableViewCell*)cell; + [encodingCell clearValues]; + } + } + } + } +} + - (void)cell:(SILDebugCharacteristicTableViewCell *)cell didRequestWriteForCharacteristic:(CBCharacteristic *)characteristic { + [self refreshTable]; if (cell.characteristicTableModel.isUnknown) { SILEncodingPseudoFieldRowModel *model = [[SILEncodingPseudoFieldRowModel alloc]initForCharacteristicModel:cell.characteristicTableModel]; [self displayCharacteristicEncoding:model.parentCharacteristicModel canEdit:model.parentCharacteristicModel.canWrite]; @@ -694,7 +789,7 @@ - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForServi - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { [CrashlyticsKit setObjectValue:peripheral.name forKey:@"peripheral"]; [self addOrUpdateModelForCharacteristic:characteristic forService:characteristic.service]; - [self markTableForUpdate]; + [self refreshTable]; [self postRegisterLogNotification:[SILLogDataModel prepareLogDescription:@"didUpdateValueForCharacteristic: " andCharacteristic:characteristic andPeripheral:peripheral andError:error]]; } @@ -734,6 +829,20 @@ - (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error self.rssiLabel.text = rssiDescription; } +- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { + NSLog(@"%@", error); + if (error == nil) { + [self postRegisterLogNotification:[SILLogDataModel prepareLogDescription:@"didUpdateNotificationStateForCharacteristic: Successful! " andCharacteristic:characteristic andPeripheral:peripheral andError:error]]; + } else { + [self postRegisterLogNotification:[SILLogDataModel prepareLogDescription:@"didUpdateNotificationStateForCharacteristic: Failed! " andCharacteristic:characteristic andPeripheral:peripheral andError:error]]; + + SILGattPropertiesErrorToastModel* errorToast = [[SILGattPropertiesErrorToastModel alloc] initWithPeripheralName:peripheral.name errorCode:error.code]; + NSString* ErrorMessage = [errorToast getErrorMessageForToast]; + [self showToastWithMessage:ErrorMessage toastType:ToastTypeGattPropertiesError completion:^{}]; + } + [self refreshTable]; +} + #pragma mark - Add or Update Attribute Models - (BOOL)addOrUpdateModelForService:(CBService *)service { @@ -914,9 +1023,9 @@ - (void)refreshTable { - (void)configureOtaButtonWithPeripheral:(CBPeripheral *)peripheral { if ([peripheral hasOTAService]) { - [self.otaButton setHidden:NO]; + [self.menuButton setHidden:NO]; } else { - [self.otaButton setHidden:YES]; + [self.menuButton setHidden:YES]; } } @@ -948,6 +1057,7 @@ - (void)presentDetailsViewControllerForIndex:(NSInteger)index { self.peripheral = connectedPeripheral.peripheral; [self.refreshControl removeFromSuperview]; self.refreshControl = nil; + self.allServiceModels = nil; [self viewDidLoad]; [self viewWillAppear:YES]; [self viewDidAppear:YES]; diff --git a/SiliconLabsApp/ViewModels/DebugDeviceViewModel.swift b/SiliconLabsApp/ViewModels/DebugDeviceViewModel.swift index 6f4e4a7d..7608fd56 100644 --- a/SiliconLabsApp/ViewModels/DebugDeviceViewModel.swift +++ b/SiliconLabsApp/ViewModels/DebugDeviceViewModel.swift @@ -80,7 +80,7 @@ final class DebugDeviceViewModel: NSObject { var peripheralDisconnectedMessage: String? { guard let peripheral = connectedPeripheral else { return nil } - let peripheralName = peripheral.name ?? "Unknown" + let peripheralName = peripheral.name ?? DefaultDeviceName return "Disconnected from \(peripheralName)" } diff --git a/SiliconLabsApp/ViewModels/SILAdvertisementDataViewModel.m b/SiliconLabsApp/ViewModels/SILAdvertisementDataViewModel.m index fb7d5954..7b148849 100644 --- a/SiliconLabsApp/ViewModels/SILAdvertisementDataViewModel.m +++ b/SiliconLabsApp/ViewModels/SILAdvertisementDataViewModel.m @@ -9,11 +9,18 @@ #import "SILAdvertisementDataViewModel.h" #import "SILBluetoothModelManager.h" -NSString * const kAdModelTypeUUID = @"UUID"; -NSString * const kAdModelTypeName = @"LOCAL NAME"; -NSString * const kAdModelTypePower = @"POWER LEVEL"; -NSString * const kAdModelTypeMajor = @"MAJOR"; -NSString * const kAdModelTypeMinor = @"MINOR"; +NSString * const kAdModelTypeAdvertisedServiceUUIDs16Bit = @"List of 16-bit Advertised Service UUIDs"; +NSString * const kAdModelTypeAdvertisedServiceUUIDs32Bit = @"List of 32-bit Advertised Service UUIDs"; +NSString * const kAdModelTypeAdvertisedServiceUUIDs128Bit = @"List of 128-bit Advertised Service UUIDs"; +NSString * const kAdModelTypeSolicitedServiceUUIDs16Bit = @"List of 16-bit Solicited Service UUIDs"; +NSString * const kAdModelTypeSolicitedServiceUUIDs128Bit = @"List of 128-bit Solicited Service UUIDs"; +NSString * const kAdModelTypeName = @"Complete Local Name"; +NSString * const kAdModelTypeTXPowerLevel = @"TX Power Level"; +NSString * const kAdModelTypeManufacturerData = @"Manufacturer Specific Data"; +NSString * const kAdModelTypeDataServiceData = @"Data Service Data"; +NSString * const kAdModelTypeIBeacon = @"iBeacon Data"; +NSString * const kAdModelTypeAltBeacon = @"AltBeacon Data"; +NSString * const kAdModelTypeEddystoneBeacon = @"Eddystone Data"; @interface SILAdvertisementDataViewModel () @@ -55,34 +62,48 @@ - (NSString *)typeString { - (NSString *)valueStringForType:(AdModelType)type { NSString *valueString; - if (type == AdModelTypeServiceUUID) { - valueString = [NSString stringWithFormat:@"0X%@", _advertisementDataModel.value]; - } else { - valueString = _advertisementDataModel.value; - } + valueString = _advertisementDataModel.value; return valueString; } - (NSString *)typeStringForType:(AdModelType)type { NSString *typeString; switch (type) { - case AdModelTypeName: + case AdModelTypeCompleteLocalName: typeString = kAdModelTypeName; break; - case AdModelTypeMajor: - typeString = kAdModelTypeMajor; + case AdModelTypeSolicitedServiceUUIDs16Bit: + typeString = kAdModelTypeSolicitedServiceUUIDs16Bit; + break; + case AdModelTypeSolicitedServiceUUIDs128Bit: + typeString = kAdModelTypeSolicitedServiceUUIDs128Bit; + break; + case AdModelTypeTXPowerLevel: + typeString = kAdModelTypeTXPowerLevel; + break; + case AdModelTypeManufacturerData: + typeString = kAdModelTypeManufacturerData; + break; + case AdModelTypeAdvertisedServiceUUIDs16Bit: + typeString = kAdModelTypeAdvertisedServiceUUIDs16Bit; break; - case AdModelTypeUUID: - typeString = kAdModelTypeUUID; + case AdModelTypeAdvertisedServiceUUIDs32Bit: + typeString = kAdModelTypeAdvertisedServiceUUIDs32Bit; break; - case AdModelTypePower: - typeString = kAdModelTypePower; + case AdModelTypeAdvertisedServiceUUIDs128Bit: + typeString = kAdModelTypeAdvertisedServiceUUIDs128Bit; break; - case AdModelTypeMinor: - typeString = kAdModelTypeMinor; + case AdModelTypeDataServiceData: + typeString = kAdModelTypeDataServiceData; break; - case AdModelTypeServiceUUID: - typeString = [self typeStringForServiceUUIDValue:_advertisementDataModel.value]; + case AdModelTypeIBeacon: + typeString = kAdModelTypeIBeacon; + break; + case AdModelTypeAltBeacon: + typeString = kAdModelTypeAltBeacon; + break; + case AdModelTypeEddystoneBeacon: + typeString = kAdModelTypeEddystoneBeacon; break; default: break; @@ -90,9 +111,5 @@ - (NSString *)typeStringForType:(AdModelType)type { return typeString; } -- (NSString *)typeStringForServiceUUIDValue:(NSString *)value { - NSString *typeString = [[SILBluetoothModelManager sharedManager] serviceModelForUUIDString:value].name; - return typeString != nil ? typeString : @"UNKNOWN SERVICE"; -} - @end + diff --git a/SiliconLabsApp/ViewModels/SILBrowserFilterViewModel.h b/SiliconLabsApp/ViewModels/SILBrowserFilterViewModel.h index 7676cc6a..049b42ce 100644 --- a/SiliconLabsApp/ViewModels/SILBrowserFilterViewModel.h +++ b/SiliconLabsApp/ViewModels/SILBrowserFilterViewModel.h @@ -15,7 +15,6 @@ NS_ASSUME_NONNULL_BEGIN @interface SILBrowserFilterViewModel : NSObject @property (strong, nonatomic, readwrite) NSString *searchByDeviceName; -@property (strong, nonatomic, readwrite) NSString *searchByRawAdvertisingData; @property (nonatomic, readwrite) NSInteger dBmValue; @property (nonatomic, readwrite) BOOL isBeaconTypeExpanded; @property (nonatomic, readwrite) BOOL isFavouriteFilterSet; diff --git a/SiliconLabsApp/ViewModels/SILBrowserFilterViewModel.m b/SiliconLabsApp/ViewModels/SILBrowserFilterViewModel.m index a0a6effd..b0b6451a 100644 --- a/SiliconLabsApp/ViewModels/SILBrowserFilterViewModel.m +++ b/SiliconLabsApp/ViewModels/SILBrowserFilterViewModel.m @@ -53,7 +53,6 @@ - (void)installRealmDatabase { - (void)setDefaultValues { self.searchByDeviceName = EmptyText; - self.searchByRawAdvertisingData = EmptyText; self.dBmValue = DefaultDBMValue; self.isBeaconTypeExpanded = NO; self.isFavouriteFilterSet = NO; @@ -69,7 +68,6 @@ - (void)setDefaultValues { - (void)clearViewModelData { self.searchByDeviceName = EmptyText; - self.searchByRawAdvertisingData = EmptyText; self.dBmValue = DefaultDBMValue; self.isConnectableFilterSet = NO; self.isFavouriteFilterSet = NO; @@ -81,7 +79,6 @@ - (void)clearViewModelData { - (void)returnStateFrom:(SILBrowserSavedSearches*)savedSearch { self.searchByDeviceName = savedSearch.searchByDeviceNameText; - self.searchByRawAdvertisingData = savedSearch.searchByRawAdvetisingDataText; self.dBmValue = savedSearch.dBmValue; self.isFavouriteFilterSet = savedSearch.isFavourite; self.isConnectableFilterSet = savedSearch.isConnectable; @@ -168,13 +165,6 @@ - (void)setSearchByDeviceName:(NSString *)searchByDeviceName { } } -- (void)setSearchByRawAdvertisingData:(NSString *)searchByRawAdvertisingData { - if (_searchByRawAdvertisingData != searchByRawAdvertisingData) { - _searchByRawAdvertisingData = searchByRawAdvertisingData; - [self changeStateOfIsActiveFilterFromSavedSearchesIfNeeded]; - } -} - - (void)setDBmValue:(NSInteger)dBmValue { if (_dBmValue != dBmValue) { _dBmValue = dBmValue; @@ -206,7 +196,7 @@ - (void)changeStateOfIsActiveFilterFromSavedSearchesIfNeeded { #pragma mark - Saving Search - (void)saveCurrentFilterDataToSavedSearches { - SILBrowserSavedSearches* saveSearch = [[SILBrowserSavedSearches alloc] initWithSearchByDeviceNameText:_searchByDeviceName searchByRawAdveritisingDataText:_searchByRawAdvertisingData dBmValue:_dBmValue beaconTypes:_beaconTypes isFavourite:_isFavouriteFilterSet isConnectable:_isConnectableFilterSet andIsSelected:NO]; + SILBrowserSavedSearches* saveSearch = [[SILBrowserSavedSearches alloc] initWithSearchByDeviceNameText:_searchByDeviceName dBmValue:_dBmValue beaconTypes:_beaconTypes isFavourite:_isFavouriteFilterSet isConnectable:_isConnectableFilterSet andIsSelected:NO]; if ([self isUniqueSavedSearches:saveSearch]) { [_savedSearches addObject:saveSearch]; [self updateSavedSearches:[_savedSearches count] - 1]; @@ -220,7 +210,7 @@ - (void)saveCurrentFilterDataToSavedSearches { - (void)saveSearchInRealmDatabase:(SILBrowserSavedSearches*)savedSearch { SILSavedSearchesRealmModel* saveSearchRealm = [[SILSavedSearchesRealmModel alloc] init]; saveSearchRealm.searchByDeviceName = savedSearch.searchByDeviceNameText; - saveSearchRealm.searchByAdvertisingData = savedSearch.searchByRawAdvetisingDataText; + // saveSearchRealm.searchByAdvertisingData = savedSearch.searchByRawAdvetisingDataText; saveSearchRealm.dBmValue = savedSearch.dBmValue; saveSearchRealm.isFavouriteSetFilter = savedSearch.isFavourite; saveSearchRealm.isConnectableSetFilter = savedSearch.isConnectable; @@ -247,9 +237,6 @@ - (BOOL)isEqualTwoSavedSearches:(SILBrowserSavedSearches*)savedSearch andSecond: if (![savedSearch.searchByDeviceNameText isEqualToString:savedSearch2.searchByDeviceNameText]) { return NO; } - if (![savedSearch.searchByRawAdvetisingDataText isEqualToString:savedSearch2.searchByRawAdvetisingDataText]) { - return NO; - } if (savedSearch.dBmValue != savedSearch2.dBmValue) { return NO; } @@ -327,7 +314,7 @@ - (SILBrowserSavedSearches*)getSavedSearchRealmObject:(SILSavedSearchesRealmMode SILBrowserBeaconType* beacon = [[SILBrowserBeaconType alloc] initWithName:savedSearchBeacon.beaconName andSelection:savedSearchBeacon.isSelected]; [beaconTypes addObject:beacon]; } - SILBrowserSavedSearches* browserSavedSearch = [[SILBrowserSavedSearches alloc] initWithSearchByDeviceNameText:savedSearchRealm.searchByDeviceName searchByRawAdveritisingDataText:savedSearchRealm.searchByAdvertisingData dBmValue:savedSearchRealm.dBmValue beaconTypes:beaconTypes isFavourite:savedSearchRealm.isFavouriteSetFilter isConnectable:savedSearchRealm.isConnectableSetFilter andIsSelected:NO]; + SILBrowserSavedSearches* browserSavedSearch = [[SILBrowserSavedSearches alloc] initWithSearchByDeviceNameText:savedSearchRealm.searchByDeviceName dBmValue:savedSearchRealm.dBmValue beaconTypes:beaconTypes isFavourite:savedSearchRealm.isFavouriteSetFilter isConnectable:savedSearchRealm.isConnectableSetFilter andIsSelected:NO]; return browserSavedSearch; } @@ -337,9 +324,6 @@ - (BOOL)isFilterActive { if (![_searchByDeviceName isEqual:EmptyText]) { return YES; } - if (![_searchByRawAdvertisingData isEqual:EmptyText]) { - return YES; - } if (_dBmValue != DefaultDBMValue) { return YES; } @@ -389,14 +373,7 @@ - (NSString*)getStringRepresentationForObjectAtIndex:(NSUInteger)index { [stringRepresentation appendString:savedSearches.searchByDeviceNameText]; [stringRepresentation appendString:QuoteText]; } - - if (![savedSearches.searchByRawAdvetisingDataText isEqual:EmptyText]) { - [stringRepresentation appendString:SearchByPacketContent]; - [stringRepresentation appendString:QuoteText]; - [stringRepresentation appendString:savedSearches.searchByRawAdvetisingDataText]; - [stringRepresentation appendString:QuoteText]; - } - + if (savedSearches.isFavourite) { [stringRepresentation appendString:OnlyFavouritesTitle]; } diff --git a/SiliconLabsApp/ViewModels/SILDiscoveredPeripheralDisplayDataViewModel.h b/SiliconLabsApp/ViewModels/SILDiscoveredPeripheralDisplayDataViewModel.h index b9bad1be..6afa069d 100644 --- a/SiliconLabsApp/ViewModels/SILDiscoveredPeripheralDisplayDataViewModel.h +++ b/SiliconLabsApp/ViewModels/SILDiscoveredPeripheralDisplayDataViewModel.h @@ -15,7 +15,6 @@ @property (strong, nonatomic, readonly) SILDiscoveredPeripheralDisplayData *discoveredPeripheralDisplayData; @property (strong, nonatomic, readonly) NSArray *advertisementDataViewModels; -@property (strong, nonatomic, readonly) NSArray *advertisementDataViewModelsForDevicesTable; @property (strong, nonatomic, readonly) NSArray *advertisementDataViewModelsForInfoView; - (instancetype)initWithDiscoveredPeripheralDisplayData:(SILDiscoveredPeripheralDisplayData *)discoveredPeripheralDisplayData; diff --git a/SiliconLabsApp/ViewModels/SILDiscoveredPeripheralDisplayDataViewModel.m b/SiliconLabsApp/ViewModels/SILDiscoveredPeripheralDisplayDataViewModel.m index 884749ef..daccfee9 100644 --- a/SiliconLabsApp/ViewModels/SILDiscoveredPeripheralDisplayDataViewModel.m +++ b/SiliconLabsApp/ViewModels/SILDiscoveredPeripheralDisplayDataViewModel.m @@ -14,7 +14,6 @@ @interface SILDiscoveredPeripheralDisplayDataViewModel () @property (strong, nonatomic, readwrite) SILDiscoveredPeripheralDisplayData *discoveredPeripheralDisplayData; @property (strong, nonatomic, readwrite) NSArray *advertisementDataViewModels; -@property (strong, nonatomic, readwrite) NSArray *advertisementDataViewModelsForDevicesTable; @property (strong, nonatomic, readwrite) NSArray *advertisementDataViewModelsForInfoView; @end @@ -34,13 +33,6 @@ - (instancetype)initWithDiscoveredPeripheralDisplayData:(SILDiscoveredPeripheral #pragma mark - Properties -- (NSArray *)advertisementDataViewModelsForDevicesTable { - if (_advertisementDataViewModelsForDevicesTable == nil) { - _advertisementDataViewModelsForDevicesTable = [self advertisementDataViewModelsForadvertisementDataModels:_discoveredPeripheralDisplayData.advertisementDataModelsForDevicesTable]; - } - return _advertisementDataViewModelsForDevicesTable; -} - - (NSArray *)advertisementDataViewModelsForInfoView { if (_advertisementDataViewModelsForInfoView == nil) { _advertisementDataViewModelsForInfoView = [self advertisementDataViewModelsForadvertisementDataModels:_discoveredPeripheralDisplayData.advertisementDataModelsForInfoView]; diff --git a/SiliconLabsApp/Views/SILAlertBarView.h b/SiliconLabsApp/Views/SILAlertBarView.h deleted file mode 100644 index 7615528c..00000000 --- a/SiliconLabsApp/Views/SILAlertBarView.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// SILAlertBarView.h -// SiliconLabsApp -// -// Created by Eric Peterson on 10/25/15. -// Copyright © 2015 SiliconLabs. All rights reserved. -// - -#import -#import - -@interface SILAlertBarView : UIView - -@property (nonatomic) BOOL isAnimating; - -- (void)configureLabel:(UILabel *)label revealConstraint:(NSLayoutConstraint *)revealConstraint hideConstraint:(NSLayoutConstraint *)hideConstraint; -///@discussion displayTime of 0 or less means the message should stay revealed indefinitely -- (void)revealAlertBarWithMessage:(NSString *)message revealTime:(CGFloat)revealTime displayTime:(CGFloat)displayTime; -///@discussion hides the bar if revealed indefinitely, will interrupt any animations that would otherwise hide it -- (void)hideBarIfRevealed:(CGFloat)hideTime; - -@end diff --git a/SiliconLabsApp/Views/SILAlertBarView.m b/SiliconLabsApp/Views/SILAlertBarView.m deleted file mode 100644 index a707f272..00000000 --- a/SiliconLabsApp/Views/SILAlertBarView.m +++ /dev/null @@ -1,76 +0,0 @@ -// -// SILAlertBarView.m -// SiliconLabsApp -// -// Created by Eric Peterson on 10/25/15. -// Copyright © 2015 SiliconLabs. All rights reserved. -// - -#import "SILAlertBarView.h" -#import "UIColor+SILColors.h" - -const CGFloat kActivePriority = 999; -const CGFloat kMainPriority = 998; -const CGFloat kInactivePriority = 1; - -@interface SILAlertBarView() - -@property (weak, nonatomic) UILabel *alertMessageLabel; -@property (weak, nonatomic) NSLayoutConstraint *alertBarHideConstraint; -@property (weak, nonatomic) NSLayoutConstraint *alertBarRevealConstraint; - -@end - -@implementation SILAlertBarView - -- (void)awakeFromNib { - [super awakeFromNib]; - self.backgroundColor = [UIColor sil_siliconLabsRedColor]; -} - -- (void)configureLabel:(UILabel *)label revealConstraint:(NSLayoutConstraint *)revealConstraint hideConstraint:(NSLayoutConstraint *)hideConstraint { - self.alertMessageLabel = label; - self.alertBarRevealConstraint = revealConstraint; - self.alertBarHideConstraint = hideConstraint; - - self.alertBarHideConstraint.priority = kMainPriority; - self.alertBarRevealConstraint.priority = kInactivePriority; -} - -- (void)revealAlertBarWithMessage:(NSString *)message revealTime:(CGFloat)revealTime displayTime:(CGFloat)displayTime { - self.alertMessageLabel.text = message; - [self layoutIfNeeded]; - __weak SILAlertBarView *weakSelf = self; - if (!self.isAnimating) { - self.isAnimating = YES; - [UIView animateWithDuration:revealTime animations:^{ - weakSelf.alertBarRevealConstraint.priority = kActivePriority; - [weakSelf layoutIfNeeded]; - } completion:^(BOOL finished) { - if (displayTime > 0) { - [weakSelf hideBarAnimation:weakSelf duration:revealTime delay:displayTime]; - } else { - weakSelf.isAnimating = NO; - } - }]; - } -} - -- (void)hideBarIfRevealed:(CGFloat)hideTime { - [self.layer removeAllAnimations]; - [self layoutIfNeeded]; - [self hideBarAnimation:self duration:hideTime delay:0]; -} - -- (void)hideBarAnimation:(SILAlertBarView *)alertBarView duration:(CGFloat)duration delay:(CGFloat)delayTime { - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^{ - alertBarView.alertBarRevealConstraint.priority = kInactivePriority; - [alertBarView layoutIfNeeded]; - } completion:^(BOOL finished){ - alertBarView.isAnimating = NO; - }]; - }); -} - -@end diff --git a/SiliconLabsApp/XML/services/org.bluetooth.service.fitness_machine.xml b/SiliconLabsApp/XML/services/org.bluetooth.service.fitness_machine.xml new file mode 100644 index 00000000..2b100178 --- /dev/null +++ b/SiliconLabsApp/XML/services/org.bluetooth.service.fitness_machine.xml @@ -0,0 +1,338 @@ + + + + + This service exposes training-related data in the sports and fitness environment, which allows a Server (e.g., a fitness machine) to send training-related data to a Client. +

The Fitness Machine Service (FTMS) exposes training-related data in the sports and fitness environment, which allows a Client to collect training data while a user is exercising with a fitness machine (Server). + + + This service has no dependencies on other GATT-based services. + + + Mandatory + Mandatory + C.1 + Optional + + C1: Mandatory if the Fitness Machine Control Point is supported, otherwise Optional. + + true + true + true + + + + + Mandatory + + Mandatory + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + + + + + Optional + + Excluded + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + + + + Mandatory + + Mandatory + Mandatory + + + + + + + Optional + + Excluded + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + + + + Mandatory + + Mandatory + Mandatory + + + + + + + Optional + + Excluded + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + + + + Mandatory + + Mandatory + Mandatory + + + + + + + Optional + + Excluded + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + + + + Mandatory + + Mandatory + Mandatory + + + + + + + Optional + + Excluded + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + + + + Mandatory + + Mandatory + Mandatory + + + + + + + Optional + + Excluded + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + + + + Mandatory + + Mandatory + Mandatory + + + + + + + Optional + + Mandatory + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + + + + Mandatory + + Mandatory + Mandatory + + + + + + + C1 + + C1: Mandatory if the Speed Target Setting feature is supported; otherwise Optional. + Mandatory + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + + + + + C2 + + C2: Mandatory if the Inclination Target Setting feature is supported; otherwise Optional. + Mandatory + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + + + + + C3 + + C3: Mandatory if the Resistance Target Setting feature is supported; otherwise Optional. + Mandatory + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + + + + + C4 + + C4: Mandatory if the Power Target Setting feature is supported; otherwise Optional. + Mandatory + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + + + + + C5 + + C5: Mandatory if the Heart Rate Target Setting feature is supported; otherwise Optional. + Mandatory + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + + + + + + Optional + + Excluded + Mandatory + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + + + + Mandatory + + Mandatory + Mandatory + + + + + + + C6 + + C6: Mandatory if the Fitness Machine Control Point is supported; otherwise Optional. + Excluded + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + + + + Mandatory + + Mandatory + Mandatory + + + + + + diff --git a/SiliconLabsApp/XML/services/org.bluetooth.service.insulin_delivery.xml b/SiliconLabsApp/XML/services/org.bluetooth.service.insulin_delivery.xml new file mode 100644 index 00000000..5e7e45d9 --- /dev/null +++ b/SiliconLabsApp/XML/services/org.bluetooth.service.insulin_delivery.xml @@ -0,0 +1,292 @@ + + + + + + This service exposes the control capability, the status of an Insulin Delivery Device (IDD) running an insulin infusion therapy, and historical therapy data to be used in the personal and professional healthcare industry. + + + The Insulin Delivery service is instantiated as a Primary Service. + + + + This service is not dependent upon any other services. + + + Mandatory + Mandatory + Mandatory + Mandatory + Mandatory + + + false + true + + + + + + + + + The IDD Status Changed characteristic is comprised of status changes of the insulin therapy and the insulin delivery device. + + Mandatory + + Mandatory + Excluded + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + + + Authorization + + + + Mandatory + + Mandatory + Mandatory + + + + + + + The IDD Status characteristic is comprised of status values of the insulin delivery device and the insulin therapy. + + Mandatory + + Mandatory + Excluded + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + + + Authorization + + + + Mandatory + + Mandatory + Mandatory + + + + + + + The IDD Annunciation Status characteristic is a variable length structure comprising of messages that describe state changes of the insulin delivery device and in the therapy relevant functions. + + Mandatory + + Mandatory + Excluded + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + + + Authorization + + + + Mandatory + + Mandatory + Mandatory + + + + + + + The IDD Features characteristic shall be used to describe the supported features of the Server. + + Mandatory + + Mandatory + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + + + Authorization + + + + + The IDD Status Reader Control Point provides insulin therapy relevant status information (e.g., currently running boluses or current basal rate). + + Mandatory + + Excluded + Mandatory + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + + + Authorization + + + + Mandatory + + Mandatory + Mandatory + + + + + + + The IDD Command Control Point provides procedures to support the insulin therapy by adapting therapy parameters to operate the insulin therapy remotely and to perform a remote operation of the device maintenance. + + Optional + + Excluded + Mandatory + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + + + Authorization + + + + Mandatory + + Mandatory + Mandatory + + + + + + + The IDD Command Data characteristic is comprised of response records from executed procedures of the IDD Command Control Point. + + Conditional + + + Mandatory if the optional IDD Command Control Point is included, otherwise excluded. + + Excluded + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + + + Authorization + + + + Mandatory + + Mandatory + Mandatory + + + + + + + The IDD Record Access Control Point (IDD RACP) is based on the Record Access Control Point (RACP). The IDD RACP is used for basic management and access of the history database of the Server including historical data of the insulin therapy, device state changes, and annunciations. + + Optional + + Excluded + Mandatory + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + + + Authorization + + + + Read Property: Readable with no authentication or authorization already defined in Bluetooth Core Specification v4.0 as amended by CSA3 and CSS v2 or later + Mandatory + + Mandatory + Mandatory + + + + + + + The IDD History Data characteristic is comprised of response records from executed procedures of the Record Access Control Point. + + Conditional + + + Mandatory if the optional IDD Record Access Control Point is included, otherwise excluded. + + Excluded + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + + + Authorization + + + + Mandatory + + Mandatory + Mandatory + + + + + + diff --git a/SiliconLabsApp/XML/services/org.bluetooth.service.internet_protocol_support.xml b/SiliconLabsApp/XML/services/org.bluetooth.service.internet_protocol_support.xml new file mode 100644 index 00000000..94f76f29 --- /dev/null +++ b/SiliconLabsApp/XML/services/org.bluetooth.service.internet_protocol_support.xml @@ -0,0 +1,22 @@ + + + + + + When in a GAP Discoverable Mode for an initial connection to a Router, the Node will include the IP Support Service UUID 0x1820 in the Service UUIDs AD type field + of the advertising data. This enhances the user experience as a Node may be identified by the Router before initiating a connection. + + This service does not define any characteristics + + + + This service has no dependencies on other GATT-based services. + + + + false + true + + + diff --git a/SiliconLabsApp/XML/services/org.bluetooth.service.mesh_provisioning.xml b/SiliconLabsApp/XML/services/org.bluetooth.service.mesh_provisioning.xml new file mode 100644 index 00000000..a068f459 --- /dev/null +++ b/SiliconLabsApp/XML/services/org.bluetooth.service.mesh_provisioning.xml @@ -0,0 +1,64 @@ + + + + + + The Mesh Provisioning Service allows a Provisioning Client to provision a Provisioning Server to allow it to participate in the mesh network. + + + + + This service has no dependencies on other GATT-based services. + + + Mandatory + Mandatory + Mandatory + + + false + true + + + + The Mesh Provisioning Data In characteristic can be written to send a Proxy PDU message containing Provisioning PDU to the Provisioning Server. The characteristic value is 66 octets long to accommodate the longest known Proxy PDU containing Provisioning PDU. + Mandatory + + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + + + + + The Mesh Provisioning Data Out characteristic can be notified to send a Proxy PDU message containing Provisioning PDU from a Provisioning Server to a Provisioning Client. The characteristic value is 66 octets long to accommodate the longest known Proxy PDU message containing Provisioning PDU. + Mandatory + + Excluded + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + + + + Mandatory + + Mandatory + Mandatory + + + + + + diff --git a/SiliconLabsApp/XML/services/org.bluetooth.service.mesh_proxy.xml b/SiliconLabsApp/XML/services/org.bluetooth.service.mesh_proxy.xml new file mode 100644 index 00000000..8188455c --- /dev/null +++ b/SiliconLabsApp/XML/services/org.bluetooth.service.mesh_proxy.xml @@ -0,0 +1,64 @@ + + + + + + The Mesh Proxy Service is used to enable a server to send and receive Proxy PDUs with a client. + + + + + This service has no dependencies on other GATT-based services. + + + Mandatory + Mandatory + Mandatory + + + false + true + + + + The Mesh Proxy Data In characteristic is used by the client to send Proxy PDUs to the server. + Mandatory + + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + Excluded + Excluded + Excluded + + + + + The Mesh Proxy Data Out characteristic is used by the server to send Proxy PDUs to the client. The Mesh Proxy Data Out characteristic shall support Proxy PDU messages containing Network PDUs, mesh beacon, and proxy configuration messages and shall not support other Proxy PDU type messages. + Mandatory + + Excluded + Excluded + Excluded + Excluded + Excluded + Mandatory + Excluded + Excluded + Excluded + + + + Mandatory + + Mandatory + Mandatory + + + + + + diff --git a/SiliconLabsApp/XML/services/org.bluetooth.service.reconnection_configuration.xml b/SiliconLabsApp/XML/services/org.bluetooth.service.reconnection_configuration.xml new file mode 100644 index 00000000..392800d2 --- /dev/null +++ b/SiliconLabsApp/XML/services/org.bluetooth.service.reconnection_configuration.xml @@ -0,0 +1,13 @@ + + This GATT-based service enables the control of certain communication parameters of a Bluetooth Low Energy peripheral device. + This service is not dependent upon any other services but is intended to be used together with other services.MandatoryC1: Mandatory if Ready for Disconnect is supported, otherwise Optional.C2: Mandatory if Reconnection Configuration Control Point (RCCP) procedures are supported, otherwise Excluded.MandatoryMandatoryfalsetrue + The RC Features characteristic shall be used to describe the supported features of the Reconnection Configuration server. + MandatoryMandatoryExcludedExcludedExcludedExcludedExcludedExcludedExcludedExcludedExcluded + The RC Settings characteristic shall be used to both read and notify supported features on the Reconnection Configuration server. + C1 + C1: Mandatory if device supports one or more of the following features: "Ready for Disconnect", "Advertisement Configuration", "Upgrade to LESC Only", "Next Pairing OOB", "Limited Access", otherwise Excluded. + + Notify is Mandatory if device supports "Enable Disconnect" feature, otherwise Optional. + MandatoryExcludedExcludedExcludedExcludedOptionalExcludedExcludedExcludedExcludedC2: Mandatory if device supports "Ready for Disconnect" feature, otherwise Excluded.MandatoryMandatoryC3 + C3: Mandatory if device supports one or more of the following features: "Enable Disconnect", "Propose Reconnection Timeout", "Propose Connection Interval", "Propose Slave Latency", "Propose Supervision Timeout", "Propose Advertisement Interval", "Propose Advertisement Count", "Propose Advertisement Repetition Time", "Advertisement Configuration 1", "Advertisement Configuration 2", "Advertisement Configuration 3", "Advertisement Configuration 4", "Upgrade to LESC Only", "Next Pairing OOB", "Use of White List", "Limited Access", otherwise Excluded. + MandatoryMandatoryExcludedExcludedExcludedExcludedMandatoryExcludedExcludedExcludedC3: Mandatory if device supports one or more of the following features: "Enable Disconnect", "Propose Reconnection Timeout", "Propose Connection Interval", "Propose Slave Latency", "Propose Supervision Timeout", "Propose Advertisement Interval", "Propose Advertisement Count", "Propose Advertisement Repetition Time", "Advertisement Configuration 1", "Advertisement Configuration 2", "Advertisement Configuration 3", "Advertisement Configuration 4", "Upgrade to LESC Only", "Next Pairing OOB", "Use of White List", "Limited Access", otherwise Excluded.MandatoryMandatory \ No newline at end of file