Compare commits
1 commit
master
...
puppetModu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6d10619ff |
BIN
.DS_Store
vendored
Normal 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
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -206,12 +206,6 @@ class BlePeripheral: NSObject {
|
||||||
let command = BleCommand(type: .discoverDescriptor, parameters: [characteristic], completion: completion)
|
let command = BleCommand(type: .discoverDescriptor, parameters: [characteristic], completion: completion)
|
||||||
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? {
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
completion?(.success(()))
|
||||||
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(()))
|
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)):
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -10,42 +10,38 @@ import UIKit
|
||||||
|
|
||||||
@UIApplicationMain
|
@UIApplicationMain
|
||||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
var window: UIWindow?
|
var window: UIWindow?
|
||||||
|
|
||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||||
|
|
||||||
|
|
||||||
startup()
|
startup()
|
||||||
|
|
||||||
ScreenFlowManager.enableBleStateManagement()
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillResignActive(_ application: UIApplication) {
|
func applicationWillResignActive(_ application: UIApplication) {
|
||||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||||
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
|
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationDidEnterBackground(_ application: UIApplication) {
|
func applicationDidEnterBackground(_ application: UIApplication) {
|
||||||
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillEnterForeground(_ application: UIApplication) {
|
func applicationWillEnterForeground(_ application: UIApplication) {
|
||||||
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
|
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationDidBecomeActive(_ application: UIApplication) {
|
func applicationDidBecomeActive(_ application: UIApplication) {
|
||||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
private func startup() {
|
private func startup() {
|
||||||
// Settings
|
// Settings
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 325 KiB |
|
Before Width: | Height: | Size: 261 KiB |
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 87 KiB |
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 996 B After Width: | Height: | Size: 1 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -12,13 +12,10 @@ 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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
@ -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
|
||||||
|
|
@ -192,11 +188,7 @@ class CPBBle {
|
||||||
func accelerometerLastValue() -> BlePeripheral.AccelerometerValue? {
|
func accelerometerLastValue() -> BlePeripheral.AccelerometerValue? {
|
||||||
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)}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
@ -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 CPB’s built-in NeoPixel LEDs individually, as a group, or by playing preset animations.
|
"neopixels_help" = "Neopixel mode allows you to control the color of CPB’s 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
|
||||||
|
|
|
||||||
334
BluefruitPlayground/Resources/models3d/Sparky_Gold1.dae
Normal 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 {
|
||||||
// Config (navbar metrics)
|
static let rightMargin: CGFloat = 8//12
|
||||||
static let navBarHeightSmallState: CGFloat = 44
|
|
||||||
static let navBarHeightLargeState: CGFloat = 96.5
|
static let bottomMarginForLargeState: CGFloat = 9
|
||||||
|
static let bottomMarginForSmallState: CGFloat = 2
|
||||||
private static let rightMargin: CGFloat = 6//8
|
|
||||||
|
static let navBarHeightSmallState: CGFloat = 44
|
||||||
private static let bottomMarginForLargeState: CGFloat = 6//9
|
static let navBarHeightLargeState: CGFloat = 96.5
|
||||||
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
|
||||||
|
|
@ -60,15 +58,22 @@ class NavigationBarWithScrollAwareRightButton : UINavigationBar {
|
||||||
// Detect topViewControllerChange
|
// Detect topViewControllerChange
|
||||||
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)
|
||||||
|
|
|
||||||
|
|
@ -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 mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
|
||||||
let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: ScannerViewController.kNavigationControllerIdentifier)
|
|
||||||
changeRootViewController(rootViewController: rootNavigationViewController, animationOptions: .transitionFlipFromRight)
|
// Add home to scanner
|
||||||
|
let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: ScannerViewController.kIdentifier) as! UINavigationController
|
||||||
|
let homeViewController = mainStoryboard.instantiateViewController(withIdentifier: HomeViewController.kIdentifier)
|
||||||
|
rootNavigationViewController.viewControllers.append(homeViewController)
|
||||||
|
|
||||||
|
ScreenFlowManager.changeRootViewController(rootViewController: rootNavigationViewController, animated: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func restoreAndGoToCPBModules() {
|
|
||||||
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
|
|
||||||
|
|
||||||
let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: HomeViewController.kNavigationControllerIdentifier)
|
|
||||||
|
|
||||||
changeRootViewController(rootViewController: rootNavigationViewController) {
|
|
||||||
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 {
|
||||||
window.rootViewController = rootViewController
|
rootViewController.view.layoutIfNeeded()
|
||||||
// rootViewController.view.layoutIfNeeded()
|
UIView.transition(with: window, duration: 0.3, options: .transitionCrossDissolve, animations: {
|
||||||
UIView.transition(with: window, duration: 0.3, options: animationOptions, animations: {
|
window.rootViewController = rootViewController
|
||||||
}, 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BIN
BluefruitPlayground/ViewControllers/.DS_Store
vendored
Normal 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
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -37,6 +37,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
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,8 @@ import UIKit
|
||||||
|
|
||||||
class HomeViewController: UIViewController {
|
class HomeViewController: UIViewController {
|
||||||
// Constants
|
// Constants
|
||||||
static let kNavigationControllerIdentifier = "ModulesNavigationController"
|
|
||||||
static let kIdentifier = "HomeViewController"
|
static let kIdentifier = "HomeViewController"
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
@IBOutlet weak var baseTableView: UITableView!
|
@IBOutlet weak var baseTableView: UITableView!
|
||||||
|
|
||||||
|
|
@ -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,18 +113,14 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -184,64 +129,36 @@ extension HomeViewController: UITableViewDataSource {
|
||||||
extension HomeViewController: UITableViewDelegate {
|
extension HomeViewController: UITableViewDelegate {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
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)
|
|
||||||
switch(cellType) {
|
|
||||||
case .details:
|
|
||||||
break
|
|
||||||
case .module:
|
|
||||||
if let module = Modules(rawValue: indexPath.row) {
|
|
||||||
var storyboardId: String? = nil
|
|
||||||
switch module {
|
|
||||||
case .color:
|
|
||||||
storyboardId = NeoPixelsViewController.kIdentifier
|
|
||||||
case .light:
|
|
||||||
storyboardId = LightSensorViewController.kIdentifier
|
|
||||||
case .button:
|
|
||||||
storyboardId = ButtonStatusViewController.kIdentifier
|
|
||||||
case .tone:
|
|
||||||
storyboardId = ToneGeneratorViewController.kIdentifier
|
|
||||||
case .accelerometer:
|
|
||||||
storyboardId = AccelerometerViewController.kIdentifier
|
|
||||||
case .temperature:
|
|
||||||
storyboardId = TemperatureViewController.kIdentifier
|
|
||||||
}
|
|
||||||
|
|
||||||
if let identifier = storyboardId, let viewController = storyboard?.instantiateViewController(withIdentifier: identifier) {
|
|
||||||
|
|
||||||
// Show viewController with completion block
|
|
||||||
CATransaction.begin()
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
let isDetails = indexPath.section == 0
|
||||||
// MARK: UIScrollViewDelegate
|
guard !isDetails else { return }
|
||||||
extension HomeViewController {
|
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
guard let module = Modules(rawValue: indexPath.row) else { return }
|
||||||
// NavigationBar Button Custom Animation
|
var storyboardId: String? = nil
|
||||||
if let customNavigationBar = navigationController?.navigationBar as? NavigationBarWithScrollAwareRightButton {
|
switch module {
|
||||||
customNavigationBar.updateRightButtonPosition()
|
case .color:
|
||||||
|
storyboardId = NeoPixelsViewController.kIdentifier
|
||||||
|
case .light:
|
||||||
|
storyboardId = LightSensorViewController.kIdentifier
|
||||||
|
case .button:
|
||||||
|
storyboardId = ButtonStatusViewController.kIdentifier
|
||||||
|
case .tone:
|
||||||
|
storyboardId = ToneGeneratorViewController.kIdentifier
|
||||||
|
case .accelerometer:
|
||||||
|
storyboardId = AccelerometerViewController.kIdentifier
|
||||||
|
case .temperature:
|
||||||
|
storyboardId = TemperatureViewController.kIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let identifier = storyboardId, let viewController = storyboard?.instantiateViewController(withIdentifier: identifier) {
|
||||||
|
|
||||||
|
self.show(viewController, sender: self)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
@ -24,9 +23,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
|
||||||
|
|
@ -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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,14 +44,9 @@ 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) {
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
|
|
@ -67,7 +62,7 @@ class CircuitViewController: UIViewController {
|
||||||
// Notifications
|
// Notifications
|
||||||
registerNotifications(enabled: true)
|
registerNotifications(enabled: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillDisappear(_ animated: Bool) {
|
override func viewWillDisappear(_ animated: Bool) {
|
||||||
super.viewWillDisappear(animated)
|
super.viewWillDisappear(animated)
|
||||||
|
|
||||||
|
|
@ -102,7 +97,8 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,26 +89,21 @@ 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()
|
||||||
|
|
||||||
// Clear peripherals
|
// Clear peripherals
|
||||||
peripheralList.clear()
|
peripheralList.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -458,7 +456,16 @@ extension ScannerViewController {
|
||||||
reloadBaseTable()
|
reloadBaseTable()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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,56 +75,25 @@ 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
// Navigationbar setup
|
// Navigationbar setup
|
||||||
if let customNavigationBar = navigationController?.navigationBar as? NavigationBarWithScrollAwareRightButton {
|
if let customNavigationBar = navigationController?.navigationBar as? NavigationBarWithScrollAwareRightButton {
|
||||||
customNavigationBar.setRightButton(topViewController: self, image: UIImage(named: "help"), target: self, action: #selector(help(_:)))
|
customNavigationBar.setRightButton(topViewController: self, image: UIImage(named: "help"), target: self, action: #selector(help(_:)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
tonesPlaying.insert(tag)
|
tonesPlaying.insert(tag)
|
||||||
|
|
||||||
// UI Animation
|
// UI Animation
|
||||||
UIView.animate(withDuration: 0.2) {
|
UIView.animate(withDuration: 0.2) {
|
||||||
self.speakerImageView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
|
self.speakerImageView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
|
||||||
|
|
@ -143,7 +108,7 @@ class ToneGeneratorViewController: UIViewController {
|
||||||
@objc private func keyUp(_ sender: UIButton) {
|
@objc private func keyUp(_ sender: UIButton) {
|
||||||
let tag = sender.tag
|
let tag = sender.tag
|
||||||
tonesPlaying.remove(tag)
|
tonesPlaying.remove(tag)
|
||||||
|
|
||||||
// UI Animation
|
// UI Animation
|
||||||
if tonesPlaying.isEmpty {
|
if tonesPlaying.isEmpty {
|
||||||
UIView.animate(withDuration: 0.15) {
|
UIView.animate(withDuration: 0.15) {
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
||||||
16
Gemfile.lock
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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/
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||