Compare commits

...

1 commit

Author SHA1 Message Date
Trev_Knows
f6d10619ff Version 1.1.0
- Puppet Module
2020-02-04 14:25:18 -05:00
71 changed files with 1505 additions and 2502 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View file

@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
2981E3B523C6526C00974EA2 /* Sparky_Gold1.dae in Resources */ = {isa = PBXBuildFile; fileRef = 2981E3B423C6526C00974EA2 /* Sparky_Gold1.dae */; };
A901EC48237C43C000687BE6 /* Data+LittleEndianTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = A901EC47237C43C000687BE6 /* Data+LittleEndianTypes.swift */; }; A901EC48237C43C000687BE6 /* Data+LittleEndianTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = A901EC47237C43C000687BE6 /* Data+LittleEndianTypes.swift */; };
A901EC4D237C47D800687BE6 /* CPBDataSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = A901EC4C237C47D800687BE6 /* CPBDataSeries.swift */; }; A901EC4D237C47D800687BE6 /* CPBDataSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = A901EC4C237C47D800687BE6 /* CPBDataSeries.swift */; };
A92B9F0D234DE4A2002374F0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92B9F0C234DE4A2002374F0 /* AppDelegate.swift */; }; A92B9F0D234DE4A2002374F0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92B9F0C234DE4A2002374F0 /* AppDelegate.swift */; };
@ -177,16 +178,6 @@
A9BED77A234FBEAB002FFF53 /* CommandQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BED763234FBEAB002FFF53 /* CommandQueue.swift */; }; A9BED77A234FBEAB002FFF53 /* CommandQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BED763234FBEAB002FFF53 /* CommandQueue.swift */; };
A9BED77B234FBEAB002FFF53 /* LogHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BED764234FBEAB002FFF53 /* LogHelper.swift */; }; A9BED77B234FBEAB002FFF53 /* LogHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BED764234FBEAB002FFF53 /* LogHelper.swift */; };
A9BED77D234FBEAB002FFF53 /* Data+ScanValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BED766234FBEAB002FFF53 /* Data+ScanValue.swift */; }; A9BED77D234FBEAB002FFF53 /* Data+ScanValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BED766234FBEAB002FFF53 /* Data+ScanValue.swift */; };
A9D12E8423E3661800F3C259 /* UIColor+Interpolate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D12E8323E3661800F3C259 /* UIColor+Interpolate.swift */; };
A9D12E8623E3C5CF00F3C259 /* LightChartPanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D12E8523E3C5CF00F3C259 /* LightChartPanelViewController.swift */; };
A9D12E8723E3C5CF00F3C259 /* LightChartPanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D12E8523E3C5CF00F3C259 /* LightChartPanelViewController.swift */; };
A9D12E8823E4434000F3C259 /* UIColor+Interpolate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D12E8323E3661800F3C259 /* UIColor+Interpolate.swift */; };
A9D5B21023E0985600178E1B /* AutoConnectViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D5B20F23E0985600178E1B /* AutoConnectViewController.swift */; };
A9D5B21123E0985600178E1B /* AutoConnectViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D5B20F23E0985600178E1B /* AutoConnectViewController.swift */; };
A9D5B21323E25C1600178E1B /* ActiveLabelIntrinsicSizeFix.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D5B21223E25C1600178E1B /* ActiveLabelIntrinsicSizeFix.swift */; };
A9D5B21423E25C1600178E1B /* ActiveLabelIntrinsicSizeFix.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D5B21223E25C1600178E1B /* ActiveLabelIntrinsicSizeFix.swift */; };
A9D5B21623E3024700178E1B /* BluetoothStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D5B21523E3024700178E1B /* BluetoothStatusViewController.swift */; };
A9D5B21723E3024800178E1B /* BluetoothStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D5B21523E3024700178E1B /* BluetoothStatusViewController.swift */; };
A9DF06122359B0600094327F /* ButtonStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DF06112359B0600094327F /* ButtonStatusViewController.swift */; }; A9DF06122359B0600094327F /* ButtonStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DF06112359B0600094327F /* ButtonStatusViewController.swift */; };
A9DF06142359B31C0094327F /* ButtonStatusPanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DF06132359B31C0094327F /* ButtonStatusPanelViewController.swift */; }; A9DF06142359B31C0094327F /* ButtonStatusPanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DF06132359B31C0094327F /* ButtonStatusPanelViewController.swift */; };
A9E4C02E2358E6B400B5A493 /* UIColor+LightAndDark.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E4C02D2358E6B400B5A493 /* UIColor+LightAndDark.swift */; }; A9E4C02E2358E6B400B5A493 /* UIColor+LightAndDark.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E4C02D2358E6B400B5A493 /* UIColor+LightAndDark.swift */; };
@ -222,6 +213,7 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
02A29BC776CC7DA5C23560A7 /* Pods-BluefruitPlayground.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BluefruitPlayground.release.xcconfig"; path = "Target Support Files/Pods-BluefruitPlayground/Pods-BluefruitPlayground.release.xcconfig"; sourceTree = "<group>"; }; 02A29BC776CC7DA5C23560A7 /* Pods-BluefruitPlayground.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BluefruitPlayground.release.xcconfig"; path = "Target Support Files/Pods-BluefruitPlayground/Pods-BluefruitPlayground.release.xcconfig"; sourceTree = "<group>"; };
02C55600113CD967F7C0C908 /* Pods-CPX.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPX.debug.xcconfig"; path = "Target Support Files/Pods-CPX/Pods-CPX.debug.xcconfig"; sourceTree = "<group>"; }; 02C55600113CD967F7C0C908 /* Pods-CPX.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPX.debug.xcconfig"; path = "Target Support Files/Pods-CPX/Pods-CPX.debug.xcconfig"; sourceTree = "<group>"; };
2981E3B423C6526C00974EA2 /* Sparky_Gold1.dae */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml.dae; path = Sparky_Gold1.dae; sourceTree = "<group>"; };
3348DDE33D89A5959702CAC9 /* Pods-BluefruitPlayground.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BluefruitPlayground.debug.xcconfig"; path = "Target Support Files/Pods-BluefruitPlayground/Pods-BluefruitPlayground.debug.xcconfig"; sourceTree = "<group>"; }; 3348DDE33D89A5959702CAC9 /* Pods-BluefruitPlayground.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BluefruitPlayground.debug.xcconfig"; path = "Target Support Files/Pods-BluefruitPlayground/Pods-BluefruitPlayground.debug.xcconfig"; sourceTree = "<group>"; };
41FC984EE1CDB9B54C5084ED /* Pods-CPX.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPX.release.xcconfig"; path = "Target Support Files/Pods-CPX/Pods-CPX.release.xcconfig"; sourceTree = "<group>"; }; 41FC984EE1CDB9B54C5084ED /* Pods-CPX.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CPX.release.xcconfig"; path = "Target Support Files/Pods-CPX/Pods-CPX.release.xcconfig"; sourceTree = "<group>"; };
8BFE7B5A63DA548F17D0945D /* Pods_BluefruitPlayground.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BluefruitPlayground.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8BFE7B5A63DA548F17D0945D /* Pods_BluefruitPlayground.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BluefruitPlayground.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -318,11 +310,6 @@
A9BED763234FBEAB002FFF53 /* CommandQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandQueue.swift; sourceTree = "<group>"; }; A9BED763234FBEAB002FFF53 /* CommandQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandQueue.swift; sourceTree = "<group>"; };
A9BED764234FBEAB002FFF53 /* LogHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogHelper.swift; sourceTree = "<group>"; }; A9BED764234FBEAB002FFF53 /* LogHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogHelper.swift; sourceTree = "<group>"; };
A9BED766234FBEAB002FFF53 /* Data+ScanValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+ScanValue.swift"; sourceTree = "<group>"; }; A9BED766234FBEAB002FFF53 /* Data+ScanValue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+ScanValue.swift"; sourceTree = "<group>"; };
A9D12E8323E3661800F3C259 /* UIColor+Interpolate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Interpolate.swift"; sourceTree = "<group>"; };
A9D12E8523E3C5CF00F3C259 /* LightChartPanelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LightChartPanelViewController.swift; sourceTree = "<group>"; };
A9D5B20F23E0985600178E1B /* AutoConnectViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoConnectViewController.swift; sourceTree = "<group>"; };
A9D5B21223E25C1600178E1B /* ActiveLabelIntrinsicSizeFix.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveLabelIntrinsicSizeFix.swift; sourceTree = "<group>"; };
A9D5B21523E3024700178E1B /* BluetoothStatusViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothStatusViewController.swift; sourceTree = "<group>"; };
A9DF06112359B0600094327F /* ButtonStatusViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonStatusViewController.swift; sourceTree = "<group>"; }; A9DF06112359B0600094327F /* ButtonStatusViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonStatusViewController.swift; sourceTree = "<group>"; };
A9DF06132359B31C0094327F /* ButtonStatusPanelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonStatusPanelViewController.swift; sourceTree = "<group>"; }; A9DF06132359B31C0094327F /* ButtonStatusPanelViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonStatusPanelViewController.swift; sourceTree = "<group>"; };
A9E4C02D2358E6B400B5A493 /* UIColor+LightAndDark.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+LightAndDark.swift"; sourceTree = "<group>"; }; A9E4C02D2358E6B400B5A493 /* UIColor+LightAndDark.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+LightAndDark.swift"; sourceTree = "<group>"; };
@ -491,7 +478,6 @@
children = ( children = (
A986AB92235942D600DF12C8 /* LightSensorViewController.swift */, A986AB92235942D600DF12C8 /* LightSensorViewController.swift */,
A98F1B8A23594BE400C5269E /* LightSensorPanelViewController.swift */, A98F1B8A23594BE400C5269E /* LightSensorPanelViewController.swift */,
A9D12E8523E3C5CF00F3C259 /* LightChartPanelViewController.swift */,
); );
path = LightSensor; path = LightSensor;
sourceTree = "<group>"; sourceTree = "<group>";
@ -500,6 +486,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A98E4D3123566785005B166D /* cpb.scn */, A98E4D3123566785005B166D /* cpb.scn */,
2981E3B423C6526C00974EA2 /* Sparky_Gold1.dae */,
A96AA5BA235F903D004A06BA /* CPB_PcbSurface_Color.png */, A96AA5BA235F903D004A06BA /* CPB_PcbSurface_Color.png */,
); );
path = models3d; path = models3d;
@ -546,8 +533,6 @@
A9BED700234F754B002FFF53 /* Utils */ = { A9BED700234F754B002FFF53 /* Utils */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A99BA722235000520080D03F /* ScreenFlowManager.swift */,
A9BED70C234F76C5002FFF53 /* LocalizationManager.swift */,
A9716BA92350128100CB174D /* RssiUI.swift */, A9716BA92350128100CB174D /* RssiUI.swift */,
A96AA5D2236358A8004A06BA /* UIView+RoundedCorners.swift */, A96AA5D2236358A8004A06BA /* UIView+RoundedCorners.swift */,
A96AA5D423635A89004A06BA /* NSLayoutConstraint+ChangeMultiplier.swift */, A96AA5D423635A89004A06BA /* NSLayoutConstraint+ChangeMultiplier.swift */,
@ -558,10 +543,11 @@
A96AA5C0236050E4004A06BA /* NavigationBarWithScrollAwareRightButton.swift */, A96AA5C0236050E4004A06BA /* NavigationBarWithScrollAwareRightButton.swift */,
A9BED701234F754B002FFF53 /* GradientView.swift */, A9BED701234F754B002FFF53 /* GradientView.swift */,
A96AA5D023634F03004A06BA /* GridView.swift */, A96AA5D023634F03004A06BA /* GridView.swift */,
A9BED70C234F76C5002FFF53 /* LocalizationManager.swift */,
A99BA722235000520080D03F /* ScreenFlowManager.swift */,
A9B3569E2365D420002093EE /* PanelInsetView.swift */, A9B3569E2365D420002093EE /* PanelInsetView.swift */,
A958E77D238B7457006A225A /* NormalBrightnessSliderControl.swift */, A958E77D238B7457006A225A /* NormalBrightnessSliderControl.swift */,
A958E77F238B75D9006A225A /* NormalBrightnessColorPickerController.swift */, A958E77F238B75D9006A225A /* NormalBrightnessColorPickerController.swift */,
A9D12E8323E3661800F3C259 /* UIColor+Interpolate.swift */,
); );
path = Utils; path = Utils;
sourceTree = "<group>"; sourceTree = "<group>";
@ -633,19 +619,10 @@
A9BED766234FBEAB002FFF53 /* Data+ScanValue.swift */, A9BED766234FBEAB002FFF53 /* Data+ScanValue.swift */,
A901EC47237C43C000687BE6 /* Data+LittleEndianTypes.swift */, A901EC47237C43C000687BE6 /* Data+LittleEndianTypes.swift */,
A9BDEC40237C75C900EDDC46 /* Types+Data.swift */, A9BDEC40237C75C900EDDC46 /* Types+Data.swift */,
A9D5B21223E25C1600178E1B /* ActiveLabelIntrinsicSizeFix.swift */,
); );
path = Utils; path = Utils;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
A9D5B20E23E0983F00178E1B /* AutoConnect */ = {
isa = PBXGroup;
children = (
A9D5B20F23E0985600178E1B /* AutoConnectViewController.swift */,
);
path = AutoConnect;
sourceTree = "<group>";
};
A9DF06102359B04C0094327F /* ButtonStatus */ = { A9DF06102359B04C0094327F /* ButtonStatus */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -692,10 +669,8 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A9BED716234FBDBA002FFF53 /* StartupViewController.swift */, A9BED716234FBDBA002FFF53 /* StartupViewController.swift */,
A9D5B21523E3024700178E1B /* BluetoothStatusViewController.swift */,
A9482988237B73B100B6FC13 /* Common */, A9482988237B73B100B6FC13 /* Common */,
A9BED703234F75E4002FFF53 /* Tips */, A9BED703234F75E4002FFF53 /* Tips */,
A9D5B20E23E0983F00178E1B /* AutoConnect */,
A9EAA18D2350B26B00FA615E /* Scanner */, A9EAA18D2350B26B00FA615E /* Scanner */,
A9EAA1982351F1DD00FA615E /* Home */, A9EAA1982351F1DD00FA615E /* Home */,
A9EAA19F2352973400FA615E /* Neopixels */, A9EAA19F2352973400FA615E /* Neopixels */,
@ -826,6 +801,7 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
2981E3B523C6526C00974EA2 /* Sparky_Gold1.dae in Resources */,
A98E4D3223566785005B166D /* cpb.scn in Resources */, A98E4D3223566785005B166D /* cpb.scn in Resources */,
A92B9F19234DE4A5002374F0 /* LaunchScreen.storyboard in Resources */, A92B9F19234DE4A5002374F0 /* LaunchScreen.storyboard in Resources */,
A99BA71F234FFECF0080D03F /* DefaultPreferences.plist in Resources */, A99BA71F234FFECF0080D03F /* DefaultPreferences.plist in Resources */,
@ -957,7 +933,6 @@
A92B9F0D234DE4A2002374F0 /* AppDelegate.swift in Sources */, A92B9F0D234DE4A2002374F0 /* AppDelegate.swift in Sources */,
A96AA5C6236312CE004A06BA /* ToneGeneratorViewController.swift in Sources */, A96AA5C6236312CE004A06BA /* ToneGeneratorViewController.swift in Sources */,
A9BED77D234FBEAB002FFF53 /* Data+ScanValue.swift in Sources */, A9BED77D234FBEAB002FFF53 /* Data+ScanValue.swift in Sources */,
A9D12E8623E3C5CF00F3C259 /* LightChartPanelViewController.swift in Sources */,
A901EC48237C43C000687BE6 /* Data+LittleEndianTypes.swift in Sources */, A901EC48237C43C000687BE6 /* Data+LittleEndianTypes.swift in Sources */,
A980FE6D2357D14400E3A909 /* TipPowerUpViewController.swift in Sources */, A980FE6D2357D14400E3A909 /* TipPowerUpViewController.swift in Sources */,
A94AC14E2364A82B0062AB11 /* CPBBle.swift in Sources */, A94AC14E2364A82B0062AB11 /* CPBBle.swift in Sources */,
@ -990,7 +965,6 @@
A958E782238B7936006A225A /* BlePeripheral+CPBAccelerometer.swift in Sources */, A958E782238B7936006A225A /* BlePeripheral+CPBAccelerometer.swift in Sources */,
A9DF06122359B0600094327F /* ButtonStatusViewController.swift in Sources */, A9DF06122359B0600094327F /* ButtonStatusViewController.swift in Sources */,
A96AA5D3236358A8004A06BA /* UIView+RoundedCorners.swift in Sources */, A96AA5D3236358A8004A06BA /* UIView+RoundedCorners.swift in Sources */,
A9D5B21023E0985600178E1B /* AutoConnectViewController.swift in Sources */,
A98E4D2823565B76005B166D /* TipWelcomeViewController.swift in Sources */, A98E4D2823565B76005B166D /* TipWelcomeViewController.swift in Sources */,
A9BED70B234F7617002FFF53 /* Config.swift in Sources */, A9BED70B234F7617002FFF53 /* Config.swift in Sources */,
A980FE6F2357D15400E3A909 /* TipDiscoverViewController.swift in Sources */, A980FE6F2357D15400E3A909 /* TipDiscoverViewController.swift in Sources */,
@ -1006,7 +980,6 @@
A98F1B8B23594BE400C5269E /* LightSensorPanelViewController.swift in Sources */, A98F1B8B23594BE400C5269E /* LightSensorPanelViewController.swift in Sources */,
A96AA5C32362F488004A06BA /* AboutViewController.swift in Sources */, A96AA5C32362F488004A06BA /* AboutViewController.swift in Sources */,
A9A08BAF23A7957700498069 /* BlePeripheral+CPBButtons.swift in Sources */, A9A08BAF23A7957700498069 /* BlePeripheral+CPBButtons.swift in Sources */,
A9D12E8423E3661800F3C259 /* UIColor+Interpolate.swift in Sources */,
A98F1B892359494500C5269E /* ConfigUI.swift in Sources */, A98F1B892359494500C5269E /* ConfigUI.swift in Sources */,
A9BED773234FBEAB002FFF53 /* UartPacketManager.swift in Sources */, A9BED773234FBEAB002FFF53 /* UartPacketManager.swift in Sources */,
A986AB93235942D600DF12C8 /* LightSensorViewController.swift in Sources */, A986AB93235942D600DF12C8 /* LightSensorViewController.swift in Sources */,
@ -1025,8 +998,6 @@
A9EAA1A3235297B400FA615E /* NeopixelsLightSequenceViewController.swift in Sources */, A9EAA1A3235297B400FA615E /* NeopixelsLightSequenceViewController.swift in Sources */,
A901EC4D237C47D800687BE6 /* CPBDataSeries.swift in Sources */, A901EC4D237C47D800687BE6 /* CPBDataSeries.swift in Sources */,
A99BA71D234FFE780080D03F /* Settings.swift in Sources */, A99BA71D234FFE780080D03F /* Settings.swift in Sources */,
A9D5B21323E25C1600178E1B /* ActiveLabelIntrinsicSizeFix.swift in Sources */,
A9D5B21623E3024700178E1B /* BluetoothStatusViewController.swift in Sources */,
A9BED775234FBEAB002FFF53 /* BlePeripheral+Battery.swift in Sources */, A9BED775234FBEAB002FFF53 /* BlePeripheral+Battery.swift in Sources */,
A94AC1482363A7BD0062AB11 /* TemperaturePanelViewController.swift in Sources */, A94AC1482363A7BD0062AB11 /* TemperaturePanelViewController.swift in Sources */,
A9EAA1912350B26B00FA615E /* ScannerViewController.swift in Sources */, A9EAA1912350B26B00FA615E /* ScannerViewController.swift in Sources */,
@ -1047,7 +1018,6 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
A9D12E8723E3C5CF00F3C259 /* LightChartPanelViewController.swift in Sources */,
A9A08B3923A523C900498069 /* Int+Bytes.swift in Sources */, A9A08B3923A523C900498069 /* Int+Bytes.swift in Sources */,
A9A08B3A23A523C900498069 /* UIView+AllSubviewsWithClass.swift in Sources */, A9A08B3A23A523C900498069 /* UIView+AllSubviewsWithClass.swift in Sources */,
A9A08BAD23A794F500498069 /* BlePeripheralSimulated+CPBLight.swift in Sources */, A9A08BAD23A794F500498069 /* BlePeripheralSimulated+CPBLight.swift in Sources */,
@ -1063,7 +1033,6 @@
A9A08B4523A523C900498069 /* Data+ScanValue.swift in Sources */, A9A08B4523A523C900498069 /* Data+ScanValue.swift in Sources */,
A9A08B4623A523C900498069 /* Data+LittleEndianTypes.swift in Sources */, A9A08B4623A523C900498069 /* Data+LittleEndianTypes.swift in Sources */,
A9A08B4723A523C900498069 /* TipPowerUpViewController.swift in Sources */, A9A08B4723A523C900498069 /* TipPowerUpViewController.swift in Sources */,
A9D12E8823E4434000F3C259 /* UIColor+Interpolate.swift in Sources */,
A9A08B4823A523C900498069 /* CPBBle.swift in Sources */, A9A08B4823A523C900498069 /* CPBBle.swift in Sources */,
A9A08B4923A523C900498069 /* NavigationBarWithScrollAwareRightButton.swift in Sources */, A9A08B4923A523C900498069 /* NavigationBarWithScrollAwareRightButton.swift in Sources */,
A9A08B4B23A523C900498069 /* UartPacketManagerBase.swift in Sources */, A9A08B4B23A523C900498069 /* UartPacketManagerBase.swift in Sources */,
@ -1081,7 +1050,6 @@
A9A08B5723A523C900498069 /* GridView.swift in Sources */, A9A08B5723A523C900498069 /* GridView.swift in Sources */,
A9A08B5823A523C900498069 /* BlePeripheral+Uart.swift in Sources */, A9A08B5823A523C900498069 /* BlePeripheral+Uart.swift in Sources */,
A9A08B5923A523C900498069 /* BlePeripheral+CPBCommon.swift in Sources */, A9A08B5923A523C900498069 /* BlePeripheral+CPBCommon.swift in Sources */,
A9D5B21123E0985600178E1B /* AutoConnectViewController.swift in Sources */,
A9A08B5A23A523C900498069 /* BlePeripheral+CPBToneGenerator.swift in Sources */, A9A08B5A23A523C900498069 /* BlePeripheral+CPBToneGenerator.swift in Sources */,
A9A08B5B23A523C900498069 /* RssiUI.swift in Sources */, A9A08B5B23A523C900498069 /* RssiUI.swift in Sources */,
A9A08B5C23A523C900498069 /* HelpViewController.swift in Sources */, A9A08B5C23A523C900498069 /* HelpViewController.swift in Sources */,
@ -1098,7 +1066,6 @@
A9A08B6823A523C900498069 /* TipWelcomeViewController.swift in Sources */, A9A08B6823A523C900498069 /* TipWelcomeViewController.swift in Sources */,
A9A08BA423A5286700498069 /* BlePeripheralSimulated.swift in Sources */, A9A08BA423A5286700498069 /* BlePeripheralSimulated.swift in Sources */,
A9A08B6923A523C900498069 /* Config.swift in Sources */, A9A08B6923A523C900498069 /* Config.swift in Sources */,
A9D5B21423E25C1600178E1B /* ActiveLabelIntrinsicSizeFix.swift in Sources */,
A9A08B6A23A523C900498069 /* TipDiscoverViewController.swift in Sources */, A9A08B6A23A523C900498069 /* TipDiscoverViewController.swift in Sources */,
A9A08B6B23A523C900498069 /* ModulePanelViewController.swift in Sources */, A9A08B6B23A523C900498069 /* ModulePanelViewController.swift in Sources */,
A9A08B6C23A523C900498069 /* TipsViewController.swift in Sources */, A9A08B6C23A523C900498069 /* TipsViewController.swift in Sources */,
@ -1113,7 +1080,6 @@
A9A08B7323A523C900498069 /* BleManager.swift in Sources */, A9A08B7323A523C900498069 /* BleManager.swift in Sources */,
A9A08B7423A523C900498069 /* LightSensorPanelViewController.swift in Sources */, A9A08B7423A523C900498069 /* LightSensorPanelViewController.swift in Sources */,
A9A08B7523A523C900498069 /* AboutViewController.swift in Sources */, A9A08B7523A523C900498069 /* AboutViewController.swift in Sources */,
A9D5B21723E3024800178E1B /* BluetoothStatusViewController.swift in Sources */,
A9A08BAA23A7891500498069 /* ObjectBuilder.m in Sources */, A9A08BAA23A7891500498069 /* ObjectBuilder.m in Sources */,
A9A08B7623A523C900498069 /* ConfigUI.swift in Sources */, A9A08B7623A523C900498069 /* ConfigUI.swift in Sources */,
A9A08B7723A523C900498069 /* UartPacketManager.swift in Sources */, A9A08B7723A523C900498069 /* UartPacketManager.swift in Sources */,
@ -1303,7 +1269,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8; CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 2X94RM7457; DEVELOPMENT_TEAM = 2X94RM7457;
INFOPLIST_FILE = BluefruitPlayground/Info.plist; INFOPLIST_FILE = BluefruitPlayground/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
@ -1311,13 +1277,13 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.2.0; MARKETING_VERSION = 1.1.1;
PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.BluefruitPlayground; PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.BluefruitPlayground;
PRODUCT_NAME = "Bluefruit Playground"; PRODUCT_NAME = "Bluefruit Playground";
SWIFT_OBJC_BRIDGING_HEADER = "BluefruitPlayground/BluefruitPlayground-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "BluefruitPlayground/BluefruitPlayground-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
}; };
@ -1328,7 +1294,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8; CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 2X94RM7457; DEVELOPMENT_TEAM = 2X94RM7457;
INFOPLIST_FILE = BluefruitPlayground/Info.plist; INFOPLIST_FILE = BluefruitPlayground/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
@ -1336,12 +1302,12 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.2.0; MARKETING_VERSION = 1.1.1;
PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.BluefruitPlayground; PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.BluefruitPlayground;
PRODUCT_NAME = "Bluefruit Playground"; PRODUCT_NAME = "Bluefruit Playground";
SWIFT_OBJC_BRIDGING_HEADER = "BluefruitPlayground/BluefruitPlayground-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "BluefruitPlayground/BluefruitPlayground-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Release; name = Release;
}; };
@ -1392,7 +1358,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8; CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 2X94RM7457; DEVELOPMENT_TEAM = 2X94RM7457;
INFOPLIST_FILE = "BluefruitPlayground-SimulatedBluetooth-Info.plist"; INFOPLIST_FILE = "BluefruitPlayground-SimulatedBluetooth-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
@ -1400,7 +1366,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.2.0; MARKETING_VERSION = 1.1.2;
OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -D SIMULATEBLUETOOTH"; OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -D SIMULATEBLUETOOTH";
PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.BluefruitPlayground; PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.BluefruitPlayground;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@ -1418,7 +1384,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 8; CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 2X94RM7457; DEVELOPMENT_TEAM = 2X94RM7457;
INFOPLIST_FILE = "BluefruitPlayground-SimulatedBluetooth-Info.plist"; INFOPLIST_FILE = "BluefruitPlayground-SimulatedBluetooth-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 12.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
@ -1426,7 +1392,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.2.0; MARKETING_VERSION = 1.1.2;
OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -D SIMULATEBLUETOOTH"; OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -D SIMULATEBLUETOOTH";
PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.BluefruitPlayground; PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.BluefruitPlayground;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";

BIN
BluefruitPlayground/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -27,15 +27,8 @@ class BleManager: NSObject {
private var centralManagerPoweredOnSemaphore = DispatchSemaphore(value: 1) private var centralManagerPoweredOnSemaphore = DispatchSemaphore(value: 1)
// Scanning // Scanning
var isScanning: Bool { var isScanning = false
return scanningStartTime != nil
}
var scanningElapsedTime: TimeInterval? {
guard let scanningStartTime = scanningStartTime else { return nil }
return CACurrentMediaTime() - scanningStartTime
}
private var isScanningWaitingToStart = false private var isScanningWaitingToStart = false
internal var scanningStartTime: TimeInterval? // Time when the scanning started. nil if stopped
private var scanningServicesFilter: [CBUUID]? private var scanningServicesFilter: [CBUUID]?
internal var peripheralsFound = [UUID: BlePeripheral]() internal var peripheralsFound = [UUID: BlePeripheral]()
private var peripheralsFoundLock = NSLock() private var peripheralsFoundLock = NSLock()
@ -121,7 +114,7 @@ class BleManager: NSObject {
} }
// DLog("start scan") // DLog("start scan")
scanningStartTime = CACurrentMediaTime() isScanning = true
NotificationCenter.default.post(name: .didStartScanning, object: nil) NotificationCenter.default.post(name: .didStartScanning, object: nil)
let options = BleManager.kAlwaysAllowDuplicateKeys ? [CBCentralManagerScanOptionAllowDuplicatesKey: true] : nil let options = BleManager.kAlwaysAllowDuplicateKeys ? [CBCentralManagerScanOptionAllowDuplicatesKey: true] : nil
@ -132,7 +125,7 @@ class BleManager: NSObject {
func stopScan() { func stopScan() {
// DLog("stop scan") // DLog("stop scan")
centralManager?.stopScan() centralManager?.stopScan()
scanningStartTime = nil isScanning = false
isScanningWaitingToStart = false isScanningWaitingToStart = false
NotificationCenter.default.post(name: .didStopScanning, object: nil) NotificationCenter.default.post(name: .didStopScanning, object: nil)
} }
@ -218,19 +211,11 @@ class BleManager: NSObject {
} }
} }
func disconnect(from peripheral: BlePeripheral, waitForQueuedCommands: Bool = false) { func disconnect(from peripheral: BlePeripheral) {
guard let centralManager = centralManager else { return}
DLog("disconnect") DLog("disconnect")
NotificationCenter.default.post(name: .willDisconnectFromPeripheral, object: nil, userInfo: [NotificationUserInfoKey.uuid.rawValue: peripheral.identifier]) NotificationCenter.default.post(name: .willDisconnectFromPeripheral, object: nil, userInfo: [NotificationUserInfoKey.uuid.rawValue: peripheral.identifier])
centralManager?.cancelPeripheralConnection(peripheral.peripheral)
if waitForQueuedCommands {
// Send the disconnection to the command queue, so all the previous command are executed before disconnecting
peripheral.disconnect(centralManager: centralManager)
}
else {
centralManager.cancelPeripheralConnection(peripheral.peripheral)
}
} }
func reconnecToPeripherals(withIdentifiers identifiers: [UUID], withServices services: [CBUUID], timeout: Double? = nil) -> Bool { func reconnecToPeripherals(withIdentifiers identifiers: [UUID], withServices services: [CBUUID], timeout: Double? = nil) -> Bool {
@ -314,10 +299,7 @@ extension BleManager: CBCentralManagerDelegate {
if isScanning { if isScanning {
isScanningWaitingToStart = true isScanningWaitingToStart = true
} }
scanningStartTime = nil isScanning = false
// Remove all peripherals found (Important because the BlePeripheral queues could contain old commands that were processing when the bluetooth state changed)
peripheralsFound.removeAll()
} }
NotificationCenter.default.post(name: .didUpdateBleState, object: nil) NotificationCenter.default.post(name: .didUpdateBleState, object: nil)

View file

@ -207,12 +207,6 @@ class BlePeripheral: NSObject {
commandQueue.append(command) commandQueue.append(command)
} }
// MARK: - Connection
func disconnect(centralManager: CBCentralManager) {
let command = BleCommand(type: .disconnect, parameters: [centralManager], completion: nil)
commandQueue.append(command)
}
// MARK: - Service // MARK: - Service
func discoveredService(uuid: CBUUID) -> CBService? { func discoveredService(uuid: CBUUID) -> CBService? {
let service = peripheral.services?.first(where: {$0.uuid == uuid}) let service = peripheral.services?.first(where: {$0.uuid == uuid})
@ -325,7 +319,6 @@ class BlePeripheral: NSObject {
case writeCharacteristic case writeCharacteristic
case writeCharacteristicAndWaitNofity case writeCharacteristicAndWaitNofity
case readDescriptor case readDescriptor
case disconnect
} }
enum CommandError: Error { enum CommandError: Error {
@ -369,8 +362,6 @@ class BlePeripheral: NSObject {
write(with: command) write(with: command)
case .readDescriptor: case .readDescriptor:
readDescriptor(with: command) readDescriptor(with: command)
case .disconnect:
disconnect(with: command)
} }
} }
@ -500,12 +491,6 @@ class BlePeripheral: NSObject {
peripheral.readValue(for: descriptor) peripheral.readValue(for: descriptor)
} }
private func disconnect(with command: BleCommand) {
let centralManager = command.parameters!.first as! CBCentralManager
centralManager.cancelPeripheralConnection(self.peripheral)
finishedExecutingCommand(error: nil)
}
} }
// MARK: - CBPeripheralDelegate // MARK: - CBPeripheralDelegate

View file

@ -47,8 +47,7 @@ extension BlePeripheral {
// MARK: - Actions // MARK: - Actions
func cpbButtonsEnable(responseHandler: @escaping(Result<(ButtonsState, UUID), Error>) -> Void, completion: ((Result<Void, Error>) -> Void)?) { func cpbButtonsEnable(responseHandler: @escaping(Result<(ButtonsState, UUID), Error>) -> Void, completion: ((Result<Void, Error>) -> Void)?) {
let timePeriod: TimeInterval = 0 // 0 means that the responseHandler will be called only when there is a change self.cpbServiceEnable(serviceUuid: BlePeripheral.kCPBButtonsServiceUUID, mainCharacteristicUuid: BlePeripheral.kCPBButtonsCharacteristicUUID, timePeriod: 0, responseHandler: { response in
self.cpbServiceEnable(serviceUuid: BlePeripheral.kCPBButtonsServiceUUID, mainCharacteristicUuid: BlePeripheral.kCPBButtonsCharacteristicUUID, timePeriod: timePeriod, responseHandler: { response in
switch response { switch response {
case let .success((data, uuid)): case let .success((data, uuid)):
@ -68,21 +67,7 @@ extension BlePeripheral {
} }
self.cpbButtonsCharacteristic = characteristic self.cpbButtonsCharacteristic = characteristic
if timePeriod == 0 { // Read initial state if the timePeriod is 0 (update only when changed)
CPBBle.shared.buttonsReadState { response in
switch response {
case .success(_, _):
completion?(.success(())) completion?(.success(()))
case .failure(let error):
DLog("Error receiving initial button state data: \(error)")
completion?(.failure(error))
}
}
}
else {
completion?(.success(()))
}
case let .failure(error): case let .failure(error):
self.cpbButtonsCharacteristic = nil self.cpbButtonsCharacteristic = nil

View file

@ -14,7 +14,7 @@ extension BlePeripheral {
static let kCPBLightServiceUUID = CBUUID(string: "ADAF0300-C332-42A8-93BD-25E905756CB8") static let kCPBLightServiceUUID = CBUUID(string: "ADAF0300-C332-42A8-93BD-25E905756CB8")
private static let kCPBLightCharacteristicUUID = CBUUID(string: "ADAF0301-C332-42A8-93BD-25E905756CB8") private static let kCPBLightCharacteristicUUID = CBUUID(string: "ADAF0301-C332-42A8-93BD-25E905756CB8")
private static let kCPBLightDefaultPeriod: TimeInterval = 0.1 static let kCPBLightDefaultPeriod: TimeInterval = 0.1
// MARK: - Custom properties // MARK: - Custom properties
private struct CustomPropertiesKeys { private struct CustomPropertiesKeys {

View file

@ -14,8 +14,6 @@ extension BlePeripheral {
static let kCPBTemperatureServiceUUID = CBUUID(string: "ADAF0100-C332-42A8-93BD-25E905756CB8") static let kCPBTemperatureServiceUUID = CBUUID(string: "ADAF0100-C332-42A8-93BD-25E905756CB8")
private static let kCPBTemperatureCharacteristicUUID = CBUUID(string: "ADAF0101-C332-42A8-93BD-25E905756CB8") private static let kCPBTemperatureCharacteristicUUID = CBUUID(string: "ADAF0101-C332-42A8-93BD-25E905756CB8")
private static let kCPBTemperatureDefaultPeriod: TimeInterval = 0.1
// MARK: - Custom properties // MARK: - Custom properties
private struct CustomPropertiesKeys { private struct CustomPropertiesKeys {
static var cpbTemperatureCharacteristic: CBCharacteristic? static var cpbTemperatureCharacteristic: CBCharacteristic?
@ -33,7 +31,7 @@ extension BlePeripheral {
// MARK: - Actions // MARK: - Actions
func cpbTemperatureEnable(responseHandler: @escaping(Result<(Float, UUID), Error>) -> Void, completion: ((Result<Void, Error>) -> Void)?) { func cpbTemperatureEnable(responseHandler: @escaping(Result<(Float, UUID), Error>) -> Void, completion: ((Result<Void, Error>) -> Void)?) {
self.cpbServiceEnable(serviceUuid: BlePeripheral.kCPBTemperatureServiceUUID, mainCharacteristicUuid: BlePeripheral.kCPBTemperatureCharacteristicUUID, timePeriod: BlePeripheral.kCPBTemperatureDefaultPeriod, responseHandler: { response in self.cpbServiceEnable(serviceUuid: BlePeripheral.kCPBTemperatureServiceUUID, mainCharacteristicUuid: BlePeripheral.kCPBTemperatureCharacteristicUUID, timePeriod: 0.5, responseHandler: { response in
switch response { switch response {
case let .success((data, uuid)): case let .success((data, uuid)):

View file

@ -6,7 +6,7 @@
// Copyright © 2019 Adafruit. All rights reserved. // Copyright © 2019 Adafruit. All rights reserved.
// //
import UIKit import Foundation
import CoreBluetooth import CoreBluetooth
class BleManagerSimulated: BleManager { class BleManagerSimulated: BleManager {
@ -20,7 +20,7 @@ class BleManagerSimulated: BleManager {
} }
override func startScan(withServices services: [CBUUID]? = nil) { override func startScan(withServices services: [CBUUID]? = nil) {
scanningStartTime = CACurrentMediaTime() isScanning = true
// Add simulated peripheral // Add simulated peripheral
let simulatedBlePeripheral = BlePeripheralSimulated() let simulatedBlePeripheral = BlePeripheralSimulated()

View file

@ -1,16 +0,0 @@
//
// ActiveLabelIntrinsicSizeFix.swift
// BluefruitPlayground
//
// Created by Antonio García on 30/01/2020.
// Copyright © 2020 Adafruit. All rights reserved.
//
import ActiveLabel
// Fix as recommended here: https://github.com/optonaut/ActiveLabel.swift/issues/312
extension ActiveLabel {
open override var intrinsicContentSize: CGSize {
return super.intrinsicContentSize
}
}

View file

@ -0,0 +1,22 @@
//
// TouchReleaseRectangularPaletteControl.swift
// BluefruitPlayground
//
// Created by Antonio García on 16/12/2019.
// Copyright © 2019 Adafruit. All rights reserved.
//
import UIKit
import FlexColorPicker
class TouchReleaseRectangularPaletteControl: RectangularPaletteControl {
/*
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
}
*/
}

View file

@ -17,8 +17,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
startup() startup()
ScreenFlowManager.enableBleStateManagement()
return true return true
} }
@ -42,8 +40,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationWillTerminate(_ application: UIApplication) { func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
ScreenFlowManager.disableBleStateManagement()
} }
// MARK: - Startup // MARK: - Startup

View file

@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View file

@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "scan_cpb.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "scan_cpb@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "scan_cpb@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 325 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 261 KiB

View file

@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "scanning_background.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "scanning_background@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "scanning_background@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

View file

@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View file

@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "bluetooth_status.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "bluetooth_status@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "bluetooth_status@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 996 B

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -7,12 +7,12 @@
{ {
"idiom" : "universal", "idiom" : "universal",
"color" : { "color" : {
"color-space" : "srgb", "color-space" : "display-p3",
"components" : { "components" : {
"red" : "0.949", "red" : "0.010",
"alpha" : "1.000", "alpha" : "1.000",
"blue" : "0.961", "blue" : "0.802",
"green" : "0.961" "green" : "0.219"
} }
} }
} }

View file

@ -1,20 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "display-p3",
"components" : {
"red" : "0.889",
"alpha" : "0.800",
"blue" : "0.360",
"green" : "0.400"
}
}
}
]
}

File diff suppressed because it is too large Load diff

View file

@ -13,12 +13,9 @@ struct Config {
// Debug----------------------------------------------------------------------------- // Debug-----------------------------------------------------------------------------
static let isDebugEnabled = _isDebugAssertConfiguration() static let isDebugEnabled = _isDebugAssertConfiguration()
// Fastlane snapshots
private static let areFastlaneSnapshotsRunning = UserDefaults.standard.bool(forKey: "FASTLANE_SNAPSHOT")
// Bluetooth // Bluetooth
#if SIMULATEBLUETOOTH #if SIMULATEBLUETOOTH
static let isTutorialEnabled = areFastlaneSnapshotsRunning || !isDebugEnabled static let isTutorialEnabled = !isDebugEnabled
static let isBleUnsupportedWarningEnabled = false static let isBleUnsupportedWarningEnabled = false
static let bleManager = BleManagerSimulated.simulated static let bleManager = BleManagerSimulated.simulated
#else #else

View file

@ -28,6 +28,10 @@
<string>This app needs access to Bluetooth to connect to Circuit Playground Bluefruit devices</string> <string>This app needs access to Bluetooth to connect to Circuit Playground Bluefruit devices</string>
<key>NSBluetoothPeripheralUsageDescription</key> <key>NSBluetoothPeripheralUsageDescription</key>
<string>This app needs access to Bluetooth to connect to Circuit Playground Bluefruit devices</string> <string>This app needs access to Bluetooth to connect to Circuit Playground Bluefruit devices</string>
<key>NSCameraUsageDescription</key>
<string>This module requires the Camera to function</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This module reqires library access to store your photos</string>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>

BIN
BluefruitPlayground/Model/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -30,7 +30,8 @@ class CPBBle {
// Constants // Constants
private static let kLightSequenceFramesPerSecond = 10 private static let kLightSequenceFramesPerSecond = 10
private static let kLightSequenceDefaultBrightness: CGFloat = 0.25 private static let kLightSequenceDefaultBrightness: CGFloat = 0.25
public static let kLightSequenceDefaultSpeed: Double = 0.3 private static let kLightSequenceDefaultSpeed: Double = 0.3
// Singleton // Singleton
static let shared = CPBBle() static let shared = CPBBle()
@ -51,30 +52,25 @@ class CPBBle {
weak var buttonsDelegate: CPBBleButtonsDelegate? weak var buttonsDelegate: CPBBleButtonsDelegate?
weak var accelerometerDelegate: CPBBleAccelerometerDelegate? weak var accelerometerDelegate: CPBBleAccelerometerDelegate?
var neopixelLightSequenceAnimationBrightness: CGFloat = CPBBle.kLightSequenceDefaultBrightness
var neopixelLightSequenceAnimationSpeed: Double = CPBBle.kLightSequenceDefaultSpeed {
didSet {
lightSequenceAnimation?.speed = neopixelLightSequenceAnimationSpeed
}
}
// Data // Data
private var temperatureData = CPBDataSeries<Float>() private var temperatureData = CPBDataSeries<Float>()
private var lightData = CPBDataSeries<Float>() private var lightData = CPBDataSeries<Float>()
private var accelerometerData = CPBDataSeries<BlePeripheral.AccelerometerValue>() private var accelerometerData = CPBDataSeries<BlePeripheral.AccelerometerValue>()
private weak var blePeripheral: BlePeripheral? private weak var blePeripheral: BlePeripheral?
private var currentLightSequenceAnimation: LightSequenceAnimation? private var lightSequenceAnimation: LightSequenceAnimation?
public var neopixelCurrentLightSequenceAnimationSpeed: Double {
get {
return currentLightSequenceAnimation?.speed ?? 0
}
set {
currentLightSequenceAnimation?.speed = newValue
}
}
// MARK: - Lifecycle // MARK: - Lifecycle
private init() { private init() {
registerNotifications(enabled: true)
}
deinit {
registerNotifications(enabled: false)
} }
// MARK: - Setup // MARK: - Setup
@ -193,10 +189,6 @@ class CPBBle {
return blePeripheral?.cpbAccelerometerLastValue() return blePeripheral?.cpbAccelerometerLastValue()
} }
func lightDataSeries() -> [CPBDataSeries<Float>.Entry] {
return lightData.values
}
func temperatureDataSeries() -> [CPBDataSeries<Float>.Entry] { func temperatureDataSeries() -> [CPBDataSeries<Float>.Entry] {
return temperatureData.values return temperatureData.values
} }
@ -257,9 +249,36 @@ class CPBBle {
private func receiveButtonsData(response: Result<(BlePeripheral.ButtonsState, UUID), Error>) { private func receiveButtonsData(response: Result<(BlePeripheral.ButtonsState, UUID), Error>) {
switch response { switch response {
case let .success(buttonsState, uuid): case let .success(buttonsState, uuid):
DLog("Buttons: \(buttonsState.slideSwitch == .left ? "⬅️":"➡️") \(buttonsState.buttonA == .pressed ? "🔳":"🔲") \(buttonsState.buttonB == .pressed ? "🔳":"🔲") ") DLog("Buttons: \(buttonsState.slideSwitch == .left ? "⬅️":"➡️") \(buttonsState.buttonA == .pressed ? "🔳":"🔲") \(buttonsState.buttonB == .pressed ? "🔳":"🔲") ")
// NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Command"), object: nil)
if buttonsState.buttonA == .pressed{
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "EmoteOne"), object: nil)
}
if buttonsState.buttonB == .pressed{
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "EmoteTwo"), object: nil)
}
if buttonsState.slideSwitch == .right{
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "EmoteFour"), object: nil)
}
if buttonsState.slideSwitch == .left{
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "EmoteThree"), object: nil)
}
// Send to delegate // Send to delegate
if let buttonsDelegate = buttonsDelegate { if let buttonsDelegate = buttonsDelegate {
DispatchQueue.main.async { // Delegates are called in the main thread DispatchQueue.main.async { // Delegates are called in the main thread
@ -273,9 +292,15 @@ class CPBBle {
NotificationUserInfoKey.uuid.rawValue: uuid, NotificationUserInfoKey.uuid.rawValue: uuid,
]) ])
case .failure(let error): case .failure(let error):
DLog("Error receiving light data: \(error)") DLog("Error receiving light data: \(error)")
} }
} }
private func receiveAccelerometerData(response: Result<(BlePeripheral.AccelerometerValue, UUID), Error>) { private func receiveAccelerometerData(response: Result<(BlePeripheral.AccelerometerValue, UUID), Error>) {
@ -323,26 +348,19 @@ class CPBBle {
blePeripheral?.cpbPixelSetColor(index: 0, color: color, pixelMask: pixelMask) blePeripheral?.cpbPixelSetColor(index: 0, color: color, pixelMask: pixelMask)
} }
func neopixelStartLightSequence(_ lightSequenceGenerator: LightSequenceGenerator, func neopixelStartLightSequence(_ lightSequenceGenerator: LightSequenceGenerator) {
framesPerSecond: Int = CPBBle.kLightSequenceFramesPerSecond,
speed: Double = CPBBle.kLightSequenceDefaultSpeed,
brightness: CGFloat = CPBBle.kLightSequenceDefaultBrightness,
repeating: Bool = true,
sendLightSequenceNotifications: Bool = true) {
neopixelStopLightSequence() neopixelStopLightSequence()
currentLightSequenceAnimation = LightSequenceAnimation(lightSequenceGenerator: lightSequenceGenerator, framesPerSecond: framesPerSecond, repeating: repeating) lightSequenceAnimation = LightSequenceAnimation(lightSequenceGenerator: lightSequenceGenerator, framesPerSecond: CPBBle.kLightSequenceFramesPerSecond)
currentLightSequenceAnimation!.speed = speed lightSequenceAnimation!.speed = neopixelLightSequenceAnimationSpeed
currentLightSequenceAnimation!.start(stopHandler: { [weak self] in lightSequenceAnimation!.start() { [weak self] pixelsBytes in
self?.blePeripheral?.cpbPixelSetAllPixelsColor(.clear)
}) { [weak self] pixelsBytes in
guard let self = self else { return } guard let self = self else { return }
guard let blePeripheral = self.blePeripheral else { return } guard let blePeripheral = self.blePeripheral else { return }
let pixelBytesAdjustingBrightness = pixelsBytes.map {[ let pixelBytesAdjustingBrightness = pixelsBytes.map {[
UInt8(CGFloat($0[0]) * brightness), UInt8(CGFloat($0[0]) * self.neopixelLightSequenceAnimationBrightness),
UInt8(CGFloat($0[1]) * brightness), UInt8(CGFloat($0[1]) * self.neopixelLightSequenceAnimationBrightness),
UInt8(CGFloat($0[2]) * brightness), UInt8(CGFloat($0[2]) * self.neopixelLightSequenceAnimationBrightness),
]} ]}
let lightData = pixelBytesAdjustingBrightness.reduce(Data()) { (data, element) in let lightData = pixelBytesAdjustingBrightness.reduce(Data()) { (data, element) in
@ -351,35 +369,16 @@ class CPBBle {
blePeripheral.cpbPixelsWriteData(offset: 0, pixelData: lightData) blePeripheral.cpbPixelsWriteData(offset: 0, pixelData: lightData)
// Send notification // Send notification
if sendLightSequenceNotifications {
NotificationCenter.default.post(name: .didUpdateNeopixelLightSequence, object: nil, userInfo: [ NotificationCenter.default.post(name: .didUpdateNeopixelLightSequence, object: nil, userInfo: [
NotificationUserInfoKey.value.rawValue: pixelsBytes, NotificationUserInfoKey.value.rawValue: pixelsBytes,
NotificationUserInfoKey.uuid.rawValue: blePeripheral.identifier, NotificationUserInfoKey.uuid.rawValue: blePeripheral.identifier,
]) ])
} }
} }
}
func neopixelStopLightSequence() { func neopixelStopLightSequence() {
currentLightSequenceAnimation?.stop() lightSequenceAnimation?.stop()
currentLightSequenceAnimation = nil lightSequenceAnimation = nil
}
// MARK: - BLE Notifications
private weak var willdDisconnectFromPeripheralObserver: NSObjectProtocol?
private func registerNotifications(enabled: Bool) {
let notificationCenter = NotificationCenter.default
if enabled {
willdDisconnectFromPeripheralObserver = notificationCenter.addObserver(forName: .willDisconnectFromPeripheral, object: nil, queue: .main, using: {[weak self] notification in
// Force clear neopixels on disconnect
self?.neopixelSetAllPixelsColor(.clear)
})
} else {
if let willdDisconnectFromPeripheralObserver = willdDisconnectFromPeripheralObserver {notificationCenter.removeObserver(willdDisconnectFromPeripheralObserver)}
}
} }
} }

View file

@ -9,9 +9,8 @@
import UIKit import UIKit
protocol LightSequenceGenerator { protocol LightSequenceGenerator {
var numFrames: Int { get } func numFrames() -> Int
var numPixels: Int { get } func numPixels() -> Int
var isCyclic: Bool { get }
func colorsForFrame(_ frame: Int) -> [[UInt8]] func colorsForFrame(_ frame: Int) -> [[UInt8]]
} }
@ -20,16 +19,12 @@ class LightSequence {
// static let kLightSequenceDefaultBrightness: CGFloat = 0.25 // static let kLightSequenceDefaultBrightness: CGFloat = 0.25
static let kNumPixels = 10 static let kNumPixels = 10
var numPixels: Int { func numPixels() -> Int {
return LightSequence.kNumPixels return LightSequence.kNumPixels
} }
var isCyclic: Bool {
return true
}
// Utils // Utils
static func preprocessColorPalette(colors: [UIColor]) -> [[UInt8]] { static func preprocessColorPalette(colors: [UIColor]/*, brightness: CGFloat*/) -> [[UInt8]] {
let colorsBytes = colors.map({ color -> [UInt8] in let colorsBytes = colors.map({ color -> [UInt8] in
let colorBytes = BlePeripheral.pixelUInt8FromColor(color) let colorBytes = BlePeripheral.pixelUInt8FromColor(color)
return colorBytes return colorBytes
@ -51,16 +46,16 @@ class RotateLightSequence: LightSequence, LightSequenceGenerator {
// MARK: - // MARK: -
override init() { override init() {
colorsBytes = LightSequence.preprocessColorPalette(colors: RotateLightSequence.kColors) colorsBytes = LightSequence.preprocessColorPalette(colors: RotateLightSequence.kColors/*, brightness: LightSequence.kLightSequenceDefaultBrightness*/)
super.init() super.init()
} }
var numFrames: Int { func numFrames() -> Int {
return RotateLightSequence.kNumFrames return RotateLightSequence.kNumFrames
} }
func colorsForFrame(_ frame: Int) -> [[UInt8]] { func colorsForFrame(_ frame: Int) -> [[UInt8]] {
return rotate(numPixels: numPixels, colors: colorsBytes, frame: frame) return rotate(numPixels: numPixels(), colors: colorsBytes, frame: frame)
} }
private func rotate(numPixels: Int, colors: [[UInt8]], frame: Int) -> [[UInt8]] { private func rotate(numPixels: Int, colors: [[UInt8]], frame: Int) -> [[UInt8]] {
@ -88,22 +83,22 @@ class PulseLightSequence: LightSequence, LightSequenceGenerator {
// MARK: - // MARK: -
override init() { override init() {
colorsBytes = LightSequence.preprocessColorPalette(colors: PulseLightSequence.kColors) colorsBytes = LightSequence.preprocessColorPalette(colors: PulseLightSequence.kColors/*, brightness: LightSequence.kLightSequenceDefaultBrightness*/)
super.init() super.init()
} }
var numFrames: Int { func numFrames() -> Int {
return PulseLightSequence.kNumFrames return PulseLightSequence.kNumFrames
} }
func colorsForFrame(_ frame: Int) -> [[UInt8]] { func colorsForFrame(_ frame: Int) -> [[UInt8]] {
return pulse(numPixels: numPixels, colors: colorsBytes, frame: frame) return pulse(numPixels: numPixels(), colors: colorsBytes, frame: frame)
} }
private func pulse(numPixels: Int, colors: [[UInt8]], frame: Int) -> [[UInt8]] { private func pulse(numPixels: Int, colors: [[UInt8]], frame: Int) -> [[UInt8]] {
var lightBytes = [[UInt8]](repeating: [0, 0, 0], count:numPixels) var lightBytes = [[UInt8]](repeating: [0, 0, 0], count:numPixels)
let reverse = frame >= numFrames / 2 let reverse = frame >= numFrames() / 2
let colorIndex = reverse ? (numFrames - 1) - frame : frame let colorIndex = reverse ? (numFrames() - 1) - frame : frame
for i in 0..<numPixels { for i in 0..<numPixels {
//DLog("pixel: \(i) color: \(colorIndex)") //DLog("pixel: \(i) color: \(colorIndex)")
let colorBytes = colors[colorIndex] let colorBytes = colors[colorIndex]
@ -128,25 +123,25 @@ class SizzleLightSequence: LightSequence, LightSequenceGenerator {
// MARK: - // MARK: -
override init() { override init() {
colorsBytes = LightSequence.preprocessColorPalette(colors: SizzleLightSequence.kColors) colorsBytes = LightSequence.preprocessColorPalette(colors: SizzleLightSequence.kColors/*, brightness: LightSequence.kLightSequenceDefaultBrightness*/)
super.init() super.init()
} }
var numFrames:Int { func numFrames() -> Int {
return SizzleLightSequence.kNumFrames return SizzleLightSequence.kNumFrames
} }
func colorsForFrame(_ frame: Int) -> [[UInt8]] { func colorsForFrame(_ frame: Int) -> [[UInt8]] {
return sizzle(numPixels: numPixels, colors: colorsBytes, frame: frame) return sizzle(numPixels: numPixels(), colors: colorsBytes, frame: frame)
} }
private func sizzle(numPixels: Int, colors: [[UInt8]], frame: Int) -> [[UInt8]] { private func sizzle(numPixels: Int, colors: [[UInt8]], frame: Int) -> [[UInt8]] {
var lightBytes = [[UInt8]](repeating: [0, 0, 0], count:numPixels) var lightBytes = [[UInt8]](repeating: [0, 0, 0], count:numPixels)
let forwardNumFrames = numFrames / 2 let forwardNumFrames = numFrames() / 2
let reverse = frame >= forwardNumFrames let reverse = frame >= forwardNumFrames
let evenIndex = reverse ? (frame % forwardNumFrames) : (forwardNumFrames - 1) - frame let evenIndex = reverse ? (frame % forwardNumFrames) : (forwardNumFrames - 1) - frame
let oddIndex = reverse ? (numFrames - 1) - frame : frame let oddIndex = reverse ? (numFrames() - 1) - frame : frame
for i in 0..<numPixels { for i in 0..<numPixels {
//DLog("pixel: \(i) color: \(colorIndex)") //DLog("pixel: \(i) color: \(colorIndex)")
@ -169,16 +164,16 @@ class SweepLightSequence: LightSequence, LightSequenceGenerator {
// MARK: - // MARK: -
override init() { override init() {
colorsBytes = LightSequence.preprocessColorPalette(colors: SweepLightSequence.kColors) colorsBytes = LightSequence.preprocessColorPalette(colors: SweepLightSequence.kColors/*, brightness: LightSequence.kLightSequenceDefaultBrightness*/)
super.init() super.init()
} }
var numFrames: Int { func numFrames() -> Int {
return SweepLightSequence.kNumFrames return SweepLightSequence.kNumFrames
} }
func colorsForFrame(_ frame: Int) -> [[UInt8]] { func colorsForFrame(_ frame: Int) -> [[UInt8]] {
return sweep(numPixels: numPixels, colors: colorsBytes, frame: frame) return sweep(numPixels: numPixels(), colors: colorsBytes, frame: frame)
} }
private func sweep(numPixels: Int, colors: [[UInt8]], frame: Int) -> [[UInt8]] { private func sweep(numPixels: Int, colors: [[UInt8]], frame: Int) -> [[UInt8]] {
@ -193,78 +188,3 @@ class SweepLightSequence: LightSequence, LightSequenceGenerator {
return lightBytes return lightBytes
} }
} }
// MARK: - Module Started Animation
class FlashLightSequence: LightSequence, LightSequenceGenerator {
// Constants
private static let kNumFrames = 8 // Default frames per second is 10,
// Data
private var baseColor: UIColor
// MARK: -
init(baseColor: UIColor) {
self.baseColor = baseColor
super.init()
}
var numFrames: Int {
return FlashLightSequence.kNumFrames
}
override var isCyclic: Bool {
return false
}
func colorsForFrame(_ frame: Int) -> [[UInt8]] {
return flash(baseColor: baseColor, numPixels: numPixels, frame: frame)
}
private func flash(baseColor: UIColor, numPixels: Int, frame: Int) -> [[UInt8]] {
var lightBytes = [[UInt8]](repeating: [0, 0, 0], count:numPixels)
let factor = CGFloat(frame) / CGFloat(numFrames)
/*
let originWhite: CGFloat
let endWhite: CGFloat
let fraction: CGFloat
if factor < 0.5 {
originWhite = 0
endWhite = 1
fraction = factor * 2
}
else {
originWhite = 1
endWhite = 0
fraction = (factor - 0.5) * 2
}
let color = UIColor(white: originWhite + (endWhite - originWhite) * fraction, alpha: 1)
*/
let originColor: UIColor
let endColor: UIColor
let fraction: CGFloat
if factor < 0.5 {
originColor = .clear
endColor = baseColor
fraction = factor * 2
}
else {
originColor = baseColor
endColor = .clear
fraction = (factor - 0.5) * 2
}
let color = originColor.interpolateRGBColorTo(end: endColor, fraction: fraction)
//let color = originColor.interpolateHSVColorFrom(end: endColor, fraction: fraction)
let colorBytes = BlePeripheral.pixelUInt8FromColor(color)
for i in 0..<numPixels {
lightBytes[i] = colorBytes
}
return lightBytes
}
}

View file

@ -21,67 +21,57 @@ class LightSequenceAnimation {
private var displaylink: CADisplayLink? private var displaylink: CADisplayLink?
//private var simulatedFrame = 0.0
private var startingTimestamp: TimeInterval = 0 private var startingTimestamp: TimeInterval = 0
private var frameHandler: (([[UInt8]])->())? private var frameHandler: (([[UInt8]])->())?
private var stopHandler: (()->())?
private var repeating: Bool
// MARK: - // MARK: -
init(lightSequenceGenerator: LightSequenceGenerator, framesPerSecond: Int, repeating: Bool) { init(lightSequenceGenerator: LightSequenceGenerator, framesPerSecond: Int) {
self.lightSequenceGenerator = lightSequenceGenerator self.lightSequenceGenerator = lightSequenceGenerator
self.lightSequenceFramesPerSecond = framesPerSecond self.lightSequenceFramesPerSecond = framesPerSecond
self.repeating = repeating
} }
deinit { deinit {
stop() stop()
} }
func start(stopHandler:(()->())? = nil, frameHandler: @escaping ([[UInt8]])->()) { func start(frameHandler: @escaping ([[UInt8]])->()) {
self.stopHandler = stopHandler
self.frameHandler = frameHandler self.frameHandler = frameHandler
// Create displayLink if needed // Create displayLink if needed
if displaylink == nil { if displaylink == nil {
displaylink = CADisplayLink(target: self, selector: #selector(displayLinkStep)) displaylink = CADisplayLink(target: self, selector: #selector(displayLinkStep))
displaylink!.add(to: .main, forMode: .default) displaylink!.add(to: .current, forMode: .default)
} }
guard let displaylink = displaylink else { return } guard let displaylink = displaylink else { return }
displaylink.preferredFramesPerSecond = lightSequenceFramesPerSecond displaylink.preferredFramesPerSecond = lightSequenceFramesPerSecond
startingTimestamp = CACurrentMediaTime() startingTimestamp = displaylink.timestamp
} }
func stop() { func stop() {
displaylink?.invalidate() displaylink?.invalidate()
displaylink = nil displaylink = nil
displaylink?.remove(from: .main, forMode: .default)
stopHandler?()
} }
@objc func displayLinkStep(displaylink: CADisplayLink) { @objc func displayLinkStep(displaylink: CADisplayLink) {
let currentTimestamp = CACurrentMediaTime() - startingTimestamp //let fps = Double(currentLightSequenceFramesPerSecond)
let numFrames = Double(lightSequenceGenerator.numFrames) let currentTimestamp = displaylink.timestamp - startingTimestamp
let numFrames = Double(lightSequenceGenerator.numFrames())
guard repeating || currentTimestamp * numFrames * speed < numFrames else { // Stop if repeating == false and has displayed all frames
stop()
return
}
let frame = (currentTimestamp * numFrames * speed).truncatingRemainder(dividingBy:numFrames) let frame = (currentTimestamp * numFrames * speed).truncatingRemainder(dividingBy:numFrames)
//let frame = simulatedFrame.truncatingRemainder(dividingBy:numFrames)
//simulatedFrame += 0.4
//DLog("frame: \(frame)") //DLog("frame: \(frame)")
var pixelsBytes: [[UInt8]] var pixelsBytes: [[UInt8]]
if LightSequenceAnimation.kIsFrameInterpolationEnabled { if LightSequenceAnimation.kIsFrameInterpolationEnabled {
let preFrame = Int(floor(frame)) let preFrame = Int(floor(frame))
let postFrame = lightSequenceGenerator.isCyclic ? Int(ceil(frame)) % lightSequenceGenerator.numFrames : min(Int(ceil(frame)), lightSequenceGenerator.numFrames) let postFrame = Int(ceil(frame)) % lightSequenceGenerator.numFrames()
let postFactor = frame - Double(preFrame) let postFactor = frame - Double(preFrame)
let preFactor = 1 - postFactor let preFactor = 1 - postFactor
//DLog("pre: \(preFrame), post: \(postFrame), frame: \(frame)")
let pixelsBytesPre = lightSequenceGenerator.colorsForFrame(preFrame) let pixelsBytesPre = lightSequenceGenerator.colorsForFrame(preFrame)
let pixelsBytesPost = lightSequenceGenerator.colorsForFrame(postFrame) let pixelsBytesPost = lightSequenceGenerator.colorsForFrame(postFrame)

BIN
BluefruitPlayground/Resources/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -6,11 +6,22 @@
// Startup // Startup
"startup_bluetooth_unsupported" = "This device doesn't support Bluetooth Low Energy which is needed to connect to the Circuit Playground Bluefruit device"; "startup_bluetooth_unsupported" = "This device doesn't support Bluetooth Low Energy which is needed to connect to the Circuit Playground Bluefruit device";
// Bluetooth Management
// Splash "bluetooth_unsupported" = "This device doesn't support Bluetooth Low Energy";
"splash_restoringconnection" = "Restoring connection..."; "bluetooth_notauthorized" = "This app is not authorized to use the Bluetooth Low Energy";
"bluetooth_poweredoff" = "Bluetooth is currently powered off";
"bluetooth_locationpermission_disabled_text" = "Location Services should be enabled to scan bluetooth peripherals.
Please, go to settings and enable it to start scanning peripherals";
"bluetooth_locationpermission_title" = "This app needs location access";
"bluetooth_locationpermission_text" = "Please grant location access so this app can scan for Bluetooth peripherals";
"bluetooth_locationpermission_notavailable_title" = "Bluetooth Scanning not available";
"bluetooth_locationpermission_notavailable_text" = "Since location access has not been granted, the app will not be able to scan for Bluetooth peripherals";
"bluetooth_scanner_errorregisteringapp" = "App cannot be registered for bluetooth scanning.
Please reset bluetooth and try again.";
"bluetooth_connecting_error" = "Error connecting to peripheral";
"bluetooth_advertising_start_error" = "Error starting advertising";
// Welcome // Welcome
"tip0_title" = "Welcome"; "tip0_title" = "Welcome";
@ -25,7 +36,7 @@ Circuit Playground Bluefruit is available at the Adafruit shop.";
"tip1_link_url" = "https://learn.adafruit.com/bluefruit-playground-app/firmware"; "tip1_link_url" = "https://learn.adafruit.com/bluefruit-playground-app/firmware";
"tip1_action" = "Next"; "tip1_action" = "Next";
"tip2_title" = "Discover"; "tip2_title" = "Discover";
"tip2_detail" = "Once your Circuit Playground Bluefruit is powered, move your iPhone towards your Circuit Playground Bluefruit"; "tip2_detail" = "Once Circuit Playground Bluefruit is powered, place it near your iPhone and tap the button below.";
"tip2_action" = "Begin pairing"; "tip2_action" = "Begin pairing";
@ -61,26 +72,9 @@ https://github.com/mindsnacks/MSWeakTimer";
"about_ios_link1_text" = "uf2 firmware file"; "about_ios_link1_text" = "uf2 firmware file";
"about_ios_link1_url" = "https://learn.adafruit.com/bluefruit-playground-app/firmware"; "about_ios_link1_url" = "https://learn.adafruit.com/bluefruit-playground-app/firmware";
// Bluetooth Status
"bluetooth_unsupported" = "This device doesn't support Bluetooth";
"bluetooth_unsupported_detail" = "Bluetooth support and specifically Bluetooth Low Energy support is needed to communicate with a Circuit Playground Device";
"bluetooth_unsupported_le" = "This device doesn't support Bluetooth Low Energy";
"bluetooth_unsupported_le_detail" = "Bluetooth Low Energy support is needed to communicate with a Circuit Playground Device";
"bluetooth_notauthorized" = "This app is not authorized to use the Bluetooth Low Energy";
"bluetooth_notauthorized_detail" = "Bluetooth permission should be granted or Bluefruit Playground to connect to a Circuit Playground Device";
"bluetooth_poweredoff" = "Bluetooth is currently powered off";
"bluetooth_poweredoff_detail" = "Bluetooth should be enabled on your device for Bluefruit Playground to communicate with a Circuit Playground Device";
"bluetooth_enable_action" = "Bluetooth permissions";
// Autoconnect
"autoconnect_title" = "Finding CPB";
"autoconnect_manual_action" = "Select device manually";
"autoconnect_backbutton" = "Autoconnect";
// Scanner // Scanner
"scanner_title" = "Select CPB"; "scanner_title" = "Finding CPB";
"scanner_connecting" = "Connecting..."; "scanner_connecting" = "Connecting...";
"scanner_discoveringservices" = "Discovering services..."; "scanner_discoveringservices" = "Discovering services...";
@ -91,7 +85,6 @@ https://github.com/mindsnacks/MSWeakTimer";
"scanner_subtitle" = "Select a Circuit Playground Bluefruit device to connect to:"; "scanner_subtitle" = "Select a Circuit Playground Bluefruit device to connect to:";
"scanner_unnamed" = "<Unknown>"; "scanner_unnamed" = "<Unknown>";
"scanner_automatic_action" = "Select device automatically";
"scanner_problems_action" = "I can't find my device"; "scanner_problems_action" = "I can't find my device";
"scanner_backbutton" = "Disconnect"; "scanner_backbutton" = "Disconnect";
@ -115,9 +108,9 @@ Download it from the Bluefruit Playground guide at learn.adafruit.com";
// Modules // Modules
"modules_title" = "Modules"; "modules_title" = "Modules";
"modules_subtitle" = "Each module interacts with a type of sensor on the Circuit Playground Bluefruit device"; "modules_subtitle" = "Each module interacts with a specific type of sensor on the Circuit Playground Bluefruit device";
"modules_color_title" = "NeoPixels"; "modules_color_title" = "Neopixels";
"modules_color_subtitle" = "Control LED color & animation"; "modules_color_subtitle" = "Control LED color & animation";
"modules_light_title" = "Light Sensor"; "modules_light_title" = "Light Sensor";
"modules_light_subtitle" = "View continuous light sensor readings"; "modules_light_subtitle" = "View continuous light sensor readings";
@ -129,18 +122,16 @@ Download it from the Bluefruit Playground guide at learn.adafruit.com";
"modules_accelerometer_subtitle" = "View orientation based on accelerometer data"; "modules_accelerometer_subtitle" = "View orientation based on accelerometer data";
"modules_temperature_title" = "Temperature"; "modules_temperature_title" = "Temperature";
"modules_temperature_subtitle" = "View current temperature readings"; "modules_temperature_subtitle" = "View current temperature readings";
"modules_disconnect_title" = "Disconnect";
"modules_disconnect_subtitle" = "Close the current connection and scan for a new CPB device";
// Neopixels // Neopixels
"neopixels_title" = "NeoPixels"; "neopixels_title" = "Neopixels";
"neopixels_help" = "NeoPixels mode allows you to control the color of CPBs built-in NeoPixel LEDs individually, as a group, or by playing preset animations. "neopixels_help" = "Neopixel mode allows you to control the color of CPBs built-in neopixel LEDs individually, as a group, or by playing preset animations.
• Select NeoPixels to control by tapping them in the Circuit Playground Bluefruit image. • Select neopixels to control by tapping them in the Circuit Playground Bluefruit image.
• Set the color of selected NeoPixels by choosing a color from the Color Palette or Color Wheel at the bottom. • Set the color of selected neopixels by choosing a color from the Color Palette or Color Wheel at the bottom.
• Tap a light sequence to trigger an animation using all of the NeoPixels."; • Tap a light sequence to trigger an animation using all of the neopixels.";
"neopixels_sequence_title" = "Light Sequence"; "neopixels_sequence_title" = "Light Sequence";
"neopixels_sequence_speed" = "Speed"; "neopixels_sequence_speed" = "Speed";
@ -160,7 +151,6 @@ Download it from the Bluefruit Playground guide at learn.adafruit.com";
"lightsensor_panel_title" = "Luminance Reading"; "lightsensor_panel_title" = "Luminance Reading";
"lightsensor_panel_unit" = "Light level"; "lightsensor_panel_unit" = "Light level";
"lightsensor_chartpanel_title" = "Luminance Chart";
// Button Status // Button Status
"buttonstatus_title" = "Button Status"; "buttonstatus_title" = "Button Status";
@ -212,7 +202,7 @@ Download it from the Bluefruit Playground guide at learn.adafruit.com";
"temperature_units_celsius" = "ºC"; "temperature_units_celsius" = "ºC";
"temperature_units_farenheit" = "ºF"; "temperature_units_farenheit" = "ºF";
"temperature_panel_title" = "Temperature Chart"; "temperature_panel_title" = "Temperature Reading";
"temperature_chart_nodata" = "No chart data available"; "temperature_chart_nodata" = "No chart data available";
// Help // Help

File diff suppressed because one or more lines are too long

View file

@ -16,22 +16,20 @@ import UIKit
class NavigationBarWithScrollAwareRightButton : UINavigationBar { class NavigationBarWithScrollAwareRightButton : UINavigationBar {
// Constants // Constants
private static let kButtonTag = 100 private static let kButtonTag = 100
public struct CustomButtonMetrics {
static let rightMargin: CGFloat = 8//12
static let bottomMarginForLargeState: CGFloat = 9
static let bottomMarginForSmallState: CGFloat = 2
// Config (navbar metrics)
static let navBarHeightSmallState: CGFloat = 44 static let navBarHeightSmallState: CGFloat = 44
static let navBarHeightLargeState: CGFloat = 96.5 static let navBarHeightLargeState: CGFloat = 96.5
}
private static let rightMargin: CGFloat = 6//8
private static let bottomMarginForLargeState: CGFloat = 6//9
private static let bottomMarginForSmallState: CGFloat = 2
// Data // Data
private var navigationButton: UIButton?// = UIButton(type: .custom) private var navigationButton: UIButton?// = UIButton(type: .custom)
private var topViewController: UIViewController? private var topViewController: UIViewController?
// MARK: - // MARK: -
public func setRightButton(topViewController: UIViewController, image: UIImage?, target: Any?, action: Selector) { public func setRightButton(topViewController: UIViewController, image: UIImage?, target: Any?, action: Selector) {
navigationButton?.removeFromSuperview() navigationButton?.removeFromSuperview()
@ -49,8 +47,8 @@ class NavigationBarWithScrollAwareRightButton : UINavigationBar {
button.translatesAutoresizingMaskIntoConstraints = false button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
button.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -NavigationBarWithScrollAwareRightButton.rightMargin), button.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -CustomButtonMetrics.rightMargin),
button.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -NavigationBarWithScrollAwareRightButton.bottomMarginForLargeState), button.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -CustomButtonMetrics.bottomMarginForLargeState),
]) ])
self.layoutIfNeeded() // Force layout to remove adding animation self.layoutIfNeeded() // Force layout to remove adding animation
@ -61,14 +59,21 @@ class NavigationBarWithScrollAwareRightButton : UINavigationBar {
self.topViewController = topViewController self.topViewController = topViewController
topViewController.navigationController?.delegate = self topViewController.navigationController?.delegate = self
/*
// Detect content scrollview scroll
if let contentScrollView = topViewController.view as? UIScrollView ?? topViewController.view.subviews.first as? UIScrollView {
contentScrollView.delegate = self
}
*/
} }
public var navigationBarScrollViewProgress: CGFloat { public var navigationBarScrollViewProgress: CGFloat {
return (self.frame.height - NavigationBarWithScrollAwareRightButton.navBarHeightSmallState) / (NavigationBarWithScrollAwareRightButton.navBarHeightLargeState - NavigationBarWithScrollAwareRightButton.navBarHeightSmallState) return (self.frame.height - CustomButtonMetrics.navBarHeightSmallState) / (CustomButtonMetrics.navBarHeightLargeState - CustomButtonMetrics.navBarHeightSmallState)
} }
public func updateRightButtonPosition() { public func updateRightButtonPosition() {
let yDelta = NavigationBarWithScrollAwareRightButton.bottomMarginForLargeState - NavigationBarWithScrollAwareRightButton.bottomMarginForSmallState let yDelta = CustomButtonMetrics.bottomMarginForLargeState - CustomButtonMetrics.bottomMarginForSmallState
let y = yDelta * max(0, (1-navigationBarScrollViewProgress)) let y = yDelta * max(0, (1-navigationBarScrollViewProgress))
navigationButton?.transform = CGAffineTransform(translationX: 0, y: y) navigationButton?.transform = CGAffineTransform(translationX: 0, y: y)

View file

@ -10,68 +10,33 @@ import UIKit
struct ScreenFlowManager { struct ScreenFlowManager {
// Data
private static var wasManualScanningLastUsed = false // Last scanning method used
// MARK: - Go to app areas // MARK: - Go to app areas
public static func gotoAutoconnect() { public static func gotoScanner() {
let bluetoothErrorDisplayed = updateStatusViewControllerIfBluetoothStateIsNotReady()
guard !bluetoothErrorDisplayed else { return }
guard let window = UIApplication.shared.keyWindow else { return }
let isAlreadyInAutoconnect = (window.rootViewController as? UINavigationController)?.topViewController is AutoConnectViewController
guard !isAlreadyInAutoconnect else { return }
let isInManualScan = (window.rootViewController as? UINavigationController)?.topViewController is ScannerViewController
let transition: UIView.AnimationOptions = isInManualScan ? .transitionFlipFromLeft : .transitionCrossDissolve
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil) let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: AutoConnectViewController.kNavigationControllerIdentifier) let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: ScannerViewController.kIdentifier)
changeRootViewController(rootViewController: rootNavigationViewController, animationOptions: transition) ScreenFlowManager.changeRootViewController(rootViewController: rootNavigationViewController, animated: false)
} }
public static func goToManualScan() { public static func restoreAndGoToHome() {
let bluetoothErrorDisplayed = updateStatusViewControllerIfBluetoothStateIsNotReady()
guard !bluetoothErrorDisplayed else { return }
guard let window = UIApplication.shared.keyWindow else { return }
let isAlreadyInManualScan = (window.rootViewController as? UINavigationController)?.topViewController is ScannerViewController
guard !isAlreadyInManualScan else { return }
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: ScannerViewController.kNavigationControllerIdentifier)
changeRootViewController(rootViewController: rootNavigationViewController, animationOptions: .transitionFlipFromRight)
}
public static func restoreAndGoToCPBModules() {
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil) let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: HomeViewController.kNavigationControllerIdentifier) // Add home to scanner
let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: ScannerViewController.kIdentifier) as! UINavigationController
let homeViewController = mainStoryboard.instantiateViewController(withIdentifier: HomeViewController.kIdentifier)
rootNavigationViewController.viewControllers.append(homeViewController)
changeRootViewController(rootViewController: rootNavigationViewController) { ScreenFlowManager.changeRootViewController(rootViewController: rootNavigationViewController, animated: false)
CPBBle.shared.neopixelStartLightSequence(FlashLightSequence(baseColor: .lightGray), speed: 1, repeating: false, sendLightSequenceNotifications: false)
}
} }
public static func gotoCPBModules() {
guard let window = UIApplication.shared.keyWindow else { return }
ScreenFlowManager.wasManualScanningLastUsed = (window.rootViewController as? UINavigationController)?.topViewController is ScannerViewController
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: HomeViewController.kNavigationControllerIdentifier)
changeRootViewController(rootViewController: rootNavigationViewController) {
CPBBle.shared.neopixelStartLightSequence(FlashLightSequence(baseColor: .white), speed: 1, repeating: false, sendLightSequenceNotifications: false)
}
}
// MARK: - Change Root View Controller // MARK: - Change Root View Controller
public static func changeRootViewController(rootViewController: UIViewController, animationOptions: UIView.AnimationOptions? = [.transitionCrossDissolve], completionHandler: (() -> Void)? = nil ) { public static func changeRootViewController(rootViewController: UIViewController, animated: Bool = true, completionHandler: (() -> Void)? = nil ) {
guard let window = UIApplication.shared.keyWindow else { return } guard let window = UIApplication.shared.keyWindow else { return }
if let animationOptions = animationOptions { if animated {
rootViewController.view.layoutIfNeeded()
UIView.transition(with: window, duration: 0.3, options: .transitionCrossDissolve, animations: {
window.rootViewController = rootViewController window.rootViewController = rootViewController
// rootViewController.view.layoutIfNeeded()
UIView.transition(with: window, duration: 0.3, options: animationOptions, animations: {
}, completion: { completed in }, completion: { completed in
completionHandler?() completionHandler?()
}) })
@ -81,92 +46,4 @@ struct ScreenFlowManager {
completionHandler?() completionHandler?()
} }
} }
// MARK: - Monitor Bluetooth State and automatically change rootViewController
private static weak var didDisconnectFromPeripheralObserver: NSObjectProtocol?
private static weak var didUpdateBleStateObserver: NSObjectProtocol?
public static func enableBleStateManagement() {
let notificationCenter = NotificationCenter.default
if didDisconnectFromPeripheralObserver == nil {
didDisconnectFromPeripheralObserver = notificationCenter.addObserver(forName: .didDisconnectFromPeripheral, object: nil, queue: .main, using: { _ in
guard let window = UIApplication.shared.keyWindow else { return }
// Don't show on startup
let topViewController = (window.rootViewController as? UINavigationController)?.topViewController ?? window.rootViewController
guard !(topViewController is StartupViewController) else { return }
// Show disconnection alert
let localizationManager = LocalizationManager.shared
let alertController = UIAlertController(title: nil, message: localizationManager.localizedString("scanner_peripheraldisconnected"), preferredStyle: .alert)
let okAction = UIAlertAction(title: localizationManager.localizedString("dialog_ok"), style: .default) { _ in
/*
let isInManualScan = (window.rootViewController as? UINavigationController)?.topViewController is ScannerViewController
if !isInManualScan { // If it is manual scanning, maintain that screen
gotoAutoconnect()
}*/
if ScreenFlowManager.wasManualScanningLastUsed {
goToManualScan()
}
else {
gotoAutoconnect()
}
}
alertController.addAction(okAction)
window.rootViewController?.present(alertController, animated: true, completion: nil)
})
}
if didUpdateBleStateObserver == nil {
didUpdateBleStateObserver = notificationCenter.addObserver(forName: .didUpdateBleState, object: nil, queue: .main) { _ in
guard let window = UIApplication.shared.keyWindow else { return }
let topViewController = (window.rootViewController as? UINavigationController)?.topViewController ?? window.rootViewController
let isAllowedToShowBluetoothDialogBasedOnCurrentRootViewController = !(topViewController is TipsViewController) // Don't show bluetooth errroes while showing tips
guard isAllowedToShowBluetoothDialogBasedOnCurrentRootViewController else { return }
let _ = updateStatusViewControllerIfBluetoothStateIsNotReady()
}
}
}
public static func disableBleStateManagement() {
let notificationCenter = NotificationCenter.default
if let didDisconnectFromPeripheralObserver = didDisconnectFromPeripheralObserver {notificationCenter.removeObserver(didDisconnectFromPeripheralObserver)
self.didDisconnectFromPeripheralObserver = nil
}
if let didUpdateBleStateObserver = didUpdateBleStateObserver {notificationCenter.removeObserver(didUpdateBleStateObserver)
self.didUpdateBleStateObserver = nil
}
}
private static func updateStatusViewControllerIfBluetoothStateIsNotReady() -> Bool { // Returns true if the rootViewController was changed to BluetoothStatusViewController
guard let window = UIApplication.shared.keyWindow else { return false }
let bluetoothState = Config.bleManager.state
let shouldShowBluetoothDialog = bluetoothState == .poweredOff || bluetoothState == .unsupported || bluetoothState == .unauthorized
let topViewController = (window.rootViewController as? UINavigationController)?.topViewController ?? window.rootViewController
let isShowingEnableBluetoothDialog = topViewController is BluetoothStatusViewController
var viewControllerIdentifier: String? = nil
if shouldShowBluetoothDialog && !isShowingEnableBluetoothDialog {
viewControllerIdentifier = BluetoothStatusViewController.kIdentifier
}
else if !shouldShowBluetoothDialog && isShowingEnableBluetoothDialog {
viewControllerIdentifier = AutoConnectViewController.kNavigationControllerIdentifier
}
if let viewControllerIdentifier = viewControllerIdentifier {
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: viewControllerIdentifier)
changeRootViewController(rootViewController: rootNavigationViewController)
return true
}
else {
return false
}
}
} }

View file

@ -1,51 +0,0 @@
//
// UIColor+Interpolate.swift
// BluefruitPlayground
//
// Created by Antonio García on 30/01/2020.
// Copyright © 2020 Adafruit. All rights reserved.
//
import UIKit
extension UIColor {
// based on: https://stackoverflow.com/questions/22868182/uicolor-transition-based-on-progress-value
func interpolateRGBColorTo(end:UIColor, fraction:CGFloat) -> UIColor {
var f = max(0, fraction)
f = min(1, fraction)
guard var c1 = self.cgColor.components, var c2 = end.cgColor.components else { return self }
// Convert 2 components colors to 4 components
if c1.count == 2 {
let whiteComponent = c1[0]
let alphaComponent = c1[1]
c1 = [whiteComponent, whiteComponent, whiteComponent, alphaComponent]
}
if c2.count == 2 {
let whiteComponent = c2[0]
let alphaComponent = c2[1]
c2 = [whiteComponent, whiteComponent, whiteComponent, alphaComponent]
}
// Interpolate
let r = c1[0] + (c2[0] - c1[0]) * f
let g = c1[1] + (c2[1] - c1[1]) * f
let b = c1[2] + (c2[2] - c1[2]) * f
let a = c1[3] + (c2[3] - c1[3]) * f
return UIColor.init(red:r, green:g, blue:b, alpha:a)
}
// Note: getHue can be negative from iOS 10
func interpolateHSVColorFrom(end: UIColor, fraction: CGFloat) -> UIColor {
var f = max(0, fraction)
f = min(1, fraction)
var h1: CGFloat = 0, s1: CGFloat = 0, b1: CGFloat = 0, a1: CGFloat = 0
self.getHue(&h1, saturation: &s1, brightness: &b1, alpha: &a1)
var h2: CGFloat = 0, s2: CGFloat = 0, b2: CGFloat = 0, a2: CGFloat = 0
end.getHue(&h2, saturation: &s2, brightness: &b2, alpha: &a2)
let h = h1 + (h2 - h1) * f
let s = s1 + (s2 - b1) * f
let b = b1 + (b2 - b1) * f
let a = a1 + (a2 - a1) * f
return UIColor(hue: h, saturation: s, brightness: b, alpha: a)
}
}

Binary file not shown.

View file

@ -24,7 +24,7 @@ class AccelerometerPanelViewController: ModulePanelViewController {
@IBOutlet weak var accelerometerEulerYLabel: UILabel! @IBOutlet weak var accelerometerEulerYLabel: UILabel!
@IBOutlet weak var accelerometerEulerZLabel: UILabel! @IBOutlet weak var accelerometerEulerZLabel: UILabel!
private var currentState = BlePeripheral.ButtonsState(slideSwitch: .left, buttonA: .released, buttonB: .released)
// MARK: - Lifecycle // MARK: - Lifecycle
override func viewDidLoad() { override func viewDidLoad() {
@ -37,6 +37,16 @@ class AccelerometerPanelViewController: ModulePanelViewController {
accelerometerTitleLabel.text = localizationManager.localizedString("accelerometer_panel_accelerometer_title") accelerometerTitleLabel.text = localizationManager.localizedString("accelerometer_panel_accelerometer_title")
accelerometerEulerAnglesTitleLabel.text = localizationManager.localizedString("accelerometer_panel_eulerangles_title") accelerometerEulerAnglesTitleLabel.text = localizationManager.localizedString("accelerometer_panel_eulerangles_title")
// // Read initial state
// CPBBle.shared.buttonsReadState { [weak self] response in
// switch response {
// case let .success(buttonsStatus, _):
// buttonsStateReceived(buttonsStatus)
// case .failure(let error):
// DLog("Error receiving temperature data: \(error)")
// }
} }
func accelerationReceived( func accelerationReceived(
@ -55,4 +65,22 @@ class AccelerometerPanelViewController: ModulePanelViewController {
accelerometerEulerZLabel.text = String(format: "%.0f", zDeg) accelerometerEulerZLabel.text = String(format: "%.0f", zDeg)
} }
// // MARK: - Data
// func buttonsStateReceived(_ buttonsState: BlePeripheral.ButtonsState) {
//
//
//
// if buttonsState.buttonA != currentState.buttonA {
// // animateState(view: buttonAStatusView, isPressed: buttonsState.buttonA == .pressed)
// }
//
// if buttonsState.buttonB != currentState.buttonB {
// // animateState(view: buttonBStatusView, isPressed: buttonsState.buttonB == .pressed)
// }
//
// currentState = buttonsState
// }
//
//}
} }

View file

@ -8,31 +8,315 @@
import UIKit import UIKit
import SceneKit import SceneKit
import ReplayKit
import AVFoundation
struct LowPassFilterSignal {
/// Current signal value
var value: Float
/// A scaling factor in the range 0.0..<1.0 that determines
/// how resistant the value is to change
let filterFactor: Float
/// Update the value, using filterFactor to attenuate changes
mutating func update(newValue: Float) {
value = filterFactor * value + (1.0 - filterFactor) * newValue
}
}
class AccelerometerViewController: ModuleViewController { class AccelerometerViewController: ModuleViewController {
// Constants // Constants
static let kIdentifier = "AccelerometerViewController" static let kIdentifier = "AccelerometerViewController"
//For Recording Function
var recordButtonWasSelected: Bool = false
var preferStatusBarHidden: Bool!
// UI // UI
@IBOutlet weak var sceneView: SCNView! @IBOutlet weak var sceneView: SCNView!
@IBOutlet var mainCamera: UIView!
@IBOutlet var rotateCameraRef: UIButton!
@IBOutlet var backgroundSwapRef: UIButton!
@IBOutlet var recordButton: UIButton!
let recorder = RPScreenRecorder.shared()
@IBAction func backgroundSwapAction(_ sender: Any) {
backgroundCameraIsActive.toggle()
if backgroundCameraIsActive{
mainCamera.alpha = 0
}else{
mainCamera.alpha = 1
}
}
@IBAction func rotateCamAction(_ sender: Any) {
print("Button Rotation Pressed")
guard let currentCameraInput : AVCaptureInput = captureSession?.inputs.first else {
return
}
if let input = currentCameraInput as? AVCaptureDeviceInput
{
if input.device.position == .back{
switchToFrontCamera()
}
if input.device.position == .front{
switchToBackCamera()
}
}
}
@IBAction func recordAction(_ sender: Any) {
print("Is Recording...")
recordButtonWasSelected = !recordButtonWasSelected
recordUpdater()
}
//Recording Functions
func recordUpdater() {
if recordButtonWasSelected {
print("Currently Recording...")
recording()
// recordButton.backgroundColor = UIColor.red
} else {
// recordButton.backgroundColor = UIColor.white
print("Stopped/Not Recording.")
stopRecording()
}
}
func recording() {
RPScreenRecorder.shared().isMicrophoneEnabled = true;
recordButton.isUserInteractionEnabled = true
recordButton.isEnabled = true
let pulse1 = CASpringAnimation(keyPath: "transform.scale")
pulse1.duration = 0.6
pulse1.fromValue = 1.0
pulse1.toValue = 1.20
pulse1.autoreverses = true
pulse1.repeatCount = 1
pulse1.initialVelocity = 0.8
pulse1.damping = 0.8
let animationGroup = CAAnimationGroup()
animationGroup.duration = 2.7
animationGroup.repeatCount = 1000
animationGroup.animations = [pulse1]
recordButton.layer.add(animationGroup, forKey: "pulse")
recorder.startRecording { (error) in
if let error = error {
print(error)
}
}
}
//Works for iPhone
func stopRecording() {
if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.phone {
recordButton.layer.removeAllAnimations()
recorder.stopRecording { (previewVC, error) in
if let previewVC = previewVC{
previewVC.previewControllerDelegate = self
self.present(previewVC, animated: true, completion: nil)
}
if let error = error {
print(error)
}
}
}
if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad {
recordButton.layer.removeAllAnimations()
let recorder = RPScreenRecorder.shared()
recorder.stopRecording { (previewVC, error) in
if let previewVC = previewVC {
previewVC.previewControllerDelegate = self as RPPreviewViewControllerDelegate
if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad {
previewVC.modalPresentationStyle = UIModalPresentationStyle.popover
previewVC.popoverPresentationController?.sourceRect = self.recordButton.frame //position popover relative to record button - NEEDS TESTING
previewVC.popoverPresentationController?.sourceView = self.view
//Show Preview
self.present(previewVC, animated: true)
}
else {
//Set boundaries safe for iPhone X - NEEDS TESTING
let safeArea = self.view.safeAreaInsets
let safeAreaHeight = self.view.frame.height - safeArea.top
let safeAreaWidth = self.view.frame.width - (safeArea.left + safeArea.right)
let scaleX = safeAreaWidth / self.view.frame.width
let scaleY = safeAreaHeight / self.view.frame.height
let scale = min(scaleX, scaleY)
previewVC.view.transform = CGAffineTransform(scaleX: scale, y: scale)
//Show Preview
self.present(previewVC, animated: true) {
previewVC.view.frame.origin.x += safeArea.left
previewVC.view.frame.origin.y += safeArea.top
}
}
}
if let error = error {
print(error)
}
}
}
}
//Background
var backgroundCameraIsActive = false
// Camera Data
var captureSession: AVCaptureSession?
var videoPreviewLayer : AVCaptureVideoPreviewLayer?
var frontCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)
var backCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
func switchToFrontCamera(){
print("Front Cam")
if frontCamera?.isConnected == true {
captureSession?.stopRunning()
let captureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)
do {
let input = try AVCaptureDeviceInput(device: captureDevice!)
captureSession = AVCaptureSession()
captureSession?.addInput(input)
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
videoPreviewLayer?.frame = view.layer.bounds
mainCamera.layer.addSublayer(videoPreviewLayer!)
captureSession?.startRunning()
}
catch{
print("Error.")
}
}
}
func switchToBackCamera(){
print("Back Cam")
if backCamera?.isConnected == true {
captureSession?.stopRunning()
let captureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
do {
let input = try AVCaptureDeviceInput(device: captureDevice!)
captureSession = AVCaptureSession()
captureSession?.addInput(input)
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
videoPreviewLayer?.frame = view.layer.bounds
mainCamera.layer.addSublayer(videoPreviewLayer!)
captureSession?.startRunning()
}
catch{
print("Error.")
}
}
}
// Data // Data
private var circuitNode: SCNNode! private var jawNode: SCNNode!
private var headNode: SCNNode!
private var sparkyFaceNode: SCNNode!
private var valuesPanelViewController: AccelerometerPanelViewController! private var valuesPanelViewController: AccelerometerPanelViewController!
private var acceleration = BlePeripheral.AccelerometerValue(x: 0, y: 0, z: 0) private var acceleration = BlePeripheral.AccelerometerValue(x: 0, y: 0, z: 0)
var accelerometerX = LowPassFilterSignal(value: 0, filterFactor: 0.6)
var accelerometerY = LowPassFilterSignal(value: 0, filterFactor: 0.7)
private var playIntroAnimation = false
// MARK: - Lifecycle // MARK: - Lifecycle
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
// Add panels // Add panels
valuesPanelViewController = (addPanelViewController(storyboardIdentifier: AccelerometerPanelViewController.kIdentifier) as! AccelerometerPanelViewController) valuesPanelViewController = (addPanelViewController(storyboardIdentifier: AccelerometerPanelViewController.kIdentifier) as! AccelerometerPanelViewController)
// Load base // Load base
let scene = SCNScene(named: "cpb.scn")! let scene = SCNScene(named: "Sparky_Gold1.dae")!
scene.background.contents = UIColor.clear scene.background.contents = UIColor.clear
circuitNode = scene.rootNode.childNode(withName: "Circuit_Playground_Bluefruit", recursively: false)! jawNode = scene.rootNode.childNode(withName: "jaw", recursively: true)!
headNode = scene.rootNode.childNode(withName: "SparkyHead", recursively: true)!
sparkyFaceNode = scene.rootNode.childNode(withName: "Face", recursively: true)!
// Setup scene // Setup scene
sceneView.scene = scene sceneView.scene = scene
@ -41,13 +325,44 @@ class AccelerometerViewController: ModuleViewController {
// Localization // Localization
let localizationManager = LocalizationManager.shared let localizationManager = LocalizationManager.shared
self.title = localizationManager.localizedString("accelerometer_title") self.title = localizationManager.localizedString("Puppets")
moduleHelpMessage = localizationManager.localizedString("accelerometer_help") moduleHelpMessage = localizationManager.localizedString("accelerometer_help")
//--- Camera Function ----
if #available(iOS 10.2, *){
let captureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
do
{
let input = try AVCaptureDeviceInput(device: captureDevice!)
captureSession = AVCaptureSession()
captureSession?.addInput(input)
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
videoPreviewLayer?.frame = view.layer.bounds
mainCamera.layer.addSublayer(videoPreviewLayer!)
captureSession?.startRunning()
} }
catch
{
print("Error.")
}
}
}
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
switchToFrontCamera()
// Initial value // Initial value
if let acceleration = CPBBle.shared.accelerometerLastValue() { if let acceleration = CPBBle.shared.accelerometerLastValue() {
self.acceleration = acceleration self.acceleration = acceleration
@ -56,6 +371,136 @@ class AccelerometerViewController: ModuleViewController {
// Set delegate // Set delegate
CPBBle.shared.accelerometerDelegate = self CPBBle.shared.accelerometerDelegate = self
//Subscribe to command notifications
notificatonsForEmoteUse()
//Sparky's Intro Animation
sparkyIntroAnimation()
}
func notificatonsForEmoteUse (){
NotificationCenter.default.addObserver(self, selector: #selector(animationOne),name:NSNotification.Name(rawValue: "EmoteOne"), object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(animationTwo),name:NSNotification.Name(rawValue: "EmoteTwo"), object: nil)
// NotificationCenter.default.addObserver(self, selector: #selector(switchOne),name:NSNotification.Name(rawValue: "EmoteThree"), object: nil)
//
// NotificationCenter.default.addObserver(self, selector: #selector(switchTwo),name:NSNotification.Name(rawValue: "EmoteFour"), object: nil)
}
func sparkyIntroAnimation(){
let scale: Float = 0.0005
headNode.scale = SCNVector3(x: scale, y: scale, z: scale)
let scaleAnimation = SCNAction.scale(to: CGFloat(1), duration: 1.3)
let rotationAction = SCNAction.rotateBy(x: 0, y: 12.57, z: 0, duration: 1.4)
// let transformAnimation = SCNAction.moveBy(x: 0, y: 0, z: 0, duration: 1.4)
rotationAction.timingMode = .easeOut
// rotationAction.timingFunction = { (p: Float) in
// return self.easeOutElastic(p)
// }
headNode.runAction(scaleAnimation)
headNode.runAction(rotationAction)
// headNode.runAction(transformAnimation)
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1401)) {
self.playIntroAnimation = true
}
}
@objc func switchOne(){
backgroundCameraIsActive.toggle()
if backgroundCameraIsActive{
DispatchQueue.main.async {
self.mainCamera.alpha = 0
}
}else{
DispatchQueue.main.async {
self.mainCamera.alpha = 1
}
}
}
@objc func switchTwo(){
print("Switched 2")
backgroundCameraIsActive.toggle()
if backgroundCameraIsActive{
DispatchQueue.main.async {
self.mainCamera.alpha = 0
}
}else{
DispatchQueue.main.async {
self.mainCamera.alpha = 1
}
}
}
@objc func animationOne(){
//Uses a bounce in and out animation for Sparky's eyes.
let scale: Float = 0.0005
sparkyFaceNode.scale = SCNVector3(x: scale, y: scale, z: scale)
let bounceAction = SCNAction.scale(to: CGFloat(1), duration: 1)
bounceAction.timingMode = .linear
// Use a custom timing function
bounceAction.timingFunction = { (p: Float) in
return self.easeOutElastic(p)
}
sparkyFaceNode.runAction(bounceAction)
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1500)) {
self.sparkyFaceNode.removeAllActions()
}
}
@objc func animationTwo(){
playIntroAnimation = false
let rotateAnimation = SCNAction.rotateTo(x: 0, y: 0, z: -0.3, duration: 0.1)
let rotateAnimationback = SCNAction.rotateTo(x: 0, y: 0, z: 0.3, duration: 0.1)
let headReset = SCNAction.rotateTo(x: -0.01, y: 0, z: -0.0, duration: 0.1)
let sequence = SCNAction.sequence([rotateAnimation, rotateAnimationback,rotateAnimation, rotateAnimationback,rotateAnimation, rotateAnimationback, headReset])
rotateAnimation.timingMode = .linear
rotateAnimationback.timingMode = .linear
headNode.runAction(sequence)
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000)) {
self.playIntroAnimation = true
self.headNode.removeAllActions()
}
} }
override func viewWillDisappear(_ animated: Bool) { override func viewWillDisappear(_ animated: Bool) {
@ -68,31 +513,112 @@ class AccelerometerViewController: ModuleViewController {
// MARK: - UI // MARK: - UI
private func updateValueUI() { private func updateValueUI() {
if playIntroAnimation == true {
// Calculate Euler Angles // Calculate Euler Angles
let eulerAngles = eulerAnglesFromAcceleration() let eulerAngles = eulerAnglesFromAcceleration()
let eulerAnglesForHead = eulerAnglesFromAccelerationForHead()
//DLog("Euler: pitch: \(eulerAngles.x) yaw: \(eulerAngles.y) roll: \(eulerAngles.z)") //DLog("Euler: pitch: \(eulerAngles.x) yaw: \(eulerAngles.y) roll: \(eulerAngles.z)")
// Update circuit model orientation // Update circuit model orientation
SCNTransaction.animationDuration = BlePeripheral.kCPBAcceleromterDefaultPeriod SCNTransaction.animationDuration = BlePeripheral.kCPBAcceleromterDefaultPeriod
circuitNode.eulerAngles = eulerAngles
jawNode.eulerAngles = eulerAngles
headNode.eulerAngles = eulerAnglesForHead
// Update panel // Update panel
valuesPanelViewController.accelerationReceived(acceleration: self.acceleration, eulerAngles: eulerAngles) valuesPanelViewController.accelerationReceived(acceleration: self.acceleration, eulerAngles: eulerAngles)
}
} }
private func eulerAnglesFromAcceleration() -> SCNVector3 { private func eulerAnglesFromAcceleration() -> SCNVector3 {
// https://robotics.stackexchange.com/questions/6953/how-to-calculate-euler-angles-from-gyroscope-output // https://robotics.stackexchange.com/questions/6953/how-to-calculate-euler-angles-from-gyroscope-output
let accelAngleX = atan2(acceleration.y, acceleration.z) let accelAngleX = atan2(acceleration.y, acceleration.z)
// let accelAngleY = atan2(-acceleration.x, sqrt(acceleration.y*acceleration.y + acceleration.z*acceleration.z))
accelerometerX.update(newValue: accelAngleX)
return SCNVector3(accelerometerX.value.clamped(min: 0.13, max: 0.8), 0, 0)
}
private func eulerAnglesFromAccelerationForHead() -> SCNVector3 {
// https://robotics.stackexchange.com/questions/6953/how-to-calculate-euler-angles-from-gyroscope-output
let accelAngleX = atan2(acceleration.y, acceleration.z)
let accelAngleY = atan2(-acceleration.x, sqrt(acceleration.y*acceleration.y + acceleration.z*acceleration.z)) let accelAngleY = atan2(-acceleration.x, sqrt(acceleration.y*acceleration.y + acceleration.z*acceleration.z))
return SCNVector3(accelAngleX, accelAngleY, 0) let accelAngleYALT = atan2(-acceleration.x, sqrt(acceleration.y*acceleration.y) + acceleration.z*acceleration.z)
accelerometerX.update(newValue: accelAngleX)
accelerometerY.update(newValue: accelAngleYALT)
return SCNVector3(-accelerometerX.value.clamped(min: 0.1, max: 0.7), -accelerometerY.value, accelerometerY.value)
} }
// Timing function that has a "bounce in" effect
func easeOutElastic(_ t: Float) -> Float {
let p: Float = 0.3
let result = pow(2.0, -5.0 * t) * sin((t - p / 4.0) * (2.0 * Float.pi) / p) + 1.0
return result
}
} }
// MARK: - CPBBleAccelerometerDelegate // MARK: - CPBBleAccelerometerDelegate
extension AccelerometerViewController: CPBBleAccelerometerDelegate { extension AccelerometerViewController: CPBBleAccelerometerDelegate {
func cpbleAccelerationReceived(_ acceleration: BlePeripheral.AccelerometerValue) { func cpbleAccelerationReceived(_ acceleration: BlePeripheral.AccelerometerValue) {
self.acceleration = acceleration self.acceleration = acceleration
updateValueUI() updateValueUI()
} }
} }
extension Float {
func clamped(min min: Float, max: Float) -> Float {
if self < min {
return min
}
if self > max {
return max
}
return self
}
}
extension AccelerometerViewController : RPPreviewViewControllerDelegate {
func previewControllerDidFinish(_ previewController: RPPreviewViewController) {
dismiss(animated: true, completion: nil)
}
}
extension UIView {
@IBInspectable
var newCornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.cornerRadius = newValue
}
}
}
extension Int {
var degreesToRadians: Double { return Double(self) * .pi/180}
}

View file

@ -1,444 +0,0 @@
//
// AutoConnectViewController.swift
// BluefruitPlayground
//
// Created by Antonio García on 28/01/2020.
// Copyright © 2020 Adafruit. All rights reserved.
//
import UIKit
class AutoConnectViewController: UIViewController {
// Constants
static let kNavigationControllerIdentifier = "AutoConnectNavigationController"
// Config
private static let kMinScanningTimeToAutoconnect: TimeInterval = 5
// UI
@IBOutlet weak var statusLabel: UILabel!
@IBOutlet weak var scanManuallyButton: UIButton!
@IBOutlet weak var problemButton: CornerShadowButton!
@IBOutlet weak var wave0ImageView: UIImageView!
@IBOutlet weak var wave1ImageView: UIImageView!
@IBOutlet weak var wave2ImageView: UIImageView!
@IBOutlet weak var detailLabel: UILabel!
@IBOutlet weak var cpbContainerView: UIView!
@IBOutlet weak var cpbImageView: UIImageView!
@IBOutlet weak var actionsContainerView: UIStackView!
// Data
private let bleManager = Config.bleManager
private var peripheralList = PeripheralList(bleManager: Config.bleManager)
private var selectedPeripheral: BlePeripheral? {
didSet {
if isViewLoaded {
UIView.animate(withDuration: 0.3) {
self.actionsContainerView.alpha = self.selectedPeripheral == nil ? 1:0
}
}
}
}
private let navigationButton = UIButton(type: .custom)
private var isAnimating = false
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
// Localization
let localizationManager = LocalizationManager.shared
self.title = localizationManager.localizedString("autoconnect_title")
scanManuallyButton.setTitle(localizationManager.localizedString("autoconnect_manual_action").uppercased(), for: .normal)
problemButton.setTitle(localizationManager.localizedString("scanner_problems_action").uppercased(), for: .normal)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationItem.backBarButtonItem = nil // Clear any custom back button
if let customNavigationBar = navigationController?.navigationBar as? NavigationBarWithScrollAwareRightButton {
customNavigationBar.setRightButton(topViewController: self, image: UIImage(named: "info"), target: self, action: #selector(troubleshooting(_:)))
}
// Disconnect if needed
let connectedPeripherals = bleManager.connectedPeripherals()
if connectedPeripherals.count == 1, let peripheral = connectedPeripherals.first {
DLog("Disconnect from previously connected peripheral")
// Disconnect from peripheral
disconnect(peripheral: peripheral)
}
// UI
updateStatusLabel()
// Animations
if !isAnimating {
startAnimating()
}
// Ble Notifications
registerNotifications(enabled: true)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Flush any pending state notifications
didUpdateBleState()
// Update UI
updateScannedPeripherals()
// Start scannning
if !bleManager.isScanning {
bleManager.startScan()
}
// Remove saved peripheral for autoconnect
Settings.autoconnectPeripheralIdentifier = nil
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Stop scanning
bleManager.stopScan()
// Clear peripherals
peripheralList.clear()
// Animations
stopAnimating()
// Ble Notifications
registerNotifications(enabled: false)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is ScannerViewController {
// Go to scanner screen
let backItem = UIBarButtonItem()
backItem.title = LocalizationManager.shared.localizedString("autoconnect_backbutton")
self.navigationItem.backBarButtonItem = backItem
}
}
// MARK: - BLE Notifications
private weak var didUpdateBleStateObserver: NSObjectProtocol?
private weak var didDiscoverPeripheralObserver: NSObjectProtocol?
private weak var willConnectToPeripheralObserver: NSObjectProtocol?
private weak var didConnectToPeripheralObserver: NSObjectProtocol?
private weak var didDisconnectFromPeripheralObserver: NSObjectProtocol?
private weak var peripheralDidUpdateNameObserver: NSObjectProtocol?
private weak var willDiscoverServicesObserver: NSObjectProtocol?
private func registerNotifications(enabled: Bool) {
let notificationCenter = NotificationCenter.default
if enabled {
didUpdateBleStateObserver = notificationCenter.addObserver(forName: .didUpdateBleState, object: nil, queue: .main, using: {[weak self] _ in self?.didUpdateBleState()})
didDiscoverPeripheralObserver = notificationCenter.addObserver(forName: .didDiscoverPeripheral, object: nil, queue: .main, using: {[weak self] _ in self?.didDiscoverPeripheral()})
willConnectToPeripheralObserver = notificationCenter.addObserver(forName: .willConnectToPeripheral, object: nil, queue: .main, using: {[weak self] notification in self?.willConnectToPeripheral(notification: notification)})
didConnectToPeripheralObserver = notificationCenter.addObserver(forName: .didConnectToPeripheral, object: nil, queue: .main, using: {[weak self] notification in self?.didConnectToPeripheral(notification: notification)})
didDisconnectFromPeripheralObserver = notificationCenter.addObserver(forName: .didDisconnectFromPeripheral, object: nil, queue: .main, using: {[weak self] notification in self?.didDisconnectFromPeripheral(notification: notification)})
peripheralDidUpdateNameObserver = notificationCenter.addObserver(forName: .peripheralDidUpdateName, object: nil, queue: .main, using: {[weak self] notification in self?.peripheralDidUpdateName(notification: notification)})
willDiscoverServicesObserver = notificationCenter.addObserver(forName: .willDiscoverServices, object: nil, queue: .main, using: {[weak self] notification in self?.willDiscoverServices(notification: notification)})
} else {
if let didUpdateBleStateObserver = didUpdateBleStateObserver {notificationCenter.removeObserver(didUpdateBleStateObserver)}
if let didDiscoverPeripheralObserver = didDiscoverPeripheralObserver {notificationCenter.removeObserver(didDiscoverPeripheralObserver)}
if let willConnectToPeripheralObserver = willConnectToPeripheralObserver {notificationCenter.removeObserver(willConnectToPeripheralObserver)}
if let didConnectToPeripheralObserver = didConnectToPeripheralObserver {notificationCenter.removeObserver(didConnectToPeripheralObserver)}
if let didDisconnectFromPeripheralObserver = didDisconnectFromPeripheralObserver {notificationCenter.removeObserver(didDisconnectFromPeripheralObserver)}
if let peripheralDidUpdateNameObserver = peripheralDidUpdateNameObserver {notificationCenter.removeObserver(peripheralDidUpdateNameObserver)}
if let willDiscoverServicesObserver = willDiscoverServicesObserver {notificationCenter.removeObserver(willDiscoverServicesObserver)}
}
}
private func didUpdateBleState() {
guard Config.isBleUnsupportedWarningEnabled else { return }
guard let state = bleManager.centralManager?.state else { return }
// Check if there is any error
var errorMessageId: String?
switch state {
case .unsupported:
errorMessageId = "bluetooth_unsupported"
case .unauthorized:
errorMessageId = "bluetooth_notauthorized"
case .poweredOff:
errorMessageId = "bluetooth_poweredoff"
default:
errorMessageId = nil
}
// Show alert if error found
if let errorMessageId = errorMessageId {
let localizationManager = LocalizationManager.shared
let errorMessage = localizationManager.localizedString(errorMessageId)
DLog("Error: \(errorMessage)")
// Reload peripherals
refreshPeripherals()
// Show error
let alertController = UIAlertController(title: localizationManager.localizedString("dialog_error"), message: errorMessage, preferredStyle: .alert)
let okAction = UIAlertAction(title: localizationManager.localizedString("dialog_ok"), style: .default, handler: { (_) -> Void in
if let navController = self.splitViewController?.viewControllers[0] as? UINavigationController {
navController.popViewController(animated: true)
}
})
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil)
}
}
private func didDiscoverPeripheral() {
// Update current scanning state
updateScannedPeripherals()
}
private func willConnectToPeripheral(notification: Notification) {
guard let selectedPeripheral = selectedPeripheral, let identifier = notification.userInfo?[BleManager.NotificationUserInfoKey.uuid.rawValue] as? UUID, selectedPeripheral.identifier == identifier else {
DLog("willConnect to an unexpected peripheral")
return
}
let localizationManager = LocalizationManager.shared
updateStatusLabel()
detailLabel.text = localizationManager.localizedString("scanner_connecting")
}
private func didConnectToPeripheral(notification: Notification) {
guard let selectedPeripheral = selectedPeripheral, let identifier = notification.userInfo?[BleManager.NotificationUserInfoKey.uuid.rawValue] as? UUID, selectedPeripheral.identifier == identifier else {
DLog("didConnect to an unexpected peripheral")
return
}
// Setup peripheral
CPBBle.shared.setupPeripheral(blePeripheral: selectedPeripheral) { [weak self] result in
guard let self = self else { return }
switch result {
case .success():
DLog("setupPeripheral success")
// Finished setup
self.showPeripheralDetails()
case .failure(let error):
DLog("setupPeripheral error: \(error.localizedDescription)")
let localizationManager = LocalizationManager.shared
let alertController = UIAlertController(title: localizationManager.localizedString("dialog_error"), message: localizationManager.localizedString("uart_error_peripheralinit"), preferredStyle: .alert)
let okAction = UIAlertAction(title: localizationManager.localizedString("dialog_ok"), style: .default, handler: nil)
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil)
self.disconnect(peripheral: selectedPeripheral)
}
}
}
private func willDiscoverServices(notification: Notification) {
detailLabel.text = LocalizationManager.shared.localizedString("scanner_discoveringservices")
}
private func didDisconnectFromPeripheral(notification: Notification) {
let peripheral = bleManager.peripheral(from: notification)
let currentlyConnectedPeripheralsCount = bleManager.connectedPeripherals().count
guard let selectedPeripheral = selectedPeripheral, selectedPeripheral.identifier == peripheral?.identifier || currentlyConnectedPeripheralsCount == 0 else { // If selected peripheral is disconnected or if there are no peripherals connected (after a failed dfu update)
return
}
// Clear selected peripheral
self.selectedPeripheral = nil
// UI
updateStatusLabel()
}
private func peripheralDidUpdateName(notification: Notification) {
let name = notification.userInfo?[BlePeripheral.NotificationUserInfoKey.name.rawValue] as? String
DLog("centralManager peripheralDidUpdateName: \(name ?? "<unknown>")")
updateStatusLabel()
}
// MARK: - Connections
private func connect(peripheral: BlePeripheral) {
// Connect to selected peripheral
selectedPeripheral = peripheral
bleManager.connect(to: peripheral)
}
private func disconnect(peripheral: BlePeripheral) {
selectedPeripheral = nil
bleManager.disconnect(from: peripheral)
}
// MARK: - Actions
@IBAction func troubleshooting(_ sender: Any) {
guard let viewController = storyboard?.instantiateViewController(withIdentifier: AboutViewController.kIdentifier) else { return }
self.present(viewController, animated: true, completion: nil)
}
@IBAction func scanManually(_ sender: Any) {
ScreenFlowManager.goToManualScan()
}
private func showPeripheralDetails() {
// Save selected peripheral for autoconnect
Settings.autoconnectPeripheralIdentifier = selectedPeripheral?.identifier
ScreenFlowManager.gotoCPBModules()
}
// MARK: - UI
private func refreshPeripherals() {
bleManager.refreshPeripherals()
// reloadBaseTable()
}
private func updateScannedPeripherals() {
// Only update autoconnect if we are not already connecting to a peripheral
guard bleManager.connectedOrConnectingPeripherals().isEmpty else { return }
guard bleManager.scanningElapsedTime ?? 0 > AutoConnectViewController.kMinScanningTimeToAutoconnect else {
DLog("remaining scan time: \(AutoConnectViewController.kMinScanningTimeToAutoconnect - (bleManager.scanningElapsedTime ?? 0))")
return
}
// Sort by RSSI
let filteredPeripherals = peripheralList.filteredPeripherals(forceUpdate: true) // Refresh the peripherals
let sortedPeripherals = filteredPeripherals.sorted { (blePeripheral0, blePeripheral1) -> Bool in
return blePeripheral0.rssi ?? -127 > blePeripheral1.rssi ?? -127
}
//DLog("peripherals: \(sortedPeripherals.count)")
// Connect to closest CPB
guard let peripheral = sortedPeripherals.first else { return }
connect(peripheral: peripheral)
}
private func updateStatusLabel() {
let localizationManager = LocalizationManager.shared
let statusText: String
if let selectedPeripheral = selectedPeripheral {
statusText = "Device found:\n\(selectedPeripheral.name ?? localizationManager.localizedString("scanner_unnamed"))"
// Animate found CPB
UIView.animate(withDuration: 0.1, animations: {
self.cpbContainerView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
}) { finished in
if finished {
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0.5, options: [.curveEaseOut], animations: {
self.cpbContainerView.transform = .identity
}, completion: nil)
UIView.animate(withDuration: 0.3) {
self.cpbImageView.alpha = 1
}
}
}
}
else {
UIView.animate(withDuration: 0.3) {
self.cpbImageView.alpha = 0.2
}
statusText = localizationManager.localizedString("scanner_searching")
detailLabel.text = " "
}
statusLabel.text = statusText
}
}
// MARK: UIScrollViewDelegate
extension AutoConnectViewController {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// NavigationBar Button Custom Animation
if let customNavigationBar = navigationController?.navigationBar as? NavigationBarWithScrollAwareRightButton {
customNavigationBar.updateRightButtonPosition()
}
}
}
// MARK: - Animations
extension AutoConnectViewController {
private func stopAnimating() {
isAnimating = false
if isViewLoaded {
let waveImageViews = [wave0ImageView, wave1ImageView]//, wave2ImageView]
for waveImageView in waveImageViews {
if let waveImageView = waveImageView {
waveImageView.layer.removeAllAnimations()
waveImageView.alpha = 0
}
}
}
}
private func startAnimating() {
isAnimating = true
guard isViewLoaded else { return }
let waveImageViews = [wave0ImageView, wave1ImageView]//, wave2ImageView]
// Scanning animation
let duration: Double = 8
let initialScaleFactor: CGFloat = 0.60
let finalScaleFactor: CGFloat = 1.10
let initialAlphaFactor: CGFloat = 0.80
let finalAlphaFactor: CGFloat = 0
// - Initial position
let introMaxValueFactor: CGFloat = 0.7
for (i, waveImageView) in waveImageViews.enumerated() {
if let waveImageView = waveImageView {
//DLog("intro: \(i)")
let factor: CGFloat = CGFloat(i) / CGFloat(waveImageViews.count-1) * introMaxValueFactor
let scaleFactor = (finalScaleFactor - initialScaleFactor) * factor + initialScaleFactor
waveImageView.transform = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor);
let alphaFactor = (finalAlphaFactor - initialAlphaFactor) * factor + initialAlphaFactor
waveImageView.alpha = alphaFactor
// DLog("\(i): factor: \(factor) scale: \(scaleFactor)")
let introDuration = (1 - Double(factor)) * duration
UIView.animate(withDuration: introDuration, delay: 0, options: [.curveEaseOut], animations: {
waveImageView.transform = CGAffineTransform(scaleX: finalScaleFactor, y: finalScaleFactor)
waveImageView.alpha = finalAlphaFactor
}, completion: { _ in
// Ongoing
waveImageView.transform = CGAffineTransform(scaleX: initialScaleFactor, y: initialScaleFactor);
waveImageView.alpha = initialAlphaFactor
UIView.animate(withDuration: duration, delay: 0, options: [.repeat, .curveEaseOut], animations: {
waveImageView.transform = CGAffineTransform(scaleX: finalScaleFactor, y: finalScaleFactor)
waveImageView.alpha = finalAlphaFactor
}, completion: nil)
})
}
}
}
}

View file

@ -1,85 +0,0 @@
//
// BluetoothStatusViewController.swift
// BluefruitPlayground
//
// Created by Antonio García on 30/01/2020.
// Copyright © 2020 Adafruit. All rights reserved.
//
import UIKit
import CoreBluetooth
class BluetoothStatusViewController: UIViewController {
// Constants
//static let kNavigationControllerIdentifier = "BluetoothStatusNavigationController"
static let kIdentifier = "BluetoothStatusViewController"
// UI
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var subtitleLabel: UILabel!
@IBOutlet weak var actionView: UIView!
@IBOutlet weak var actionButton: UIButton!
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
// Localization
actionButton.setTitle(LocalizationManager.shared.localizedString("bluetooth_enable_action").uppercased(), for: .normal)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Set text messages depending on current state
let messageTitle: String
let message: String
let bluetoothState = Config.bleManager.state
var isActionHidden: Bool
switch bluetoothState {
case .unauthorized:
messageTitle = "bluetooth_notauthorized"
message = "bluetooth_notauthorized_detail"
isActionHidden = false
case .unsupported:
messageTitle = "bluetooth_unsupported_le"
message = "bluetooth_unsupported_le_detail"
isActionHidden = true
case .poweredOff:
messageTitle = "bluetooth_poweredoff"
message = "bluetooth_poweredoff_detail"
isActionHidden = false
default:
DLog("Error: StatusBluetoothViewController in wrong state: \(bluetoothState)")
messageTitle = "bluetooth_unsupported"
message = "bluetooth_unsupported_detail"
isActionHidden = true
break
}
let localizationManager = LocalizationManager.shared
titleLabel.text = localizationManager.localizedString(messageTitle)
subtitleLabel.text = localizationManager.localizedString(message)
let settingsUrl = URL(string: UIApplication.openSettingsURLString)
actionView.isHidden = isActionHidden || settingsUrl == nil || !UIApplication.shared.canOpenURL(settingsUrl!)
}
// MARK: - Actions
@IBAction func enableBluetooth(_ sender: Any) {
let bluetoothState = Config.bleManager.state
if bluetoothState == .poweredOff {
// Force iOS to show the "Turn on bluetooth" alert
let _ = CBCentralManager(delegate: nil, queue: .main, options: [CBCentralManagerOptionShowPowerAlertKey: true])
}
else {
// Go to settings
if let settingsUrl = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(settingsUrl)
}
}
}
}

View file

@ -38,6 +38,16 @@ class ButtonStatusPanelViewController: ModulePanelViewController {
// Init // Init
switchImageView.tintColor = offColor switchImageView.tintColor = offColor
// Read initial state
CPBBle.shared.buttonsReadState { [weak self] response in
switch response {
case let .success(buttonsStatus, _):
self?.buttonsStateReceived(buttonsStatus)
case .failure(let error):
DLog("Error receiving temperature data: \(error)")
}
}
// Localization // Localization
let localizationManager = LocalizationManager.shared let localizationManager = LocalizationManager.shared
titleLabel.text = localizationManager.localizedString("buttonstatus_panel_title") titleLabel.text = localizationManager.localizedString("buttonstatus_panel_title")
@ -57,6 +67,8 @@ class ButtonStatusPanelViewController: ModulePanelViewController {
} }
private func animateDown(view: UIView) { private func animateDown(view: UIView) {
UIView.animate(withDuration: 0.2) { UIView.animate(withDuration: 0.2) {
view.transform = CGAffineTransform(scaleX: 1.2, y: 1.2) view.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
view.tintColor = self.onColor view.tintColor = self.onColor
@ -88,6 +100,7 @@ class ButtonStatusPanelViewController: ModulePanelViewController {
if buttonsState.buttonA != currentState.buttonA { if buttonsState.buttonA != currentState.buttonA {
animateState(view: buttonAStatusView, isPressed: buttonsState.buttonA == .pressed) animateState(view: buttonAStatusView, isPressed: buttonsState.buttonA == .pressed)
// NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Command"), object: nil)
} }
if buttonsState.buttonB != currentState.buttonB { if buttonsState.buttonB != currentState.buttonB {

View file

@ -10,7 +10,6 @@ import UIKit
class HomeViewController: UIViewController { class HomeViewController: UIViewController {
// Constants // Constants
static let kNavigationControllerIdentifier = "ModulesNavigationController"
static let kIdentifier = "HomeViewController" static let kIdentifier = "HomeViewController"
// UI // UI
@ -67,60 +66,18 @@ class HomeViewController: UIViewController {
super.viewDidLoad() super.viewDidLoad()
// Setup UI // Setup UI
/*
let topContentInsetForDetails: CGFloat = 20 let topContentInsetForDetails: CGFloat = 20
baseTableView.contentInset = UIEdgeInsets(top: topContentInsetForDetails, left: 0, bottom: 0, right: 0) baseTableView.contentInset = UIEdgeInsets(top: topContentInsetForDetails, left: 0, bottom: 0, right: 0)
*/
// Localization // Localization
let localizationManager = LocalizationManager.shared let localizationManager = LocalizationManager.shared
self.title = localizationManager.localizedString("modules_title") self.title = localizationManager.localizedString("modules_title")
} }
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
/*
if let customNavigationBar = navigationController?.navigationBar as? NavigationBarWithScrollAwareRightButton {
customNavigationBar.setRightButton(topViewController: self, image: UIImage(named: "info"), target: self, action: #selector(about(_:)))
}*/
/*
if let peripheral = Config.bleManager.connectedPeripherals().first {
peripheral.readRssi()
}*/
}
/*
// MARK: - Actions
@IBAction func about(_ sender: Any) {
guard let viewController = storyboard?.instantiateViewController(withIdentifier: AboutViewController.kIdentifier) else { return }
self.present(viewController, animated: true, completion: nil)
}*/
} }
// MARK: - UITableViewDataSource // MARK: - UITableViewDataSource
extension HomeViewController: UITableViewDataSource { extension HomeViewController: UITableViewDataSource {
enum CellType {
case details
case module
case disconnect
var reuseIdentifier: String {
switch(self) {
case .details: return "DetailsCell"
case .module: return "ModuleCell"
case .disconnect: return "DisconnectCell"
}
}
}
private func cellTypeFromIndexPath(_ indexPath: IndexPath) -> CellType {
return indexPath.section == 0 ? .details : indexPath.row == menuItems.count ? .disconnect : .module
}
func numberOfSections(in tableView: UITableView) -> Int { func numberOfSections(in tableView: UITableView) -> Int {
return 2 // Details + Modules return 2 // Details + Modules
} }
@ -130,31 +87,23 @@ extension HomeViewController: UITableViewDataSource {
return 1 return 1
} }
else { else {
return menuItems.count + 1 // + 1 disconnect return menuItems.count
} }
} }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let reuseIdentifier = indexPath.section == 0 ? "DetailsCell" : "ModuleCell"
let cellType = cellTypeFromIndexPath(indexPath) let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: cellType.reuseIdentifier, for: indexPath)
// Add cell data here instead of using willDisplay to avoid problems with automatic dimension calculation // Add cell data here instead of using willDisplay to avoid problems with automatic dimension calculation
let localizationManager = LocalizationManager.shared let localizationManager = LocalizationManager.shared
let isDetails = indexPath.section == 0
switch(cellType) { if isDetails {
case .details:
let detailsCell = cell as! TitleTableViewCell let detailsCell = cell as! TitleTableViewCell
detailsCell.titleLabel.text = localizationManager.localizedString("modules_subtitle") detailsCell.titleLabel.text = localizationManager.localizedString("modules_subtitle")
/* }
case .peripheral else {
let peripheralCell = cell as! CommonTableViewCell
if let peripheral = Config.bleManager.connectedPeripherals().first {
// Fill peripheral data
peripheralCell.titleLabel.text = peripheral.name ?? localizationManager.localizedString("scanner_unnamed")
peripheralCell.iconImageView.image = RssiUI.signalImage(for: peripheral.rssi)
}*/
case .module:
let moduleCell = cell as! CommonTableViewCell let moduleCell = cell as! CommonTableViewCell
let menuItem = menuItems[indexPath.row] let menuItem = menuItems[indexPath.row]
@ -164,16 +113,12 @@ extension HomeViewController: UITableViewDataSource {
let subtitleStringId = menuItem.subtitleStringId let subtitleStringId = menuItem.subtitleStringId
moduleCell.subtitleLabel?.text = localizationManager.localizedString(subtitleStringId) moduleCell.subtitleLabel?.text = localizationManager.localizedString(subtitleStringId)
//moduleCell.setPanelBackgroundColor(menuItem.color)
moduleCell.iconImageView.backgroundColor = menuItem.color moduleCell.iconImageView.backgroundColor = menuItem.color
moduleCell.iconImageView.layer.borderColor = UIColor(named: "text_default")?.cgColor moduleCell.iconImageView.layer.borderColor = UIColor(named: "text_default")?.cgColor
moduleCell.iconImageView.layer.borderWidth = 1 moduleCell.iconImageView.layer.borderWidth = 1
moduleCell.iconImageView.layer.cornerRadius = 7 moduleCell.iconImageView.layer.cornerRadius = 7
moduleCell.iconImageView.layer.masksToBounds = true moduleCell.iconImageView.layer.masksToBounds = true
case .disconnect:
let disconnectCell = cell as! CommonTableViewCell
disconnectCell.titleLabel.text = localizationManager.localizedString("modules_disconnect_title")
disconnectCell.subtitleLabel?.text = localizationManager.localizedString("modules_disconnect_subtitle")
} }
return cell return cell
@ -190,12 +135,10 @@ extension HomeViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true) tableView.deselectRow(at: indexPath, animated: true)
let cellType = cellTypeFromIndexPath(indexPath) let isDetails = indexPath.section == 0
switch(cellType) { guard !isDetails else { return }
case .details:
break guard let module = Modules(rawValue: indexPath.row) else { return }
case .module:
if let module = Modules(rawValue: indexPath.row) {
var storyboardId: String? = nil var storyboardId: String? = nil
switch module { switch module {
case .color: case .color:
@ -214,34 +157,8 @@ extension HomeViewController: UITableViewDelegate {
if let identifier = storyboardId, let viewController = storyboard?.instantiateViewController(withIdentifier: identifier) { if let identifier = storyboardId, let viewController = storyboard?.instantiateViewController(withIdentifier: identifier) {
// Show viewController with completion block
CATransaction.begin()
self.show(viewController, sender: self) self.show(viewController, sender: self)
CATransaction.setCompletionBlock({
// Flash neopixels with the module color
CPBBle.shared.neopixelStartLightSequence(FlashLightSequence(baseColor: module.color), speed: 1, repeating: false, sendLightSequenceNotifications: false)
})
CATransaction.commit()
}
} }
case .disconnect:
if let peripheral = Config.bleManager.connectedPeripherals().first {
Config.bleManager.disconnect(from: peripheral, waitForQueuedCommands: true)
}
}
} }
} }
/*
// MARK: UIScrollViewDelegate
extension HomeViewController {
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// NavigationBar Button Custom Animation
if let customNavigationBar = navigationController?.navigationBar as? NavigationBarWithScrollAwareRightButton {
customNavigationBar.updateRightButtonPosition()
}
}
}
*/

View file

@ -1,110 +0,0 @@
//
// LightPanelViewController.swift
// BluefruitPlayground
//
// Created by Antonio García on 31/01/2020.
// Copyright © 2020 Adafruit. All rights reserved.
//
import UIKit
import Charts
class LightChartPanelViewController: ModulePanelViewController {
// Constants
static let kIdentifier = "LightPanelViewController"
// UI
@IBOutlet weak var chartView: LineChartView!
// Data
private var isAutoScrollEnabled = true
private var visibleInterval: TimeInterval = 20 // in seconds
private var dataSet: LineChartDataSet!
private var originTimestamp: CFAbsoluteTime!
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupChart()
// Localization
let localizationManager = LocalizationManager.shared
titleLabel.text = localizationManager.localizedString("lightsensor_chartpanel_title")
}
// MARK: - Line Chart
private func setupChart() {
//chartView.delegate = self
chartView.backgroundColor = .clear // Fix for Charts 3.0.3 (overrides the default background color)
chartView.dragEnabled = false
chartView.isUserInteractionEnabled = false
chartView.chartDescription?.enabled = false
chartView.xAxis.granularityEnabled = true
chartView.xAxis.granularity = 5
chartView.rightAxis.enabled = false
// chartView.rightAxis.valueFormatter =
//chartView.leftAxis.drawZeroLineEnabled = true
//chartView.setExtraOffsets(left: 10, top: 10, right: 10, bottom: 0)
chartView.legend.enabled = false
chartView.noDataText = LocalizationManager.shared.localizedString("temperature_chart_nodata")
// Timestamp
let lightDataSeries = CPBBle.shared.lightDataSeries()
originTimestamp = lightDataSeries.first?.timestamp ?? CFAbsoluteTimeGetCurrent()
// Load initial data
reloadChartEntries()
}
private func reloadChartEntries() {
let lightDataSeries = CPBBle.shared.lightDataSeries()
let chartEntries = lightDataSeries.map { entry -> ChartDataEntry in
let lightReading = Double(entry.value)
return ChartDataEntry(x: entry.timestamp - originTimestamp, y: lightReading)
}
// Add Dataset
dataSet = LineChartDataSet(entries: chartEntries, label: "")
dataSet.drawCirclesEnabled = false
dataSet.drawValuesEnabled = false
dataSet.lineWidth = 2
dataSet.setColor(UIColor.blue)
//dataSet.lineDashLengths = lineDashForPeripheral[identifier]!
//DLog("color: \(color.hexString()!)")
// Set dataset
chartView.data = LineChartData(dataSet: dataSet)
}
private func notifyDataSetChanged() {
let isViewVisible = self.viewIfLoaded?.window != nil // https://stackoverflow.com/questions/2777438/how-to-tell-if-uiviewcontrollers-view-is-visible
guard isViewVisible else { return }
chartView.data?.notifyDataChanged()
chartView.notifyDataSetChanged()
chartView.setVisibleXRangeMaximum(visibleInterval)
chartView.setVisibleXRangeMinimum(visibleInterval)
if isAutoScrollEnabled {
let xOffset = (dataSet.entries.last?.x ?? 0) - (visibleInterval-1)
chartView.moveViewToX(xOffset)
}
}
// MARK: - Actions
func lightValueReceived() {
guard let lastLightDataSeries = CPBBle.shared.lightDataSeries().last else { return }
let light = Double(lastLightDataSeries.value)
let entry = ChartDataEntry(x: lastLightDataSeries.timestamp - originTimestamp, y: light)
let _ = dataSet.append(entry)
notifyDataSetChanged()
}
}

View file

@ -16,7 +16,6 @@ class LightSensorViewController: ModuleViewController {
private var circuitViewController: CircuitViewController! private var circuitViewController: CircuitViewController!
private var lightmeterPanelViewController: LightSensorPanelViewController! private var lightmeterPanelViewController: LightSensorPanelViewController!
private var light: Float? private var light: Float?
private var chartPanelViewController: LightChartPanelViewController!
// MARK: - Lifecycle // MARK: - Lifecycle
override func viewDidLoad() { override func viewDidLoad() {
@ -25,9 +24,6 @@ class LightSensorViewController: ModuleViewController {
// Add panels // Add panels
lightmeterPanelViewController = (addPanelViewController(storyboardIdentifier: LightSensorPanelViewController.kIdentifier) as! LightSensorPanelViewController) lightmeterPanelViewController = (addPanelViewController(storyboardIdentifier: LightSensorPanelViewController.kIdentifier) as! LightSensorPanelViewController)
chartPanelViewController = (addPanelViewController(storyboardIdentifier: LightChartPanelViewController.kIdentifier) as! LightChartPanelViewController)
// Localization // Localization
let localizationManager = LocalizationManager.shared let localizationManager = LocalizationManager.shared
self.title = localizationManager.localizedString("lightsensor_title") self.title = localizationManager.localizedString("lightsensor_title")
@ -65,9 +61,6 @@ class LightSensorViewController: ModuleViewController {
private func updateValueUI() { private func updateValueUI() {
if let light = self.light { if let light = self.light {
lightmeterPanelViewController.lightValueReceived(light) lightmeterPanelViewController.lightValueReceived(light)
// Update chart
chartPanelViewController.lightValueReceived()
} }
} }
} }

View file

@ -44,12 +44,7 @@ class CircuitViewController: UIViewController {
} }
// Init neopixels color // Init neopixels color
for (i, neopixelView) in self.neopixelsContainerView.subviews.enumerated() { neopixelsReset(animated: false)
if i < self.isNeopixelSelected.count {
neopixelView.backgroundColor = .clear
}
}
//neopixelsReset(animated: false)
} }
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
@ -103,6 +98,7 @@ class CircuitViewController: UIViewController {
} }
} }
// MARK: - Notifications // MARK: - Notifications
private var didUpdateNeopixelLightSequenceObserver: NSObjectProtocol? private var didUpdateNeopixelLightSequenceObserver: NSObjectProtocol?
@ -126,6 +122,7 @@ class CircuitViewController: UIViewController {
} }
} }
} }
} }
} else { } else {
if let didUpdateNeopixelLightSequenceObserver = didUpdateNeopixelLightSequenceObserver {notificationCenter.removeObserver(didUpdateNeopixelLightSequenceObserver)} if let didUpdateNeopixelLightSequenceObserver = didUpdateNeopixelLightSequenceObserver {notificationCenter.removeObserver(didUpdateNeopixelLightSequenceObserver)}
@ -171,6 +168,7 @@ class CircuitViewController: UIViewController {
// Animate // Animate
UIView.animate(withDuration: 0.2, animations: { UIView.animate(withDuration: 0.2, animations: {
selectedView.transform = isSelected ? .identity:CGAffineTransform(scaleX: 1.2, y: 1.2) selectedView.transform = isSelected ? .identity:CGAffineTransform(scaleX: 1.2, y: 1.2)
//selectedView.layer.borderWidth = self.selectedWidth// isSelected ? self.selectedWidth:0
selectedView.alpha = isSelected ? 1:CircuitViewController.kUnselectedButtonAlpha selectedView.alpha = isSelected ? 1:CircuitViewController.kUnselectedButtonAlpha
}) { (_) in }) { (_) in
selectedView.transform = .identity selectedView.transform = .identity
@ -183,7 +181,7 @@ class CircuitViewController: UIViewController {
} }
// MARK: - Actions // MARK: - Actions
func setNeopixelsColor(_ color: UIColor, onlySelected: Bool, animated: Bool, baseColor: UIColor? = nil) { func setNeopixelsColor(_ color: UIColor, onlySelected: Bool, animated: Bool) {
if onlySelected { if onlySelected {
CPBBle.shared.neopixelSetPixelColor(color, pixelMask: isNeopixelSelected) CPBBle.shared.neopixelSetPixelColor(color, pixelMask: isNeopixelSelected)
@ -194,10 +192,9 @@ class CircuitViewController: UIViewController {
// UI Animation // UI Animation
UIView.animate(withDuration: animated ? 0.1: 0) { UIView.animate(withDuration: animated ? 0.1: 0) {
// Base color is the color withouth the brightness. Used in the circuit representation to show the colors more vividly
for (i, neopixelView) in self.neopixelsContainerView.subviews.enumerated() { for (i, neopixelView) in self.neopixelsContainerView.subviews.enumerated() {
if i < self.isNeopixelSelected.count && (!onlySelected || self.isNeopixelSelected[i]) { if i < self.isNeopixelSelected.count && (!onlySelected || self.isNeopixelSelected[i]) {
neopixelView.backgroundColor = baseColor ?? color neopixelView.backgroundColor = color
} }
} }
} }

View file

@ -80,9 +80,9 @@ class NeoPixelsViewController: ModuleViewController {
circuitViewController.neopixelsReset(animated: true) circuitViewController.neopixelsReset(animated: true)
} }
private func selectColor(_ color: UIColor, baseColor: UIColor) { private func selectColor(_ color: UIColor) {
// Update circuit // Update circuit
circuitViewController.setNeopixelsColor(color, onlySelected: true, animated: true, baseColor: baseColor) circuitViewController.setNeopixelsColor(color, onlySelected: true, animated: true)
} }
// MARK: - Page Management // MARK: - Page Management
@ -104,15 +104,14 @@ class NeoPixelsViewController: ModuleViewController {
// MARK: - NeopixelsColorPaletteViewControllerDelegate // MARK: - NeopixelsColorPaletteViewControllerDelegate
extension NeoPixelsViewController: NeopixelsColorPaletteViewControllerDelegate { extension NeoPixelsViewController: NeopixelsColorPaletteViewControllerDelegate {
func colorPaletteColorSelected(color: UIColor, baseColor: UIColor) { func colorPaletteColorSelected(color: UIColor) {
selectColor(color, baseColor: baseColor) selectColor(color)
} }
} }
// MARK: - NeopixelColorWheelViewControllerDelegate // MARK: - NeopixelsColorPaletteViewControllerDelegate
extension NeoPixelsViewController: NeopixelColorWheelViewControllerDelegate { extension NeoPixelsViewController: NeopixelColorWheelViewControllerDelegate {
func colorWheelColorSelected(color: UIColor, baseColor: UIColor) { func colorWheelColorSelected(color: UIColor) {
selectColor(color, baseColor: baseColor) selectColor(color)
} }
} }

View file

@ -10,7 +10,7 @@ import UIKit
import FlexColorPicker import FlexColorPicker
protocol NeopixelsColorPaletteViewControllerDelegate: class { protocol NeopixelsColorPaletteViewControllerDelegate: class {
func colorPaletteColorSelected(color: UIColor, baseColor: UIColor) func colorPaletteColorSelected(color: UIColor)
} }
class NeopixelsColorPaletteViewController: ModulePanelViewController { class NeopixelsColorPaletteViewController: ModulePanelViewController {
@ -124,6 +124,6 @@ class NeopixelsColorPaletteViewController: ModulePanelViewController {
let hsbColor = HSBColor(color: color) let hsbColor = HSBColor(color: color)
let colorWithBrighness = hsbColor.withBrightness(brightness).toUIColor() let colorWithBrighness = hsbColor.withBrightness(brightness).toUIColor()
delegate?.colorPaletteColorSelected(color: colorWithBrighness, baseColor: color) delegate?.colorPaletteColorSelected(color: colorWithBrighness)
} }
} }

View file

@ -10,7 +10,7 @@ import UIKit
import FlexColorPicker import FlexColorPicker
protocol NeopixelColorWheelViewControllerDelegate: class { protocol NeopixelColorWheelViewControllerDelegate: class {
func colorWheelColorSelected(color: UIColor, baseColor: UIColor) func colorWheelColorSelected(color: UIColor)
} }
class NeopixelsColorWheelViewController: ModulePanelViewController { class NeopixelsColorWheelViewController: ModulePanelViewController {
@ -96,7 +96,7 @@ class NeopixelsColorWheelViewController: ModulePanelViewController {
let hsbColor = HSBColor(color: color) let hsbColor = HSBColor(color: color)
let colorWithBrighness = hsbColor.withBrightness(brightness).toUIColor() let colorWithBrighness = hsbColor.withBrightness(brightness).toUIColor()
delegate?.colorWheelColorSelected(color: colorWithBrighness, baseColor: color) delegate?.colorWheelColorSelected(color: colorWithBrighness)
} }
} }

View file

@ -20,6 +20,7 @@ class NeopixelsLightSequenceViewController: ModulePanelViewController {
// Data // Data
private var previewViewControllers = [PixelsPreviewViewController]() private var previewViewControllers = [PixelsPreviewViewController]()
private var speed: Double = 0.3
// MARK: - Lifecycle // MARK: - Lifecycle
override func viewDidLoad() { override func viewDidLoad() {
@ -29,10 +30,8 @@ class NeopixelsLightSequenceViewController: ModulePanelViewController {
let _ = previewViewControllers.map{$0.tag = $0.view.superview?.tag ?? 0} let _ = previewViewControllers.map{$0.tag = $0.view.superview?.tag ?? 0}
// Set initial speed // Set initial speed
let speed = CPBBle.kLightSequenceDefaultSpeed
speedSlider.value = Float(speed) speedSlider.value = Float(speed)
//speedChanged(speedSlider) speedChanged(speedSlider)
let _ = previewViewControllers.map{$0.speed = speed}
// Localization // Localization
let localizationManager = LocalizationManager.shared let localizationManager = LocalizationManager.shared
@ -48,10 +47,10 @@ class NeopixelsLightSequenceViewController: ModulePanelViewController {
} }
@IBAction func speedChanged(_ sender: UISlider) { @IBAction func speedChanged(_ sender: UISlider) {
let speed = Double(sender.value) speed = Double(sender.value)
DLog("speed: \(speed)") DLog("speed: \(speed)")
CPBBle.shared.neopixelCurrentLightSequenceAnimationSpeed = speed CPBBle.shared.neopixelLightSequenceAnimationSpeed = speed
let _ = previewViewControllers.map{$0.speed = speed} let _ = previewViewControllers.map{$0.speed = speed}
} }
} }

View file

@ -52,7 +52,7 @@ class PixelsPreviewViewController: UIViewController {
guard let lightSequenceGenerator = lightSequenceGeneratorForTag(self.tag) else { return } guard let lightSequenceGenerator = lightSequenceGeneratorForTag(self.tag) else { return }
lightSequenceAnimation = LightSequenceAnimation(lightSequenceGenerator: lightSequenceGenerator, framesPerSecond: 10, repeating: true) lightSequenceAnimation = LightSequenceAnimation(lightSequenceGenerator: lightSequenceGenerator, framesPerSecond: 10)
lightSequenceAnimation!.speed = speed lightSequenceAnimation!.speed = speed
lightSequenceAnimation!.start() { [weak self] pixelsBytes in lightSequenceAnimation!.start() { [weak self] pixelsBytes in
guard let self = self else { return } guard let self = self else { return }
@ -72,8 +72,6 @@ class PixelsPreviewViewController: UIViewController {
override func viewWillDisappear(_ animated: Bool) { override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated) super.viewWillDisappear(animated)
lightSequenceAnimation?.stop()
} }
override func viewDidLayoutSubviews() { override func viewDidLayoutSubviews() {

View file

@ -70,17 +70,6 @@ class AboutViewController: UIViewController {
UIApplication.shared.open(url, options: [:], completionHandler: nil) UIApplication.shared.open(url, options: [:], completionHandler: nil)
} }
} }
label.configureLinkAttribute = { (type, attributes, isSelected) in
var atts = attributes
switch type {
case customType0, customType1:
atts[.underlineStyle] = NSUnderlineStyle.single.rawValue
default: ()
}
return atts
}
} }
} }

View file

@ -55,16 +55,6 @@ extension ScanProblemsViewController: UITableViewDataSource {
UIApplication.shared.open(url, options: [:], completionHandler: nil) UIApplication.shared.open(url, options: [:], completionHandler: nil)
} }
} }
label.configureLinkAttribute = { (type, attributes, isSelected) in
var atts = attributes
switch type {
case customType:
atts[.underlineStyle] = NSUnderlineStyle.single.rawValue
default: ()
}
return atts
}
} }

View file

@ -11,9 +11,10 @@ import CoreBluetooth
class ScannerViewController: UIViewController { class ScannerViewController: UIViewController {
// Constants // Constants
static let kNavigationControllerIdentifier = "ScannerNavigationController" static let kIdentifier = "ScannerNavigationController" //"ScannerViewController"
// Config // Config
private static let kServicesToScan: [CBUUID]? = nil //[BlePeripheral.kUartServiceUUID]
private static let kDelayToShowWait: TimeInterval = 1.0 private static let kDelayToShowWait: TimeInterval = 1.0
// UI // UI
@ -21,23 +22,13 @@ class ScannerViewController: UIViewController {
@IBOutlet weak var waitView: UIView! @IBOutlet weak var waitView: UIView!
@IBOutlet weak var waitLabel: UILabel! @IBOutlet weak var waitLabel: UILabel!
@IBOutlet weak var problemsButton: UIButton! @IBOutlet weak var problemsButton: UIButton!
@IBOutlet weak var scanAutomaticallyButton: CornerShadowButton!
@IBOutlet weak var actionsContainerView: UIStackView!
// Data // Data
private let refreshControl = UIRefreshControl() private let refreshControl = UIRefreshControl()
private let bleManager = Config.bleManager private let bleManager = Config.bleManager
private var peripheralList = PeripheralList(bleManager: Config.bleManager) private var peripheralList = PeripheralList(bleManager: Config.bleManager)
private var selectedPeripheral: BlePeripheral? { private var selectedPeripheral: BlePeripheral?
didSet {
if isViewLoaded {
UIView.animate(withDuration: 0.3) {
self.actionsContainerView.alpha = self.selectedPeripheral == nil ? 1:0
}
}
}
}
private var infoAlertController: UIAlertController? private var infoAlertController: UIAlertController?
private var isBaseTableScrolling = false private var isBaseTableScrolling = false
@ -68,7 +59,6 @@ class ScannerViewController: UIViewController {
waitLabel.text = localizationManager.localizedString("scanner_searching") waitLabel.text = localizationManager.localizedString("scanner_searching")
problemsButton.setTitle(localizationManager.localizedString("scanner_problems_action").uppercased(), for: .normal) problemsButton.setTitle(localizationManager.localizedString("scanner_problems_action").uppercased(), for: .normal)
scanAutomaticallyButton.setTitle(localizationManager.localizedString("scanner_automatic_action").uppercased(), for: .normal)
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
@ -85,7 +75,7 @@ class ScannerViewController: UIViewController {
if connectedPeripherals.count == 1, let peripheral = connectedPeripherals.first { if connectedPeripherals.count == 1, let peripheral = connectedPeripherals.first {
DLog("Disconnect from previously connected peripheral") DLog("Disconnect from previously connected peripheral")
// Disconnect from peripheral // Disconnect from peripheral
disconnect(peripheral: peripheral) bleManager.disconnect(from: peripheral)
} }
} }
@ -99,17 +89,11 @@ class ScannerViewController: UIViewController {
updateScannedPeripherals() updateScannedPeripherals()
// Start scannning // Start scannning
//bleManager.startScan(withServices: ScannerViewController.kServicesToScan) bleManager.startScan(withServices: ScannerViewController.kServicesToScan)
if !bleManager.isScanning {
bleManager.startScan()
}
// Remove saved peripheral for autoconnect
Settings.autoconnectPeripheralIdentifier = nil
} }
override func viewWillDisappear(_ animated: Bool) { override func viewDidDisappear(_ animated: Bool) {
super.viewWillDisappear(animated) super.viewDidDisappear(animated)
// Stop scanning // Stop scanning
bleManager.stopScan() bleManager.stopScan()
@ -119,6 +103,7 @@ class ScannerViewController: UIViewController {
} }
deinit { deinit {
// Ble Notifications // Ble Notifications
registerNotifications(enabled: false) registerNotifications(enabled: false)
} }
@ -232,7 +217,7 @@ class ScannerViewController: UIViewController {
alertController.addAction(okAction) alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil) self.present(alertController, animated: true, completion: nil)
self.disconnect(peripheral: selectedPeripheral) self.bleManager.disconnect(from: selectedPeripheral)
} }
} }
} }
@ -258,6 +243,18 @@ class ScannerViewController: UIViewController {
// Reload table // Reload table
reloadBaseTable() reloadBaseTable()
// If is not the topViewController, pop any other thing and come back to scanning
if self.navigationController?.topViewController !== self {
self.navigationController?.popToRootViewController(animated: false)
// Show disconnection alert
let localizationManager = LocalizationManager.shared
let alertController = UIAlertController(title: nil, message: localizationManager.localizedString("scanner_peripheraldisconnected"), preferredStyle: .alert)
let okAction = UIAlertAction(title: localizationManager.localizedString("dialog_ok"), style: .default, handler: nil)
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil)
}
} }
private func peripheralDidUpdateName(notification: Notification) { private func peripheralDidUpdateName(notification: Notification) {
@ -296,16 +293,17 @@ class ScannerViewController: UIViewController {
self.present(viewController, animated: true, completion: nil) self.present(viewController, animated: true, completion: nil)
} }
@IBAction func scanAutomatically(_ sender: Any) {
ScreenFlowManager.gotoAutoconnect()
}
private func showPeripheralDetails() { private func showPeripheralDetails() {
// Save selected peripheral for autoconnect // Save selected peripheral for autoconnect
Settings.autoconnectPeripheralIdentifier = selectedPeripheral?.identifier Settings.autoconnectPeripheralIdentifier = selectedPeripheral?.identifier
// Go to home screen // Go to home screen
ScreenFlowManager.gotoCPBModules() let backItem = UIBarButtonItem()
backItem.title = LocalizationManager.shared.localizedString("scanner_backbutton")
self.navigationItem.backBarButtonItem = backItem
guard let modulesViewController = self.storyboard?.instantiateViewController(withIdentifier: HomeViewController.kIdentifier) else { return }
self.show(modulesViewController, sender: self)
} }
// MARK: - UI // MARK: - UI
@ -459,6 +457,15 @@ extension ScannerViewController {
} }
} }
/*
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
isBaseTableScrolling = false
if isScannerTableWaitingForReload {
reloadBaseTable()
}
}*/
func scrollViewDidScroll(_ scrollView: UIScrollView) { func scrollViewDidScroll(_ scrollView: UIScrollView) {
// NavigationBar Button Custom Animation // NavigationBar Button Custom Animation
@ -468,7 +475,7 @@ extension ScannerViewController {
// Move Refresh control when a large title is used // Move Refresh control when a large title is used
if let height = navigationController?.navigationBar.frame.height { if let height = navigationController?.navigationBar.frame.height {
refreshControl.bounds = CGRect(x: refreshControl.bounds.origin.x, y: NavigationBarWithScrollAwareRightButton.navBarHeightLargeState - height, width: refreshControl.bounds.size.width, height: refreshControl.bounds.size.height) refreshControl.bounds = CGRect(x: refreshControl.bounds.origin.x, y: NavigationBarWithScrollAwareRightButton.CustomButtonMetrics.navBarHeightLargeState - height, width: refreshControl.bounds.size.width, height: refreshControl.bounds.size.height)
} }
// Hide details opacity when showing the refresh control // Hide details opacity when showing the refresh control

View file

@ -17,10 +17,6 @@ class StartupViewController: UIViewController {
private static let kServicesToReconnect = [BlePeripheral.kUartServiceUUID] private static let kServicesToReconnect = [BlePeripheral.kUartServiceUUID]
private static let kReconnectTimeout = 2.0 private static let kReconnectTimeout = 2.0
// UI
@IBOutlet weak var restoreConnectionLabel: UILabel!
// Data // Data
private let bleSupportSemaphore = DispatchSemaphore(value: 0) private let bleSupportSemaphore = DispatchSemaphore(value: 0)
private var startTime: CFAbsoluteTime! private var startTime: CFAbsoluteTime!
@ -28,12 +24,6 @@ class StartupViewController: UIViewController {
// MARK: - Lifecycle // MARK: - Lifecycle
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
// UI
restoreConnectionLabel.alpha = 0
// Localization
restoreConnectionLabel.text = LocalizationManager.shared.localizedString("splash_restoringconnection")
} }
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
@ -134,7 +124,6 @@ class StartupViewController: UIViewController {
// MARK: - Reconnect previously connnected Ble Peripheral // MARK: - Reconnect previously connnected Ble Peripheral
private func reconnecToPeripheral(withIdentifier identifier: UUID) { private func reconnecToPeripheral(withIdentifier identifier: UUID) {
DLog("Reconnecting...") DLog("Reconnecting...")
// Reconnect // Reconnect
let isTryingToReconnect = BleManager.shared.reconnecToPeripherals(withIdentifiers: [identifier], withServices: StartupViewController.kServicesToReconnect, timeout: StartupViewController.kReconnectTimeout) let isTryingToReconnect = BleManager.shared.reconnecToPeripherals(withIdentifiers: [identifier], withServices: StartupViewController.kServicesToReconnect, timeout: StartupViewController.kReconnectTimeout)
@ -156,22 +145,23 @@ class StartupViewController: UIViewController {
} }
private func didDisconnectFromPeripheral() { private func didDisconnectFromPeripheral() {
/*
// Clear selected peripheral
self.selectedPeripheral = nil
*/
// Autoconnect failed // Autoconnect failed
connected(peripheral: nil) connected(peripheral: nil)
} }
private func connected(peripheral: BlePeripheral?) { private func connected(peripheral: BlePeripheral?) {
if let peripheral = peripheral { if let peripheral = peripheral {
// Show restoring connection label
UIView.animate(withDuration: 0.2) {
self.restoreConnectionLabel.alpha = 1
}
// Setup peripheral // Setup peripheral
CPBBle.shared.setupPeripheral(blePeripheral: peripheral) { [weak self] result in CPBBle.shared.setupPeripheral(blePeripheral: peripheral) { [weak self] result in
switch result { switch result {
case .success(): case .success():
ScreenFlowManager.restoreAndGoToCPBModules() ScreenFlowManager.restoreAndGoToHome()
case .failure(let error): case .failure(let error):
DLog("Failed setup peripheral: \(error.localizedDescription)") DLog("Failed setup peripheral: \(error.localizedDescription)")
@ -190,15 +180,12 @@ class StartupViewController: UIViewController {
// MARK: - Screen Flow // MARK: - Screen Flow
private func gotoInitialScreen() { private func gotoInitialScreen() {
let viewControllerIdentifier = StartupViewController.kForcedNavigationControllerIdentifier ?? (Settings.areTipsEnabled && Config.isTutorialEnabled ? TipsViewController.kIdentifier : AutoConnectViewController.kNavigationControllerIdentifier) let viewControllerIdentifier = StartupViewController.kForcedNavigationControllerIdentifier ?? (Settings.areTipsEnabled && Config.isTutorialEnabled ? TipsViewController.kIdentifier : ScannerViewController.kIdentifier)
DLog("Start app with viewController: \(viewControllerIdentifier)") DLog("Start app with viewController: \(viewControllerIdentifier)")
// Change splash screen to main screen // Change splash screen to main screen
if let rootViewController = self.storyboard?.instantiateViewController(withIdentifier: viewControllerIdentifier) { if let rootViewController = self.storyboard?.instantiateViewController(withIdentifier: viewControllerIdentifier) {
ScreenFlowManager.changeRootViewController(rootViewController: rootViewController) { ScreenFlowManager.changeRootViewController(rootViewController: rootViewController, animated: false)
// Start scannning
//Config.bleManager.startScan()
}
} }
} }

View file

@ -46,7 +46,7 @@ class TemperaturePanelViewController: ModulePanelViewController {
// MARK: - Line Chart // MARK: - Line Chart
private func setupChart() { private func setupChart() {
//chartView.delegate = self //chartView.delegate = self
chartView.backgroundColor = .clear // Fix for Charts 3.0.3 (overrides the default background color) chartView.backgroundColor = .white // Fix for Charts 3.0.3 (overrides the default background color)
chartView.dragEnabled = false chartView.dragEnabled = false
chartView.isUserInteractionEnabled = false chartView.isUserInteractionEnabled = false

View file

@ -86,15 +86,6 @@ class TipViewController: UIViewController {
UIApplication.shared.open(url, options: [:], completionHandler: nil) UIApplication.shared.open(url, options: [:], completionHandler: nil)
} }
} }
label.configureLinkAttribute = { (type, attributes, isSelected) in
var atts = attributes
switch type {
case customType:
atts[.underlineStyle] = NSUnderlineStyle.single.rawValue
default: ()
}
return atts
}
} }
} }
} }

View file

@ -134,7 +134,7 @@ class TipsViewController: UIViewController {
} }
@IBAction func skip(_ sender: Any) { @IBAction func skip(_ sender: Any) {
ScreenFlowManager.gotoAutoconnect() ScreenFlowManager.gotoScanner()
} }
@objc private func pageControlTapHandler(sender: UIPageControl) { @objc private func pageControlTapHandler(sender: UIPageControl) {

View file

@ -46,10 +46,6 @@ class ToneGeneratorViewController: UIViewController {
@IBOutlet weak var keysContainerView: UIView! @IBOutlet weak var keysContainerView: UIView!
@IBOutlet weak var whiteKeysStackView: UIStackView! @IBOutlet weak var whiteKeysStackView: UIStackView!
@IBOutlet weak var speakerImageView: UIImageView! @IBOutlet weak var speakerImageView: UIImageView!
@IBOutlet weak var halfKeyTop: UIButton!
@IBOutlet weak var halfKeyBottom: UIButton!
@IBOutlet weak var extraHalfKeyTop: UIButton!
@IBOutlet weak var extraHalfKeyBottom: UIButton!
// Data // Data
private var tonesPlaying = Set<Int>() private var tonesPlaying = Set<Int>()
@ -79,17 +75,6 @@ class ToneGeneratorViewController: UIViewController {
} }
} }
// - Add 2 extra hidden buttons to extend the touch area of the black half-keys
for button in [extraHalfKeyTop!, extraHalfKeyBottom!] {
button.addTarget(self, action: #selector(redirectHalfKeyDown(_:)), for: .touchDown)
button.addTarget(self, action: #selector(redirectHalfKeyUp(_:)), for: [.touchUpInside, .touchUpOutside, .touchCancel])
}
// - Round the container so the half-keys that are clipped look rounded too
keysContainerView.layer.cornerRadius = 4
keysContainerView.layer.masksToBounds = true
// Localization // Localization
let localizationManager = LocalizationManager.shared let localizationManager = LocalizationManager.shared
self.title = localizationManager.localizedString("tonegenerator_title") self.title = localizationManager.localizedString("tonegenerator_title")
@ -104,26 +89,6 @@ class ToneGeneratorViewController: UIViewController {
} }
} }
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Disable the pop recognizer to avoid problems with keys on the edges
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Re-enable the pop recognizer
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = true
}
override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge {
// Remove the system gestures from left and right edges
return [.top, .bottom] // not left or right
}
// MARK: - Actions // MARK: - Actions
@objc private func keyDown(_ sender: UIButton) { @objc private func keyDown(_ sender: UIButton) {
let tag = sender.tag let tag = sender.tag
@ -160,20 +125,6 @@ class ToneGeneratorViewController: UIViewController {
} }
} }
@objc private func redirectHalfKeyDown(_ sender: UIButton) {
let targetButton = sender === extraHalfKeyTop ? halfKeyTop : halfKeyBottom
targetButton?.sendActions(for: .touchDown)
halfKeyTop.isHighlighted = true
halfKeyBottom.isHighlighted = true
}
@objc private func redirectHalfKeyUp(_ sender: UIButton) {
let targetButton = sender === extraHalfKeyTop ? halfKeyTop : halfKeyBottom
targetButton?.sendActions(for: .touchUpInside)
halfKeyTop.isHighlighted = false
halfKeyBottom.isHighlighted = false
}
private func frequencyForKeyTag(_ tag: Int) -> Double? { private func frequencyForKeyTag(_ tag: Int) -> Double? {
var frequency: Double? = nil var frequency: Double? = nil
if tag >= 100 && tag < 200 { // is white note if tag >= 100 && tag < 200 { // is white note
@ -199,4 +150,3 @@ class ToneGeneratorViewController: UIViewController {
self.present(navigationController, animated: true, completion: nil) self.present(navigationController, animated: true, completion: nil)
} }
} }

View file

@ -27,7 +27,7 @@ class BluefruitPlaygroundUITests: XCTestCase {
// Put teardown code here. This method is called after the invocation of each test method in the class. // Put teardown code here. This method is called after the invocation of each test method in the class.
} }
func testSnapshots() { func testExample() {
/* /*
// UI tests must launch the application that they test. // UI tests must launch the application that they test.
let app = XCUIApplication() let app = XCUIApplication()
@ -40,7 +40,7 @@ class BluefruitPlaygroundUITests: XCTestCase {
let scrollViewsQuery = app.scrollViews let scrollViewsQuery = app.scrollViews
let elementsQuery = scrollViewsQuery.otherElements let elementsQuery = scrollViewsQuery.otherElements
sleep(2) // Wait for the intro animation sleep(3) // Wait for the intro animation
snapshot("01a_Welcome") snapshot("01a_Welcome")
elementsQuery.buttons["LET'S GET STARTED..."].tap() elementsQuery.buttons["LET'S GET STARTED..."].tap()
@ -51,7 +51,7 @@ class BluefruitPlaygroundUITests: XCTestCase {
elementsQuery.buttons["BEGIN PAIRING"].tap() elementsQuery.buttons["BEGIN PAIRING"].tap()
let tablesQuery = app.tables let tablesQuery = app.tables
tablesQuery.staticTexts["Simulated Peripheral"].tap() tablesQuery/*@START_MENU_TOKEN@*/.staticTexts["Simulated Peripheral"]/*[[".cells.staticTexts[\"Simulated Peripheral\"]",".staticTexts[\"Simulated Peripheral\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
snapshot("02_Modules") snapshot("02_Modules")

View file

@ -1,7 +1,7 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
CFPropertyList (3.0.2) CFPropertyList (2.3.6)
addressable (2.7.0) addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0) public_suffix (>= 2.0.2, < 5.0)
atomos (0.1.3) atomos (0.1.3)
@ -18,7 +18,7 @@ GEM
unf (>= 0.0.5, < 1.0.0) unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.5) dotenv (2.7.5)
emoji_regex (1.0.1) emoji_regex (1.0.1)
excon (0.71.1) excon (0.71.0)
faraday (0.17.1) faraday (0.17.1)
multipart-post (>= 1.2, < 3) multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6) faraday-cookie_jar (0.0.6)
@ -27,7 +27,7 @@ GEM
faraday_middleware (0.13.1) faraday_middleware (0.13.1)
faraday (>= 0.7.4, < 1.0) faraday (>= 0.7.4, < 1.0)
fastimage (2.1.7) fastimage (2.1.7)
fastlane (2.138.0) fastlane (2.137.0)
CFPropertyList (>= 2.3, < 4.0.0) CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0) addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0) babosa (>= 1.0.2, < 2.0.0)
@ -36,7 +36,7 @@ GEM
commander-fastlane (>= 4.4.6, < 5.0.0) commander-fastlane (>= 4.4.6, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0) dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 2.0) emoji_regex (>= 0.1, < 2.0)
excon (>= 0.71.0, < 1.0.0) excon (>= 0.45.0, < 1.0.0)
faraday (~> 0.17) faraday (~> 0.17)
faraday-cookie_jar (~> 0.0.6) faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 0.13.1) faraday_middleware (~> 0.13.1)
@ -61,7 +61,7 @@ GEM
tty-screen (>= 0.6.3, < 1.0.0) tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0) word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0) xcodeproj (>= 1.8.1, < 2.0.0)
xcpretty (~> 0.3.0) xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3) xcpretty-travis-formatter (>= 0.0.3)
gh_inspector (1.1.3) gh_inspector (1.1.3)
@ -93,7 +93,7 @@ GEM
http-cookie (1.0.3) http-cookie (1.0.3)
domain_name (~> 0.5) domain_name (~> 0.5)
httpclient (2.8.3) httpclient (2.8.3)
json (2.3.0) json (2.1.0)
jwt (2.1.0) jwt (2.1.0)
memoist (0.16.2) memoist (0.16.2)
mime-types (3.3) mime-types (3.3)
@ -121,7 +121,7 @@ GEM
faraday (~> 0.9) faraday (~> 0.9)
jwt (>= 1.5, < 3.0) jwt (>= 1.5, < 3.0)
multi_json (~> 1.10) multi_json (~> 1.10)
simctl (1.6.7) simctl (1.6.6)
CFPropertyList CFPropertyList
naturally naturally
slack-notifier (2.3.2) slack-notifier (2.3.2)
@ -138,7 +138,7 @@ GEM
unf_ext (0.0.7.6) unf_ext (0.0.7.6)
unicode-display_width (1.6.0) unicode-display_width (1.6.0)
word_wrap (1.0.0) word_wrap (1.0.0)
xcodeproj (1.14.0) xcodeproj (1.12.0)
CFPropertyList (>= 2.3.3, < 4.0) CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3) atomos (~> 0.1.3)
claide (>= 1.0.2, < 2.0) claide (>= 1.0.2, < 2.0)

View file

@ -1,3 +0,0 @@
# The Deliverfile allows you to store various App Store Connect metadata
# For more information, check out the docs
# https://docs.fastlane.tools/actions/deliver/

View file

@ -6,8 +6,8 @@
"iPhone 11", "iPhone 11",
"iPhone 8 Plus", "iPhone 8 Plus",
"iPhone 8", "iPhone 8",
"iPhone SE",
# "iPhone SE",
# "iPhone 6", # "iPhone 6",
# "iPhone 6 Plus", # "iPhone 6 Plus",
# "iPhone 5", # "iPhone 5",
@ -29,8 +29,7 @@ languages([
# Where should the resulting screenshots be stored? # Where should the resulting screenshots be stored?
# output_directory "./screenshots" # output_directory "./screenshots"
# remove the '#' to clear all previously generated screenshots before creating new ones # clear_previous_screenshots true # remove the '#' to clear all previously generated screenshots before creating new ones
clear_previous_screenshots true
# Choose which project/workspace to use # Choose which project/workspace to use
# project "./Project.xcodeproj" # project "./Project.xcodeproj"