Compare commits
1 commit
master
...
puppetModu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6d10619ff |
BIN
.DS_Store
vendored
Normal file
|
|
@ -7,6 +7,7 @@
|
|||
objects = {
|
||||
|
||||
/* 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 */; };
|
||||
A901EC4D237C47D800687BE6 /* CPBDataSeries.swift in Sources */ = {isa = PBXBuildFile; fileRef = A901EC4C237C47D800687BE6 /* CPBDataSeries.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 */; };
|
||||
A9BED77B234FBEAB002FFF53 /* LogHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9BED764234FBEAB002FFF53 /* LogHelper.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 */; };
|
||||
A9DF06142359B31C0094327F /* ButtonStatusPanelViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DF06132359B31C0094327F /* ButtonStatusPanelViewController.swift */; };
|
||||
A9E4C02E2358E6B400B5A493 /* UIColor+LightAndDark.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E4C02D2358E6B400B5A493 /* UIColor+LightAndDark.swift */; };
|
||||
|
|
@ -222,6 +213,7 @@
|
|||
/* 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>"; };
|
||||
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>"; };
|
||||
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; };
|
||||
|
|
@ -318,11 +310,6 @@
|
|||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
|
@ -491,7 +478,6 @@
|
|||
children = (
|
||||
A986AB92235942D600DF12C8 /* LightSensorViewController.swift */,
|
||||
A98F1B8A23594BE400C5269E /* LightSensorPanelViewController.swift */,
|
||||
A9D12E8523E3C5CF00F3C259 /* LightChartPanelViewController.swift */,
|
||||
);
|
||||
path = LightSensor;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -500,6 +486,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
A98E4D3123566785005B166D /* cpb.scn */,
|
||||
2981E3B423C6526C00974EA2 /* Sparky_Gold1.dae */,
|
||||
A96AA5BA235F903D004A06BA /* CPB_PcbSurface_Color.png */,
|
||||
);
|
||||
path = models3d;
|
||||
|
|
@ -546,8 +533,6 @@
|
|||
A9BED700234F754B002FFF53 /* Utils */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A99BA722235000520080D03F /* ScreenFlowManager.swift */,
|
||||
A9BED70C234F76C5002FFF53 /* LocalizationManager.swift */,
|
||||
A9716BA92350128100CB174D /* RssiUI.swift */,
|
||||
A96AA5D2236358A8004A06BA /* UIView+RoundedCorners.swift */,
|
||||
A96AA5D423635A89004A06BA /* NSLayoutConstraint+ChangeMultiplier.swift */,
|
||||
|
|
@ -558,10 +543,11 @@
|
|||
A96AA5C0236050E4004A06BA /* NavigationBarWithScrollAwareRightButton.swift */,
|
||||
A9BED701234F754B002FFF53 /* GradientView.swift */,
|
||||
A96AA5D023634F03004A06BA /* GridView.swift */,
|
||||
A9BED70C234F76C5002FFF53 /* LocalizationManager.swift */,
|
||||
A99BA722235000520080D03F /* ScreenFlowManager.swift */,
|
||||
A9B3569E2365D420002093EE /* PanelInsetView.swift */,
|
||||
A958E77D238B7457006A225A /* NormalBrightnessSliderControl.swift */,
|
||||
A958E77F238B75D9006A225A /* NormalBrightnessColorPickerController.swift */,
|
||||
A9D12E8323E3661800F3C259 /* UIColor+Interpolate.swift */,
|
||||
);
|
||||
path = Utils;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -633,19 +619,10 @@
|
|||
A9BED766234FBEAB002FFF53 /* Data+ScanValue.swift */,
|
||||
A901EC47237C43C000687BE6 /* Data+LittleEndianTypes.swift */,
|
||||
A9BDEC40237C75C900EDDC46 /* Types+Data.swift */,
|
||||
A9D5B21223E25C1600178E1B /* ActiveLabelIntrinsicSizeFix.swift */,
|
||||
);
|
||||
path = Utils;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A9D5B20E23E0983F00178E1B /* AutoConnect */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A9D5B20F23E0985600178E1B /* AutoConnectViewController.swift */,
|
||||
);
|
||||
path = AutoConnect;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A9DF06102359B04C0094327F /* ButtonStatus */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -692,10 +669,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
A9BED716234FBDBA002FFF53 /* StartupViewController.swift */,
|
||||
A9D5B21523E3024700178E1B /* BluetoothStatusViewController.swift */,
|
||||
A9482988237B73B100B6FC13 /* Common */,
|
||||
A9BED703234F75E4002FFF53 /* Tips */,
|
||||
A9D5B20E23E0983F00178E1B /* AutoConnect */,
|
||||
A9EAA18D2350B26B00FA615E /* Scanner */,
|
||||
A9EAA1982351F1DD00FA615E /* Home */,
|
||||
A9EAA19F2352973400FA615E /* Neopixels */,
|
||||
|
|
@ -826,6 +801,7 @@
|
|||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2981E3B523C6526C00974EA2 /* Sparky_Gold1.dae in Resources */,
|
||||
A98E4D3223566785005B166D /* cpb.scn in Resources */,
|
||||
A92B9F19234DE4A5002374F0 /* LaunchScreen.storyboard in Resources */,
|
||||
A99BA71F234FFECF0080D03F /* DefaultPreferences.plist in Resources */,
|
||||
|
|
@ -957,7 +933,6 @@
|
|||
A92B9F0D234DE4A2002374F0 /* AppDelegate.swift in Sources */,
|
||||
A96AA5C6236312CE004A06BA /* ToneGeneratorViewController.swift in Sources */,
|
||||
A9BED77D234FBEAB002FFF53 /* Data+ScanValue.swift in Sources */,
|
||||
A9D12E8623E3C5CF00F3C259 /* LightChartPanelViewController.swift in Sources */,
|
||||
A901EC48237C43C000687BE6 /* Data+LittleEndianTypes.swift in Sources */,
|
||||
A980FE6D2357D14400E3A909 /* TipPowerUpViewController.swift in Sources */,
|
||||
A94AC14E2364A82B0062AB11 /* CPBBle.swift in Sources */,
|
||||
|
|
@ -990,7 +965,6 @@
|
|||
A958E782238B7936006A225A /* BlePeripheral+CPBAccelerometer.swift in Sources */,
|
||||
A9DF06122359B0600094327F /* ButtonStatusViewController.swift in Sources */,
|
||||
A96AA5D3236358A8004A06BA /* UIView+RoundedCorners.swift in Sources */,
|
||||
A9D5B21023E0985600178E1B /* AutoConnectViewController.swift in Sources */,
|
||||
A98E4D2823565B76005B166D /* TipWelcomeViewController.swift in Sources */,
|
||||
A9BED70B234F7617002FFF53 /* Config.swift in Sources */,
|
||||
A980FE6F2357D15400E3A909 /* TipDiscoverViewController.swift in Sources */,
|
||||
|
|
@ -1006,7 +980,6 @@
|
|||
A98F1B8B23594BE400C5269E /* LightSensorPanelViewController.swift in Sources */,
|
||||
A96AA5C32362F488004A06BA /* AboutViewController.swift in Sources */,
|
||||
A9A08BAF23A7957700498069 /* BlePeripheral+CPBButtons.swift in Sources */,
|
||||
A9D12E8423E3661800F3C259 /* UIColor+Interpolate.swift in Sources */,
|
||||
A98F1B892359494500C5269E /* ConfigUI.swift in Sources */,
|
||||
A9BED773234FBEAB002FFF53 /* UartPacketManager.swift in Sources */,
|
||||
A986AB93235942D600DF12C8 /* LightSensorViewController.swift in Sources */,
|
||||
|
|
@ -1025,8 +998,6 @@
|
|||
A9EAA1A3235297B400FA615E /* NeopixelsLightSequenceViewController.swift in Sources */,
|
||||
A901EC4D237C47D800687BE6 /* CPBDataSeries.swift in Sources */,
|
||||
A99BA71D234FFE780080D03F /* Settings.swift in Sources */,
|
||||
A9D5B21323E25C1600178E1B /* ActiveLabelIntrinsicSizeFix.swift in Sources */,
|
||||
A9D5B21623E3024700178E1B /* BluetoothStatusViewController.swift in Sources */,
|
||||
A9BED775234FBEAB002FFF53 /* BlePeripheral+Battery.swift in Sources */,
|
||||
A94AC1482363A7BD0062AB11 /* TemperaturePanelViewController.swift in Sources */,
|
||||
A9EAA1912350B26B00FA615E /* ScannerViewController.swift in Sources */,
|
||||
|
|
@ -1047,7 +1018,6 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A9D12E8723E3C5CF00F3C259 /* LightChartPanelViewController.swift in Sources */,
|
||||
A9A08B3923A523C900498069 /* Int+Bytes.swift in Sources */,
|
||||
A9A08B3A23A523C900498069 /* UIView+AllSubviewsWithClass.swift in Sources */,
|
||||
A9A08BAD23A794F500498069 /* BlePeripheralSimulated+CPBLight.swift in Sources */,
|
||||
|
|
@ -1063,7 +1033,6 @@
|
|||
A9A08B4523A523C900498069 /* Data+ScanValue.swift in Sources */,
|
||||
A9A08B4623A523C900498069 /* Data+LittleEndianTypes.swift in Sources */,
|
||||
A9A08B4723A523C900498069 /* TipPowerUpViewController.swift in Sources */,
|
||||
A9D12E8823E4434000F3C259 /* UIColor+Interpolate.swift in Sources */,
|
||||
A9A08B4823A523C900498069 /* CPBBle.swift in Sources */,
|
||||
A9A08B4923A523C900498069 /* NavigationBarWithScrollAwareRightButton.swift in Sources */,
|
||||
A9A08B4B23A523C900498069 /* UartPacketManagerBase.swift in Sources */,
|
||||
|
|
@ -1081,7 +1050,6 @@
|
|||
A9A08B5723A523C900498069 /* GridView.swift in Sources */,
|
||||
A9A08B5823A523C900498069 /* BlePeripheral+Uart.swift in Sources */,
|
||||
A9A08B5923A523C900498069 /* BlePeripheral+CPBCommon.swift in Sources */,
|
||||
A9D5B21123E0985600178E1B /* AutoConnectViewController.swift in Sources */,
|
||||
A9A08B5A23A523C900498069 /* BlePeripheral+CPBToneGenerator.swift in Sources */,
|
||||
A9A08B5B23A523C900498069 /* RssiUI.swift in Sources */,
|
||||
A9A08B5C23A523C900498069 /* HelpViewController.swift in Sources */,
|
||||
|
|
@ -1098,7 +1066,6 @@
|
|||
A9A08B6823A523C900498069 /* TipWelcomeViewController.swift in Sources */,
|
||||
A9A08BA423A5286700498069 /* BlePeripheralSimulated.swift in Sources */,
|
||||
A9A08B6923A523C900498069 /* Config.swift in Sources */,
|
||||
A9D5B21423E25C1600178E1B /* ActiveLabelIntrinsicSizeFix.swift in Sources */,
|
||||
A9A08B6A23A523C900498069 /* TipDiscoverViewController.swift in Sources */,
|
||||
A9A08B6B23A523C900498069 /* ModulePanelViewController.swift in Sources */,
|
||||
A9A08B6C23A523C900498069 /* TipsViewController.swift in Sources */,
|
||||
|
|
@ -1113,7 +1080,6 @@
|
|||
A9A08B7323A523C900498069 /* BleManager.swift in Sources */,
|
||||
A9A08B7423A523C900498069 /* LightSensorPanelViewController.swift in Sources */,
|
||||
A9A08B7523A523C900498069 /* AboutViewController.swift in Sources */,
|
||||
A9D5B21723E3024800178E1B /* BluetoothStatusViewController.swift in Sources */,
|
||||
A9A08BAA23A7891500498069 /* ObjectBuilder.m in Sources */,
|
||||
A9A08B7623A523C900498069 /* ConfigUI.swift in Sources */,
|
||||
A9A08B7723A523C900498069 /* UartPacketManager.swift in Sources */,
|
||||
|
|
@ -1303,7 +1269,7 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 8;
|
||||
CURRENT_PROJECT_VERSION = 5;
|
||||
DEVELOPMENT_TEAM = 2X94RM7457;
|
||||
INFOPLIST_FILE = BluefruitPlayground/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
|
|
@ -1311,13 +1277,13 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.2.0;
|
||||
MARKETING_VERSION = 1.1.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.BluefruitPlayground;
|
||||
PRODUCT_NAME = "Bluefruit Playground";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "BluefruitPlayground/BluefruitPlayground-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
|
|
@ -1328,7 +1294,7 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 8;
|
||||
CURRENT_PROJECT_VERSION = 5;
|
||||
DEVELOPMENT_TEAM = 2X94RM7457;
|
||||
INFOPLIST_FILE = BluefruitPlayground/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
|
|
@ -1336,12 +1302,12 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.2.0;
|
||||
MARKETING_VERSION = 1.1.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.BluefruitPlayground;
|
||||
PRODUCT_NAME = "Bluefruit Playground";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "BluefruitPlayground/BluefruitPlayground-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
|
|
@ -1392,7 +1358,7 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 8;
|
||||
CURRENT_PROJECT_VERSION = 5;
|
||||
DEVELOPMENT_TEAM = 2X94RM7457;
|
||||
INFOPLIST_FILE = "BluefruitPlayground-SimulatedBluetooth-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
|
|
@ -1400,7 +1366,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.2.0;
|
||||
MARKETING_VERSION = 1.1.2;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -D SIMULATEBLUETOOTH";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.BluefruitPlayground;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
@ -1418,7 +1384,7 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 8;
|
||||
CURRENT_PROJECT_VERSION = 5;
|
||||
DEVELOPMENT_TEAM = 2X94RM7457;
|
||||
INFOPLIST_FILE = "BluefruitPlayground-SimulatedBluetooth-Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
|
|
@ -1426,7 +1392,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.2.0;
|
||||
MARKETING_VERSION = 1.1.2;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -D SIMULATEBLUETOOTH";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.BluefruitPlayground;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
|
|
|||
BIN
BluefruitPlayground/.DS_Store
vendored
Normal file
|
|
@ -27,15 +27,8 @@ class BleManager: NSObject {
|
|||
private var centralManagerPoweredOnSemaphore = DispatchSemaphore(value: 1)
|
||||
|
||||
// Scanning
|
||||
var isScanning: Bool {
|
||||
return scanningStartTime != nil
|
||||
}
|
||||
var scanningElapsedTime: TimeInterval? {
|
||||
guard let scanningStartTime = scanningStartTime else { return nil }
|
||||
return CACurrentMediaTime() - scanningStartTime
|
||||
}
|
||||
var isScanning = false
|
||||
private var isScanningWaitingToStart = false
|
||||
internal var scanningStartTime: TimeInterval? // Time when the scanning started. nil if stopped
|
||||
private var scanningServicesFilter: [CBUUID]?
|
||||
internal var peripheralsFound = [UUID: BlePeripheral]()
|
||||
private var peripheralsFoundLock = NSLock()
|
||||
|
|
@ -121,7 +114,7 @@ class BleManager: NSObject {
|
|||
}
|
||||
|
||||
// DLog("start scan")
|
||||
scanningStartTime = CACurrentMediaTime()
|
||||
isScanning = true
|
||||
NotificationCenter.default.post(name: .didStartScanning, object: nil)
|
||||
|
||||
let options = BleManager.kAlwaysAllowDuplicateKeys ? [CBCentralManagerScanOptionAllowDuplicatesKey: true] : nil
|
||||
|
|
@ -132,7 +125,7 @@ class BleManager: NSObject {
|
|||
func stopScan() {
|
||||
// DLog("stop scan")
|
||||
centralManager?.stopScan()
|
||||
scanningStartTime = nil
|
||||
isScanning = false
|
||||
isScanningWaitingToStart = false
|
||||
NotificationCenter.default.post(name: .didStopScanning, object: nil)
|
||||
}
|
||||
|
|
@ -218,19 +211,11 @@ class BleManager: NSObject {
|
|||
}
|
||||
}
|
||||
|
||||
func disconnect(from peripheral: BlePeripheral, waitForQueuedCommands: Bool = false) {
|
||||
guard let centralManager = centralManager else { return}
|
||||
func disconnect(from peripheral: BlePeripheral) {
|
||||
|
||||
DLog("disconnect")
|
||||
NotificationCenter.default.post(name: .willDisconnectFromPeripheral, object: nil, userInfo: [NotificationUserInfoKey.uuid.rawValue: peripheral.identifier])
|
||||
|
||||
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)
|
||||
}
|
||||
centralManager?.cancelPeripheralConnection(peripheral.peripheral)
|
||||
}
|
||||
|
||||
func reconnecToPeripherals(withIdentifiers identifiers: [UUID], withServices services: [CBUUID], timeout: Double? = nil) -> Bool {
|
||||
|
|
@ -314,10 +299,7 @@ extension BleManager: CBCentralManagerDelegate {
|
|||
if isScanning {
|
||||
isScanningWaitingToStart = true
|
||||
}
|
||||
scanningStartTime = nil
|
||||
|
||||
// Remove all peripherals found (Important because the BlePeripheral queues could contain old commands that were processing when the bluetooth state changed)
|
||||
peripheralsFound.removeAll()
|
||||
isScanning = false
|
||||
}
|
||||
|
||||
NotificationCenter.default.post(name: .didUpdateBleState, object: nil)
|
||||
|
|
|
|||
|
|
@ -207,12 +207,6 @@ class BlePeripheral: NSObject {
|
|||
commandQueue.append(command)
|
||||
}
|
||||
|
||||
// MARK: - Connection
|
||||
func disconnect(centralManager: CBCentralManager) {
|
||||
let command = BleCommand(type: .disconnect, parameters: [centralManager], completion: nil)
|
||||
commandQueue.append(command)
|
||||
}
|
||||
|
||||
// MARK: - Service
|
||||
func discoveredService(uuid: CBUUID) -> CBService? {
|
||||
let service = peripheral.services?.first(where: {$0.uuid == uuid})
|
||||
|
|
@ -325,7 +319,6 @@ class BlePeripheral: NSObject {
|
|||
case writeCharacteristic
|
||||
case writeCharacteristicAndWaitNofity
|
||||
case readDescriptor
|
||||
case disconnect
|
||||
}
|
||||
|
||||
enum CommandError: Error {
|
||||
|
|
@ -369,8 +362,6 @@ class BlePeripheral: NSObject {
|
|||
write(with: command)
|
||||
case .readDescriptor:
|
||||
readDescriptor(with: command)
|
||||
case .disconnect:
|
||||
disconnect(with: command)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -500,12 +491,6 @@ class BlePeripheral: NSObject {
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -47,8 +47,7 @@ extension BlePeripheral {
|
|||
// MARK: - Actions
|
||||
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: timePeriod, responseHandler: { response in
|
||||
self.cpbServiceEnable(serviceUuid: BlePeripheral.kCPBButtonsServiceUUID, mainCharacteristicUuid: BlePeripheral.kCPBButtonsCharacteristicUUID, timePeriod: 0, responseHandler: { response in
|
||||
|
||||
switch response {
|
||||
case let .success((data, uuid)):
|
||||
|
|
@ -68,21 +67,7 @@ extension BlePeripheral {
|
|||
}
|
||||
|
||||
self.cpbButtonsCharacteristic = characteristic
|
||||
|
||||
if timePeriod == 0 { // Read initial state if the timePeriod is 0 (update only when changed)
|
||||
CPBBle.shared.buttonsReadState { response in
|
||||
switch response {
|
||||
case .success(_, _):
|
||||
completion?(.success(()))
|
||||
case .failure(let error):
|
||||
DLog("Error receiving initial button state data: \(error)")
|
||||
completion?(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
completion?(.success(()))
|
||||
}
|
||||
completion?(.success(()))
|
||||
|
||||
case let .failure(error):
|
||||
self.cpbButtonsCharacteristic = nil
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ extension BlePeripheral {
|
|||
static let kCPBLightServiceUUID = CBUUID(string: "ADAF0300-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
|
||||
private struct CustomPropertiesKeys {
|
||||
|
|
|
|||
|
|
@ -14,8 +14,6 @@ extension BlePeripheral {
|
|||
static let kCPBTemperatureServiceUUID = CBUUID(string: "ADAF0100-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
|
||||
private struct CustomPropertiesKeys {
|
||||
static var cpbTemperatureCharacteristic: CBCharacteristic?
|
||||
|
|
@ -33,7 +31,7 @@ extension BlePeripheral {
|
|||
// MARK: - Actions
|
||||
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 {
|
||||
case let .success((data, uuid)):
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
// Copyright © 2019 Adafruit. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Foundation
|
||||
import CoreBluetooth
|
||||
|
||||
class BleManagerSimulated: BleManager {
|
||||
|
|
@ -20,7 +20,7 @@ class BleManagerSimulated: BleManager {
|
|||
}
|
||||
|
||||
override func startScan(withServices services: [CBUUID]? = nil) {
|
||||
scanningStartTime = CACurrentMediaTime()
|
||||
isScanning = true
|
||||
|
||||
// Add simulated peripheral
|
||||
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
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
|
@ -17,8 +17,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
|
||||
|
||||
startup()
|
||||
|
||||
ScreenFlowManager.enableBleStateManagement()
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -42,8 +40,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
|
||||
func applicationWillTerminate(_ application: UIApplication) {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
|
||||
ScreenFlowManager.disableBleStateManagement()
|
||||
}
|
||||
|
||||
// MARK: - Startup
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"color-space" : "display-p3",
|
||||
"components" : {
|
||||
"red" : "0.949",
|
||||
"red" : "0.010",
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.961",
|
||||
"green" : "0.961"
|
||||
"blue" : "0.802",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -13,12 +13,9 @@ struct Config {
|
|||
// Debug-----------------------------------------------------------------------------
|
||||
static let isDebugEnabled = _isDebugAssertConfiguration()
|
||||
|
||||
// Fastlane snapshots
|
||||
private static let areFastlaneSnapshotsRunning = UserDefaults.standard.bool(forKey: "FASTLANE_SNAPSHOT")
|
||||
|
||||
// Bluetooth
|
||||
#if SIMULATEBLUETOOTH
|
||||
static let isTutorialEnabled = areFastlaneSnapshotsRunning || !isDebugEnabled
|
||||
static let isTutorialEnabled = !isDebugEnabled
|
||||
static let isBleUnsupportedWarningEnabled = false
|
||||
static let bleManager = BleManagerSimulated.simulated
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@
|
|||
<string>This app needs access to Bluetooth to connect to Circuit Playground Bluefruit devices</string>
|
||||
<key>NSBluetoothPeripheralUsageDescription</key>
|
||||
<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>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
|
|
|
|||
BIN
BluefruitPlayground/Model/.DS_Store
vendored
Normal file
|
|
@ -30,7 +30,8 @@ class CPBBle {
|
|||
// Constants
|
||||
private static let kLightSequenceFramesPerSecond = 10
|
||||
private static let kLightSequenceDefaultBrightness: CGFloat = 0.25
|
||||
public static let kLightSequenceDefaultSpeed: Double = 0.3
|
||||
private static let kLightSequenceDefaultSpeed: Double = 0.3
|
||||
|
||||
// Singleton
|
||||
static let shared = CPBBle()
|
||||
|
||||
|
|
@ -51,30 +52,25 @@ class CPBBle {
|
|||
weak var buttonsDelegate: CPBBleButtonsDelegate?
|
||||
weak var accelerometerDelegate: CPBBleAccelerometerDelegate?
|
||||
|
||||
var neopixelLightSequenceAnimationBrightness: CGFloat = CPBBle.kLightSequenceDefaultBrightness
|
||||
|
||||
var neopixelLightSequenceAnimationSpeed: Double = CPBBle.kLightSequenceDefaultSpeed {
|
||||
|
||||
didSet {
|
||||
lightSequenceAnimation?.speed = neopixelLightSequenceAnimationSpeed
|
||||
}
|
||||
}
|
||||
|
||||
// Data
|
||||
private var temperatureData = CPBDataSeries<Float>()
|
||||
private var lightData = CPBDataSeries<Float>()
|
||||
private var accelerometerData = CPBDataSeries<BlePeripheral.AccelerometerValue>()
|
||||
private weak var blePeripheral: BlePeripheral?
|
||||
|
||||
private var currentLightSequenceAnimation: LightSequenceAnimation?
|
||||
public var neopixelCurrentLightSequenceAnimationSpeed: Double {
|
||||
get {
|
||||
return currentLightSequenceAnimation?.speed ?? 0
|
||||
}
|
||||
|
||||
set {
|
||||
currentLightSequenceAnimation?.speed = newValue
|
||||
}
|
||||
}
|
||||
private var lightSequenceAnimation: LightSequenceAnimation?
|
||||
|
||||
// MARK: - Lifecycle
|
||||
private init() {
|
||||
registerNotifications(enabled: true)
|
||||
}
|
||||
|
||||
deinit {
|
||||
registerNotifications(enabled: false)
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
|
@ -193,10 +189,6 @@ class CPBBle {
|
|||
return blePeripheral?.cpbAccelerometerLastValue()
|
||||
}
|
||||
|
||||
func lightDataSeries() -> [CPBDataSeries<Float>.Entry] {
|
||||
return lightData.values
|
||||
}
|
||||
|
||||
func temperatureDataSeries() -> [CPBDataSeries<Float>.Entry] {
|
||||
return temperatureData.values
|
||||
}
|
||||
|
|
@ -257,9 +249,36 @@ class CPBBle {
|
|||
|
||||
private func receiveButtonsData(response: Result<(BlePeripheral.ButtonsState, UUID), Error>) {
|
||||
switch response {
|
||||
|
||||
|
||||
|
||||
case let .success(buttonsState, uuid):
|
||||
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
|
||||
if let buttonsDelegate = buttonsDelegate {
|
||||
DispatchQueue.main.async { // Delegates are called in the main thread
|
||||
|
|
@ -273,9 +292,15 @@ class CPBBle {
|
|||
NotificationUserInfoKey.uuid.rawValue: uuid,
|
||||
])
|
||||
|
||||
|
||||
|
||||
|
||||
case .failure(let error):
|
||||
DLog("Error receiving light data: \(error)")
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
private func receiveAccelerometerData(response: Result<(BlePeripheral.AccelerometerValue, UUID), Error>) {
|
||||
|
|
@ -323,26 +348,19 @@ class CPBBle {
|
|||
blePeripheral?.cpbPixelSetColor(index: 0, color: color, pixelMask: pixelMask)
|
||||
}
|
||||
|
||||
func neopixelStartLightSequence(_ lightSequenceGenerator: LightSequenceGenerator,
|
||||
framesPerSecond: Int = CPBBle.kLightSequenceFramesPerSecond,
|
||||
speed: Double = CPBBle.kLightSequenceDefaultSpeed,
|
||||
brightness: CGFloat = CPBBle.kLightSequenceDefaultBrightness,
|
||||
repeating: Bool = true,
|
||||
sendLightSequenceNotifications: Bool = true) {
|
||||
func neopixelStartLightSequence(_ lightSequenceGenerator: LightSequenceGenerator) {
|
||||
neopixelStopLightSequence()
|
||||
|
||||
currentLightSequenceAnimation = LightSequenceAnimation(lightSequenceGenerator: lightSequenceGenerator, framesPerSecond: framesPerSecond, repeating: repeating)
|
||||
currentLightSequenceAnimation!.speed = speed
|
||||
currentLightSequenceAnimation!.start(stopHandler: { [weak self] in
|
||||
self?.blePeripheral?.cpbPixelSetAllPixelsColor(.clear)
|
||||
}) { [weak self] pixelsBytes in
|
||||
lightSequenceAnimation = LightSequenceAnimation(lightSequenceGenerator: lightSequenceGenerator, framesPerSecond: CPBBle.kLightSequenceFramesPerSecond)
|
||||
lightSequenceAnimation!.speed = neopixelLightSequenceAnimationSpeed
|
||||
lightSequenceAnimation!.start() { [weak self] pixelsBytes in
|
||||
guard let self = self else { return }
|
||||
guard let blePeripheral = self.blePeripheral else { return }
|
||||
|
||||
let pixelBytesAdjustingBrightness = pixelsBytes.map {[
|
||||
UInt8(CGFloat($0[0]) * brightness),
|
||||
UInt8(CGFloat($0[1]) * brightness),
|
||||
UInt8(CGFloat($0[2]) * brightness),
|
||||
UInt8(CGFloat($0[0]) * self.neopixelLightSequenceAnimationBrightness),
|
||||
UInt8(CGFloat($0[1]) * self.neopixelLightSequenceAnimationBrightness),
|
||||
UInt8(CGFloat($0[2]) * self.neopixelLightSequenceAnimationBrightness),
|
||||
]}
|
||||
|
||||
let lightData = pixelBytesAdjustingBrightness.reduce(Data()) { (data, element) in
|
||||
|
|
@ -351,35 +369,16 @@ class CPBBle {
|
|||
blePeripheral.cpbPixelsWriteData(offset: 0, pixelData: lightData)
|
||||
|
||||
// Send notification
|
||||
if sendLightSequenceNotifications {
|
||||
NotificationCenter.default.post(name: .didUpdateNeopixelLightSequence, object: nil, userInfo: [
|
||||
NotificationUserInfoKey.value.rawValue: pixelsBytes,
|
||||
NotificationUserInfoKey.uuid.rawValue: blePeripheral.identifier,
|
||||
])
|
||||
}
|
||||
NotificationCenter.default.post(name: .didUpdateNeopixelLightSequence, object: nil, userInfo: [
|
||||
NotificationUserInfoKey.value.rawValue: pixelsBytes,
|
||||
NotificationUserInfoKey.uuid.rawValue: blePeripheral.identifier,
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
func neopixelStopLightSequence() {
|
||||
currentLightSequenceAnimation?.stop()
|
||||
currentLightSequenceAnimation = 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)}
|
||||
}
|
||||
lightSequenceAnimation?.stop()
|
||||
lightSequenceAnimation = nil
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@
|
|||
import UIKit
|
||||
|
||||
protocol LightSequenceGenerator {
|
||||
var numFrames: Int { get }
|
||||
var numPixels: Int { get }
|
||||
var isCyclic: Bool { get }
|
||||
func numFrames() -> Int
|
||||
func numPixels() -> Int
|
||||
func colorsForFrame(_ frame: Int) -> [[UInt8]]
|
||||
}
|
||||
|
||||
|
|
@ -20,16 +19,12 @@ class LightSequence {
|
|||
// static let kLightSequenceDefaultBrightness: CGFloat = 0.25
|
||||
static let kNumPixels = 10
|
||||
|
||||
var numPixels: Int {
|
||||
func numPixels() -> Int {
|
||||
return LightSequence.kNumPixels
|
||||
}
|
||||
|
||||
var isCyclic: Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Utils
|
||||
static func preprocessColorPalette(colors: [UIColor]) -> [[UInt8]] {
|
||||
static func preprocessColorPalette(colors: [UIColor]/*, brightness: CGFloat*/) -> [[UInt8]] {
|
||||
let colorsBytes = colors.map({ color -> [UInt8] in
|
||||
let colorBytes = BlePeripheral.pixelUInt8FromColor(color)
|
||||
return colorBytes
|
||||
|
|
@ -51,16 +46,16 @@ class RotateLightSequence: LightSequence, LightSequenceGenerator {
|
|||
|
||||
// MARK: -
|
||||
override init() {
|
||||
colorsBytes = LightSequence.preprocessColorPalette(colors: RotateLightSequence.kColors)
|
||||
colorsBytes = LightSequence.preprocessColorPalette(colors: RotateLightSequence.kColors/*, brightness: LightSequence.kLightSequenceDefaultBrightness*/)
|
||||
super.init()
|
||||
}
|
||||
|
||||
var numFrames: Int {
|
||||
func numFrames() -> Int {
|
||||
return RotateLightSequence.kNumFrames
|
||||
}
|
||||
|
||||
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]] {
|
||||
|
|
@ -88,22 +83,22 @@ class PulseLightSequence: LightSequence, LightSequenceGenerator {
|
|||
|
||||
// MARK: -
|
||||
override init() {
|
||||
colorsBytes = LightSequence.preprocessColorPalette(colors: PulseLightSequence.kColors)
|
||||
colorsBytes = LightSequence.preprocessColorPalette(colors: PulseLightSequence.kColors/*, brightness: LightSequence.kLightSequenceDefaultBrightness*/)
|
||||
super.init()
|
||||
}
|
||||
|
||||
var numFrames: Int {
|
||||
func numFrames() -> Int {
|
||||
return PulseLightSequence.kNumFrames
|
||||
}
|
||||
|
||||
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]] {
|
||||
var lightBytes = [[UInt8]](repeating: [0, 0, 0], count:numPixels)
|
||||
let reverse = frame >= numFrames / 2
|
||||
let colorIndex = reverse ? (numFrames - 1) - frame : frame
|
||||
let reverse = frame >= numFrames() / 2
|
||||
let colorIndex = reverse ? (numFrames() - 1) - frame : frame
|
||||
for i in 0..<numPixels {
|
||||
//DLog("pixel: \(i) color: \(colorIndex)")
|
||||
let colorBytes = colors[colorIndex]
|
||||
|
|
@ -128,25 +123,25 @@ class SizzleLightSequence: LightSequence, LightSequenceGenerator {
|
|||
|
||||
// MARK: -
|
||||
override init() {
|
||||
colorsBytes = LightSequence.preprocessColorPalette(colors: SizzleLightSequence.kColors)
|
||||
colorsBytes = LightSequence.preprocessColorPalette(colors: SizzleLightSequence.kColors/*, brightness: LightSequence.kLightSequenceDefaultBrightness*/)
|
||||
super.init()
|
||||
}
|
||||
|
||||
var numFrames:Int {
|
||||
func numFrames() -> Int {
|
||||
return SizzleLightSequence.kNumFrames
|
||||
}
|
||||
|
||||
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]] {
|
||||
var lightBytes = [[UInt8]](repeating: [0, 0, 0], count:numPixels)
|
||||
let forwardNumFrames = numFrames / 2
|
||||
let forwardNumFrames = numFrames() / 2
|
||||
let reverse = frame >= forwardNumFrames
|
||||
|
||||
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 {
|
||||
//DLog("pixel: \(i) color: \(colorIndex)")
|
||||
|
|
@ -169,16 +164,16 @@ class SweepLightSequence: LightSequence, LightSequenceGenerator {
|
|||
|
||||
// MARK: -
|
||||
override init() {
|
||||
colorsBytes = LightSequence.preprocessColorPalette(colors: SweepLightSequence.kColors)
|
||||
colorsBytes = LightSequence.preprocessColorPalette(colors: SweepLightSequence.kColors/*, brightness: LightSequence.kLightSequenceDefaultBrightness*/)
|
||||
super.init()
|
||||
}
|
||||
|
||||
var numFrames: Int {
|
||||
func numFrames() -> Int {
|
||||
return SweepLightSequence.kNumFrames
|
||||
}
|
||||
|
||||
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]] {
|
||||
|
|
@ -193,78 +188,3 @@ class SweepLightSequence: LightSequence, LightSequenceGenerator {
|
|||
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 simulatedFrame = 0.0
|
||||
private var startingTimestamp: TimeInterval = 0
|
||||
private var frameHandler: (([[UInt8]])->())?
|
||||
private var stopHandler: (()->())?
|
||||
private var repeating: Bool
|
||||
|
||||
// MARK: -
|
||||
init(lightSequenceGenerator: LightSequenceGenerator, framesPerSecond: Int, repeating: Bool) {
|
||||
init(lightSequenceGenerator: LightSequenceGenerator, framesPerSecond: Int) {
|
||||
self.lightSequenceGenerator = lightSequenceGenerator
|
||||
self.lightSequenceFramesPerSecond = framesPerSecond
|
||||
self.repeating = repeating
|
||||
}
|
||||
|
||||
deinit {
|
||||
stop()
|
||||
}
|
||||
|
||||
func start(stopHandler:(()->())? = nil, frameHandler: @escaping ([[UInt8]])->()) {
|
||||
self.stopHandler = stopHandler
|
||||
func start(frameHandler: @escaping ([[UInt8]])->()) {
|
||||
self.frameHandler = frameHandler
|
||||
|
||||
// Create displayLink if needed
|
||||
if displaylink == nil {
|
||||
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 }
|
||||
displaylink.preferredFramesPerSecond = lightSequenceFramesPerSecond
|
||||
startingTimestamp = CACurrentMediaTime()
|
||||
startingTimestamp = displaylink.timestamp
|
||||
}
|
||||
|
||||
func stop() {
|
||||
displaylink?.invalidate()
|
||||
displaylink = nil
|
||||
displaylink?.remove(from: .main, forMode: .default)
|
||||
stopHandler?()
|
||||
}
|
||||
|
||||
@objc func displayLinkStep(displaylink: CADisplayLink) {
|
||||
|
||||
let currentTimestamp = CACurrentMediaTime() - 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 fps = Double(currentLightSequenceFramesPerSecond)
|
||||
let currentTimestamp = displaylink.timestamp - startingTimestamp
|
||||
let numFrames = Double(lightSequenceGenerator.numFrames())
|
||||
let frame = (currentTimestamp * numFrames * speed).truncatingRemainder(dividingBy:numFrames)
|
||||
//let frame = simulatedFrame.truncatingRemainder(dividingBy:numFrames)
|
||||
//simulatedFrame += 0.4
|
||||
|
||||
//DLog("frame: \(frame)")
|
||||
|
||||
var pixelsBytes: [[UInt8]]
|
||||
if LightSequenceAnimation.kIsFrameInterpolationEnabled {
|
||||
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 preFactor = 1 - postFactor
|
||||
|
||||
//DLog("pre: \(preFrame), post: \(postFrame), frame: \(frame)")
|
||||
|
||||
let pixelsBytesPre = lightSequenceGenerator.colorsForFrame(preFrame)
|
||||
let pixelsBytesPost = lightSequenceGenerator.colorsForFrame(postFrame)
|
||||
|
||||
|
|
|
|||
BIN
BluefruitPlayground/Resources/.DS_Store
vendored
Normal file
|
|
@ -6,11 +6,22 @@
|
|||
// Startup
|
||||
"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
|
||||
"splash_restoringconnection" = "Restoring connection...";
|
||||
|
||||
"bluetooth_unsupported" = "This device doesn't support Bluetooth Low Energy";
|
||||
"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
|
||||
"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_action" = "Next";
|
||||
"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";
|
||||
|
||||
|
||||
|
|
@ -61,26 +72,9 @@ https://github.com/mindsnacks/MSWeakTimer";
|
|||
"about_ios_link1_text" = "uf2 firmware file";
|
||||
"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_title" = "Select CPB";
|
||||
"scanner_title" = "Finding CPB";
|
||||
|
||||
"scanner_connecting" = "Connecting...";
|
||||
"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_unnamed" = "<Unknown>";
|
||||
|
||||
"scanner_automatic_action" = "Select device automatically";
|
||||
"scanner_problems_action" = "I can't find my device";
|
||||
"scanner_backbutton" = "Disconnect";
|
||||
|
||||
|
|
@ -115,9 +108,9 @@ Download it from the Bluefruit Playground guide at learn.adafruit.com";
|
|||
|
||||
// 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_light_title" = "Light Sensor";
|
||||
"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_temperature_title" = "Temperature";
|
||||
"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_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_title" = "Neopixels";
|
||||
"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_speed" = "Speed";
|
||||
|
|
@ -160,7 +151,6 @@ Download it from the Bluefruit Playground guide at learn.adafruit.com";
|
|||
|
||||
"lightsensor_panel_title" = "Luminance Reading";
|
||||
"lightsensor_panel_unit" = "Light level";
|
||||
"lightsensor_chartpanel_title" = "Luminance Chart";
|
||||
|
||||
// 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_farenheit" = "ºF";
|
||||
|
||||
"temperature_panel_title" = "Temperature Chart";
|
||||
"temperature_panel_title" = "Temperature Reading";
|
||||
"temperature_chart_nodata" = "No chart data available";
|
||||
|
||||
// Help
|
||||
|
|
|
|||
334
BluefruitPlayground/Resources/models3d/Sparky_Gold1.dae
Normal file
|
|
@ -16,22 +16,20 @@ import UIKit
|
|||
class NavigationBarWithScrollAwareRightButton : UINavigationBar {
|
||||
// Constants
|
||||
private static let kButtonTag = 100
|
||||
public struct CustomButtonMetrics {
|
||||
static let rightMargin: CGFloat = 8//12
|
||||
|
||||
// Config (navbar metrics)
|
||||
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
|
||||
|
||||
private static let bottomMarginForLargeState: CGFloat = 6//9
|
||||
private static let bottomMarginForSmallState: CGFloat = 2
|
||||
static let navBarHeightSmallState: CGFloat = 44
|
||||
static let navBarHeightLargeState: CGFloat = 96.5
|
||||
}
|
||||
|
||||
// Data
|
||||
private var navigationButton: UIButton?// = UIButton(type: .custom)
|
||||
private var topViewController: UIViewController?
|
||||
|
||||
|
||||
|
||||
// MARK: -
|
||||
public func setRightButton(topViewController: UIViewController, image: UIImage?, target: Any?, action: Selector) {
|
||||
navigationButton?.removeFromSuperview()
|
||||
|
|
@ -49,8 +47,8 @@ class NavigationBarWithScrollAwareRightButton : UINavigationBar {
|
|||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
NSLayoutConstraint.activate([
|
||||
|
||||
button.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -NavigationBarWithScrollAwareRightButton.rightMargin),
|
||||
button.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -NavigationBarWithScrollAwareRightButton.bottomMarginForLargeState),
|
||||
button.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -CustomButtonMetrics.rightMargin),
|
||||
button.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -CustomButtonMetrics.bottomMarginForLargeState),
|
||||
])
|
||||
|
||||
self.layoutIfNeeded() // Force layout to remove adding animation
|
||||
|
|
@ -61,14 +59,21 @@ class NavigationBarWithScrollAwareRightButton : UINavigationBar {
|
|||
self.topViewController = topViewController
|
||||
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 {
|
||||
return (self.frame.height - NavigationBarWithScrollAwareRightButton.navBarHeightSmallState) / (NavigationBarWithScrollAwareRightButton.navBarHeightLargeState - NavigationBarWithScrollAwareRightButton.navBarHeightSmallState)
|
||||
return (self.frame.height - CustomButtonMetrics.navBarHeightSmallState) / (CustomButtonMetrics.navBarHeightLargeState - CustomButtonMetrics.navBarHeightSmallState)
|
||||
}
|
||||
|
||||
public func updateRightButtonPosition() {
|
||||
let yDelta = NavigationBarWithScrollAwareRightButton.bottomMarginForLargeState - NavigationBarWithScrollAwareRightButton.bottomMarginForSmallState
|
||||
let yDelta = CustomButtonMetrics.bottomMarginForLargeState - CustomButtonMetrics.bottomMarginForSmallState
|
||||
|
||||
let y = yDelta * max(0, (1-navigationBarScrollViewProgress))
|
||||
navigationButton?.transform = CGAffineTransform(translationX: 0, y: y)
|
||||
|
|
|
|||
|
|
@ -10,68 +10,33 @@ import UIKit
|
|||
|
||||
struct ScreenFlowManager {
|
||||
|
||||
// Data
|
||||
private static var wasManualScanningLastUsed = false // Last scanning method used
|
||||
|
||||
// MARK: - Go to app areas
|
||||
public static func gotoAutoconnect() {
|
||||
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
|
||||
|
||||
public static func gotoScanner() {
|
||||
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
|
||||
let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: AutoConnectViewController.kNavigationControllerIdentifier)
|
||||
changeRootViewController(rootViewController: rootNavigationViewController, animationOptions: transition)
|
||||
let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: ScannerViewController.kIdentifier)
|
||||
ScreenFlowManager.changeRootViewController(rootViewController: rootNavigationViewController, animated: false)
|
||||
}
|
||||
|
||||
public static func goToManualScan() {
|
||||
let bluetoothErrorDisplayed = updateStatusViewControllerIfBluetoothStateIsNotReady()
|
||||
guard !bluetoothErrorDisplayed else { return }
|
||||
|
||||
guard let window = UIApplication.shared.keyWindow else { return }
|
||||
let isAlreadyInManualScan = (window.rootViewController as? UINavigationController)?.topViewController is ScannerViewController
|
||||
guard !isAlreadyInManualScan else { return }
|
||||
|
||||
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
|
||||
let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: ScannerViewController.kNavigationControllerIdentifier)
|
||||
changeRootViewController(rootViewController: rootNavigationViewController, animationOptions: .transitionFlipFromRight)
|
||||
}
|
||||
|
||||
public static func restoreAndGoToCPBModules() {
|
||||
public static func restoreAndGoToHome() {
|
||||
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
|
||||
|
||||
let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: HomeViewController.kNavigationControllerIdentifier)
|
||||
// Add home to scanner
|
||||
let rootNavigationViewController = mainStoryboard.instantiateViewController(withIdentifier: ScannerViewController.kIdentifier) as! UINavigationController
|
||||
let homeViewController = mainStoryboard.instantiateViewController(withIdentifier: HomeViewController.kIdentifier)
|
||||
rootNavigationViewController.viewControllers.append(homeViewController)
|
||||
|
||||
changeRootViewController(rootViewController: rootNavigationViewController) {
|
||||
CPBBle.shared.neopixelStartLightSequence(FlashLightSequence(baseColor: .lightGray), speed: 1, repeating: false, sendLightSequenceNotifications: false)
|
||||
}
|
||||
ScreenFlowManager.changeRootViewController(rootViewController: rootNavigationViewController, animated: 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
|
||||
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 }
|
||||
|
||||
if let animationOptions = animationOptions {
|
||||
window.rootViewController = rootViewController
|
||||
// rootViewController.view.layoutIfNeeded()
|
||||
UIView.transition(with: window, duration: 0.3, options: animationOptions, animations: {
|
||||
if animated {
|
||||
rootViewController.view.layoutIfNeeded()
|
||||
UIView.transition(with: window, duration: 0.3, options: .transitionCrossDissolve, animations: {
|
||||
window.rootViewController = rootViewController
|
||||
}, completion: { completed in
|
||||
completionHandler?()
|
||||
})
|
||||
|
|
@ -81,92 +46,4 @@ struct ScreenFlowManager {
|
|||
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 accelerometerEulerZLabel: UILabel!
|
||||
|
||||
|
||||
private var currentState = BlePeripheral.ButtonsState(slideSwitch: .left, buttonA: .released, buttonB: .released)
|
||||
|
||||
// MARK: - Lifecycle
|
||||
override func viewDidLoad() {
|
||||
|
|
@ -37,6 +37,16 @@ class AccelerometerPanelViewController: ModulePanelViewController {
|
|||
accelerometerTitleLabel.text = localizationManager.localizedString("accelerometer_panel_accelerometer_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(
|
||||
|
|
@ -55,4 +65,22 @@ class AccelerometerPanelViewController: ModulePanelViewController {
|
|||
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 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 {
|
||||
// Constants
|
||||
static let kIdentifier = "AccelerometerViewController"
|
||||
|
||||
|
||||
//For Recording Function
|
||||
var recordButtonWasSelected: Bool = false
|
||||
var preferStatusBarHidden: Bool!
|
||||
|
||||
|
||||
// UI
|
||||
@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
|
||||
private var circuitNode: SCNNode!
|
||||
private var jawNode: SCNNode!
|
||||
|
||||
private var headNode: SCNNode!
|
||||
|
||||
private var sparkyFaceNode: SCNNode!
|
||||
|
||||
private var valuesPanelViewController: AccelerometerPanelViewController!
|
||||
|
||||
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
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
||||
super.viewDidLoad()
|
||||
|
||||
// Add panels
|
||||
valuesPanelViewController = (addPanelViewController(storyboardIdentifier: AccelerometerPanelViewController.kIdentifier) as! AccelerometerPanelViewController)
|
||||
|
||||
// Load base
|
||||
let scene = SCNScene(named: "cpb.scn")!
|
||||
let scene = SCNScene(named: "Sparky_Gold1.dae")!
|
||||
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
|
||||
sceneView.scene = scene
|
||||
|
|
@ -41,13 +325,44 @@ class AccelerometerViewController: ModuleViewController {
|
|||
|
||||
// Localization
|
||||
let localizationManager = LocalizationManager.shared
|
||||
self.title = localizationManager.localizedString("accelerometer_title")
|
||||
self.title = localizationManager.localizedString("Puppets")
|
||||
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) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
switchToFrontCamera()
|
||||
|
||||
// Initial value
|
||||
if let acceleration = CPBBle.shared.accelerometerLastValue() {
|
||||
self.acceleration = acceleration
|
||||
|
|
@ -56,6 +371,136 @@ class AccelerometerViewController: ModuleViewController {
|
|||
|
||||
// Set delegate
|
||||
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) {
|
||||
|
|
@ -68,31 +513,112 @@ class AccelerometerViewController: ModuleViewController {
|
|||
// MARK: - UI
|
||||
private func updateValueUI() {
|
||||
|
||||
if playIntroAnimation == true {
|
||||
|
||||
// Calculate Euler Angles
|
||||
let eulerAngles = eulerAnglesFromAcceleration()
|
||||
|
||||
let eulerAnglesForHead = eulerAnglesFromAccelerationForHead()
|
||||
//DLog("Euler: pitch: \(eulerAngles.x) yaw: \(eulerAngles.y) roll: \(eulerAngles.z)")
|
||||
|
||||
// Update circuit model orientation
|
||||
SCNTransaction.animationDuration = BlePeripheral.kCPBAcceleromterDefaultPeriod
|
||||
circuitNode.eulerAngles = eulerAngles
|
||||
|
||||
jawNode.eulerAngles = eulerAngles
|
||||
headNode.eulerAngles = eulerAnglesForHead
|
||||
|
||||
|
||||
// Update panel
|
||||
valuesPanelViewController.accelerationReceived(acceleration: self.acceleration, eulerAngles: eulerAngles)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private func eulerAnglesFromAcceleration() -> 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))
|
||||
|
||||
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))
|
||||
|
||||
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
|
||||
extension AccelerometerViewController: CPBBleAccelerometerDelegate {
|
||||
|
||||
func cpbleAccelerationReceived(_ acceleration: BlePeripheral.AccelerometerValue) {
|
||||
self.acceleration = acceleration
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,16 @@ class ButtonStatusPanelViewController: ModulePanelViewController {
|
|||
// Init
|
||||
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
|
||||
let localizationManager = LocalizationManager.shared
|
||||
titleLabel.text = localizationManager.localizedString("buttonstatus_panel_title")
|
||||
|
|
@ -57,6 +67,8 @@ class ButtonStatusPanelViewController: ModulePanelViewController {
|
|||
}
|
||||
|
||||
private func animateDown(view: UIView) {
|
||||
|
||||
|
||||
UIView.animate(withDuration: 0.2) {
|
||||
view.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
|
||||
view.tintColor = self.onColor
|
||||
|
|
@ -88,6 +100,7 @@ class ButtonStatusPanelViewController: ModulePanelViewController {
|
|||
|
||||
if buttonsState.buttonA != currentState.buttonA {
|
||||
animateState(view: buttonAStatusView, isPressed: buttonsState.buttonA == .pressed)
|
||||
// NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Command"), object: nil)
|
||||
}
|
||||
|
||||
if buttonsState.buttonB != currentState.buttonB {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import UIKit
|
|||
|
||||
class HomeViewController: UIViewController {
|
||||
// Constants
|
||||
static let kNavigationControllerIdentifier = "ModulesNavigationController"
|
||||
static let kIdentifier = "HomeViewController"
|
||||
|
||||
// UI
|
||||
|
|
@ -67,60 +66,18 @@ class HomeViewController: UIViewController {
|
|||
super.viewDidLoad()
|
||||
|
||||
// Setup UI
|
||||
/*
|
||||
let topContentInsetForDetails: CGFloat = 20
|
||||
baseTableView.contentInset = UIEdgeInsets(top: topContentInsetForDetails, left: 0, bottom: 0, right: 0)
|
||||
*/
|
||||
|
||||
// Localization
|
||||
let localizationManager = LocalizationManager.shared
|
||||
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
|
||||
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 {
|
||||
return 2 // Details + Modules
|
||||
}
|
||||
|
|
@ -130,31 +87,23 @@ extension HomeViewController: UITableViewDataSource {
|
|||
return 1
|
||||
}
|
||||
else {
|
||||
return menuItems.count + 1 // + 1 disconnect
|
||||
return menuItems.count
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
let cellType = cellTypeFromIndexPath(indexPath)
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: cellType.reuseIdentifier, for: indexPath)
|
||||
let reuseIdentifier = indexPath.section == 0 ? "DetailsCell" : "ModuleCell"
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath)
|
||||
|
||||
// Add cell data here instead of using willDisplay to avoid problems with automatic dimension calculation
|
||||
let localizationManager = LocalizationManager.shared
|
||||
let isDetails = indexPath.section == 0
|
||||
|
||||
switch(cellType) {
|
||||
case .details:
|
||||
if isDetails {
|
||||
let detailsCell = cell as! TitleTableViewCell
|
||||
detailsCell.titleLabel.text = localizationManager.localizedString("modules_subtitle")
|
||||
/*
|
||||
case .peripheral
|
||||
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:
|
||||
}
|
||||
else {
|
||||
let moduleCell = cell as! CommonTableViewCell
|
||||
let menuItem = menuItems[indexPath.row]
|
||||
|
||||
|
|
@ -164,16 +113,12 @@ extension HomeViewController: UITableViewDataSource {
|
|||
let subtitleStringId = menuItem.subtitleStringId
|
||||
moduleCell.subtitleLabel?.text = localizationManager.localizedString(subtitleStringId)
|
||||
|
||||
//moduleCell.setPanelBackgroundColor(menuItem.color)
|
||||
moduleCell.iconImageView.backgroundColor = menuItem.color
|
||||
moduleCell.iconImageView.layer.borderColor = UIColor(named: "text_default")?.cgColor
|
||||
moduleCell.iconImageView.layer.borderWidth = 1
|
||||
moduleCell.iconImageView.layer.cornerRadius = 7
|
||||
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
|
||||
|
|
@ -184,64 +129,36 @@ extension HomeViewController: UITableViewDataSource {
|
|||
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) {
|
||||
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
|
||||
}
|
||||
let isDetails = indexPath.section == 0
|
||||
guard !isDetails else { return }
|
||||
|
||||
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)
|
||||
}
|
||||
guard let module = Modules(rawValue: indexPath.row) else { return }
|
||||
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) {
|
||||
|
||||
self.show(viewController, sender: self)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// MARK: UIScrollViewDelegate
|
||||
extension HomeViewController {
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
// NavigationBar Button Custom Animation
|
||||
if let customNavigationBar = navigationController?.navigationBar as? NavigationBarWithScrollAwareRightButton {
|
||||
customNavigationBar.updateRightButtonPosition()
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
|
|
|||
|
|
@ -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 lightmeterPanelViewController: LightSensorPanelViewController!
|
||||
private var light: Float?
|
||||
private var chartPanelViewController: LightChartPanelViewController!
|
||||
|
||||
// MARK: - Lifecycle
|
||||
override func viewDidLoad() {
|
||||
|
|
@ -25,9 +24,6 @@ class LightSensorViewController: ModuleViewController {
|
|||
// Add panels
|
||||
lightmeterPanelViewController = (addPanelViewController(storyboardIdentifier: LightSensorPanelViewController.kIdentifier) as! LightSensorPanelViewController)
|
||||
|
||||
chartPanelViewController = (addPanelViewController(storyboardIdentifier: LightChartPanelViewController.kIdentifier) as! LightChartPanelViewController)
|
||||
|
||||
|
||||
// Localization
|
||||
let localizationManager = LocalizationManager.shared
|
||||
self.title = localizationManager.localizedString("lightsensor_title")
|
||||
|
|
@ -65,9 +61,6 @@ class LightSensorViewController: ModuleViewController {
|
|||
private func updateValueUI() {
|
||||
if let light = self.light {
|
||||
lightmeterPanelViewController.lightValueReceived(light)
|
||||
|
||||
// Update chart
|
||||
chartPanelViewController.lightValueReceived()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,12 +44,7 @@ class CircuitViewController: UIViewController {
|
|||
}
|
||||
|
||||
// Init neopixels color
|
||||
for (i, neopixelView) in self.neopixelsContainerView.subviews.enumerated() {
|
||||
if i < self.isNeopixelSelected.count {
|
||||
neopixelView.backgroundColor = .clear
|
||||
}
|
||||
}
|
||||
//neopixelsReset(animated: false)
|
||||
neopixelsReset(animated: false)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
|
|
@ -103,6 +98,7 @@ class CircuitViewController: UIViewController {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Notifications
|
||||
private var didUpdateNeopixelLightSequenceObserver: NSObjectProtocol?
|
||||
|
||||
|
|
@ -126,6 +122,7 @@ class CircuitViewController: UIViewController {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
if let didUpdateNeopixelLightSequenceObserver = didUpdateNeopixelLightSequenceObserver {notificationCenter.removeObserver(didUpdateNeopixelLightSequenceObserver)}
|
||||
|
|
@ -171,6 +168,7 @@ class CircuitViewController: UIViewController {
|
|||
// Animate
|
||||
UIView.animate(withDuration: 0.2, animations: {
|
||||
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
|
||||
}) { (_) in
|
||||
selectedView.transform = .identity
|
||||
|
|
@ -183,7 +181,7 @@ class CircuitViewController: UIViewController {
|
|||
}
|
||||
|
||||
// MARK: - Actions
|
||||
func setNeopixelsColor(_ color: UIColor, onlySelected: Bool, animated: Bool, baseColor: UIColor? = nil) {
|
||||
func setNeopixelsColor(_ color: UIColor, onlySelected: Bool, animated: Bool) {
|
||||
|
||||
if onlySelected {
|
||||
CPBBle.shared.neopixelSetPixelColor(color, pixelMask: isNeopixelSelected)
|
||||
|
|
@ -194,10 +192,9 @@ class CircuitViewController: UIViewController {
|
|||
|
||||
// UI Animation
|
||||
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() {
|
||||
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)
|
||||
}
|
||||
|
||||
private func selectColor(_ color: UIColor, baseColor: UIColor) {
|
||||
private func selectColor(_ color: UIColor) {
|
||||
// Update circuit
|
||||
circuitViewController.setNeopixelsColor(color, onlySelected: true, animated: true, baseColor: baseColor)
|
||||
circuitViewController.setNeopixelsColor(color, onlySelected: true, animated: true)
|
||||
}
|
||||
|
||||
// MARK: - Page Management
|
||||
|
|
@ -104,15 +104,14 @@ class NeoPixelsViewController: ModuleViewController {
|
|||
|
||||
// MARK: - NeopixelsColorPaletteViewControllerDelegate
|
||||
extension NeoPixelsViewController: NeopixelsColorPaletteViewControllerDelegate {
|
||||
func colorPaletteColorSelected(color: UIColor, baseColor: UIColor) {
|
||||
selectColor(color, baseColor: baseColor)
|
||||
func colorPaletteColorSelected(color: UIColor) {
|
||||
selectColor(color)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - NeopixelColorWheelViewControllerDelegate
|
||||
// MARK: - NeopixelsColorPaletteViewControllerDelegate
|
||||
extension NeoPixelsViewController: NeopixelColorWheelViewControllerDelegate {
|
||||
func colorWheelColorSelected(color: UIColor, baseColor: UIColor) {
|
||||
selectColor(color, baseColor: baseColor)
|
||||
func colorWheelColorSelected(color: UIColor) {
|
||||
selectColor(color)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import UIKit
|
|||
import FlexColorPicker
|
||||
|
||||
protocol NeopixelsColorPaletteViewControllerDelegate: class {
|
||||
func colorPaletteColorSelected(color: UIColor, baseColor: UIColor)
|
||||
func colorPaletteColorSelected(color: UIColor)
|
||||
}
|
||||
|
||||
class NeopixelsColorPaletteViewController: ModulePanelViewController {
|
||||
|
|
@ -124,6 +124,6 @@ class NeopixelsColorPaletteViewController: ModulePanelViewController {
|
|||
let hsbColor = HSBColor(color: color)
|
||||
let colorWithBrighness = hsbColor.withBrightness(brightness).toUIColor()
|
||||
|
||||
delegate?.colorPaletteColorSelected(color: colorWithBrighness, baseColor: color)
|
||||
delegate?.colorPaletteColorSelected(color: colorWithBrighness)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import UIKit
|
|||
import FlexColorPicker
|
||||
|
||||
protocol NeopixelColorWheelViewControllerDelegate: class {
|
||||
func colorWheelColorSelected(color: UIColor, baseColor: UIColor)
|
||||
func colorWheelColorSelected(color: UIColor)
|
||||
}
|
||||
|
||||
class NeopixelsColorWheelViewController: ModulePanelViewController {
|
||||
|
|
@ -96,7 +96,7 @@ class NeopixelsColorWheelViewController: ModulePanelViewController {
|
|||
let hsbColor = HSBColor(color: color)
|
||||
let colorWithBrighness = hsbColor.withBrightness(brightness).toUIColor()
|
||||
|
||||
delegate?.colorWheelColorSelected(color: colorWithBrighness, baseColor: color)
|
||||
delegate?.colorWheelColorSelected(color: colorWithBrighness)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ class NeopixelsLightSequenceViewController: ModulePanelViewController {
|
|||
|
||||
// Data
|
||||
private var previewViewControllers = [PixelsPreviewViewController]()
|
||||
private var speed: Double = 0.3
|
||||
|
||||
// MARK: - Lifecycle
|
||||
override func viewDidLoad() {
|
||||
|
|
@ -29,10 +30,8 @@ class NeopixelsLightSequenceViewController: ModulePanelViewController {
|
|||
let _ = previewViewControllers.map{$0.tag = $0.view.superview?.tag ?? 0}
|
||||
|
||||
// Set initial speed
|
||||
let speed = CPBBle.kLightSequenceDefaultSpeed
|
||||
speedSlider.value = Float(speed)
|
||||
//speedChanged(speedSlider)
|
||||
let _ = previewViewControllers.map{$0.speed = speed}
|
||||
speedChanged(speedSlider)
|
||||
|
||||
// Localization
|
||||
let localizationManager = LocalizationManager.shared
|
||||
|
|
@ -48,10 +47,10 @@ class NeopixelsLightSequenceViewController: ModulePanelViewController {
|
|||
}
|
||||
|
||||
@IBAction func speedChanged(_ sender: UISlider) {
|
||||
let speed = Double(sender.value)
|
||||
speed = Double(sender.value)
|
||||
DLog("speed: \(speed)")
|
||||
|
||||
CPBBle.shared.neopixelCurrentLightSequenceAnimationSpeed = speed
|
||||
CPBBle.shared.neopixelLightSequenceAnimationSpeed = speed
|
||||
let _ = previewViewControllers.map{$0.speed = speed}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class PixelsPreviewViewController: UIViewController {
|
|||
|
||||
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!.start() { [weak self] pixelsBytes in
|
||||
guard let self = self else { return }
|
||||
|
|
@ -72,8 +72,6 @@ class PixelsPreviewViewController: UIViewController {
|
|||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
lightSequenceAnimation?.stop()
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
|
|
|
|||
|
|
@ -70,17 +70,6 @@ class AboutViewController: UIViewController {
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
// Constants
|
||||
static let kNavigationControllerIdentifier = "ScannerNavigationController"
|
||||
static let kIdentifier = "ScannerNavigationController" //"ScannerViewController"
|
||||
|
||||
// Config
|
||||
private static let kServicesToScan: [CBUUID]? = nil //[BlePeripheral.kUartServiceUUID]
|
||||
private static let kDelayToShowWait: TimeInterval = 1.0
|
||||
|
||||
// UI
|
||||
|
|
@ -21,23 +22,13 @@ class ScannerViewController: UIViewController {
|
|||
@IBOutlet weak var waitView: UIView!
|
||||
@IBOutlet weak var waitLabel: UILabel!
|
||||
@IBOutlet weak var problemsButton: UIButton!
|
||||
@IBOutlet weak var scanAutomaticallyButton: CornerShadowButton!
|
||||
@IBOutlet weak var actionsContainerView: UIStackView!
|
||||
|
||||
// Data
|
||||
private let refreshControl = UIRefreshControl()
|
||||
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 var selectedPeripheral: BlePeripheral?
|
||||
private var infoAlertController: UIAlertController?
|
||||
|
||||
private var isBaseTableScrolling = false
|
||||
|
|
@ -68,7 +59,6 @@ class ScannerViewController: UIViewController {
|
|||
|
||||
waitLabel.text = localizationManager.localizedString("scanner_searching")
|
||||
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) {
|
||||
|
|
@ -85,7 +75,7 @@ class ScannerViewController: UIViewController {
|
|||
if connectedPeripherals.count == 1, let peripheral = connectedPeripherals.first {
|
||||
DLog("Disconnect from previously connected peripheral")
|
||||
// Disconnect from peripheral
|
||||
disconnect(peripheral: peripheral)
|
||||
bleManager.disconnect(from: peripheral)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -99,17 +89,11 @@ class ScannerViewController: UIViewController {
|
|||
updateScannedPeripherals()
|
||||
|
||||
// Start scannning
|
||||
//bleManager.startScan(withServices: ScannerViewController.kServicesToScan)
|
||||
if !bleManager.isScanning {
|
||||
bleManager.startScan()
|
||||
|
||||
}
|
||||
// Remove saved peripheral for autoconnect
|
||||
Settings.autoconnectPeripheralIdentifier = nil
|
||||
bleManager.startScan(withServices: ScannerViewController.kServicesToScan)
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
override func viewDidDisappear(_ animated: Bool) {
|
||||
super.viewDidDisappear(animated)
|
||||
|
||||
// Stop scanning
|
||||
bleManager.stopScan()
|
||||
|
|
@ -119,6 +103,7 @@ class ScannerViewController: UIViewController {
|
|||
}
|
||||
|
||||
deinit {
|
||||
|
||||
// Ble Notifications
|
||||
registerNotifications(enabled: false)
|
||||
}
|
||||
|
|
@ -232,7 +217,7 @@ class ScannerViewController: UIViewController {
|
|||
alertController.addAction(okAction)
|
||||
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
|
||||
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) {
|
||||
|
|
@ -296,16 +293,17 @@ class ScannerViewController: UIViewController {
|
|||
self.present(viewController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@IBAction func scanAutomatically(_ sender: Any) {
|
||||
ScreenFlowManager.gotoAutoconnect()
|
||||
}
|
||||
|
||||
private func showPeripheralDetails() {
|
||||
// Save selected peripheral for autoconnect
|
||||
Settings.autoconnectPeripheralIdentifier = selectedPeripheral?.identifier
|
||||
|
||||
// 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
|
||||
|
|
@ -459,6 +457,15 @@ extension ScannerViewController {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
|
||||
isBaseTableScrolling = false
|
||||
|
||||
if isScannerTableWaitingForReload {
|
||||
reloadBaseTable()
|
||||
}
|
||||
}*/
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
|
||||
// NavigationBar Button Custom Animation
|
||||
|
|
@ -468,7 +475,7 @@ extension ScannerViewController {
|
|||
|
||||
// Move Refresh control when a large title is used
|
||||
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
|
||||
|
|
|
|||
|
|
@ -17,10 +17,6 @@ class StartupViewController: UIViewController {
|
|||
private static let kServicesToReconnect = [BlePeripheral.kUartServiceUUID]
|
||||
private static let kReconnectTimeout = 2.0
|
||||
|
||||
// UI
|
||||
@IBOutlet weak var restoreConnectionLabel: UILabel!
|
||||
|
||||
|
||||
// Data
|
||||
private let bleSupportSemaphore = DispatchSemaphore(value: 0)
|
||||
private var startTime: CFAbsoluteTime!
|
||||
|
|
@ -28,12 +24,6 @@ class StartupViewController: UIViewController {
|
|||
// MARK: - Lifecycle
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// UI
|
||||
restoreConnectionLabel.alpha = 0
|
||||
|
||||
// Localization
|
||||
restoreConnectionLabel.text = LocalizationManager.shared.localizedString("splash_restoringconnection")
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
|
|
@ -134,7 +124,6 @@ class StartupViewController: UIViewController {
|
|||
// MARK: - Reconnect previously connnected Ble Peripheral
|
||||
private func reconnecToPeripheral(withIdentifier identifier: UUID) {
|
||||
DLog("Reconnecting...")
|
||||
|
||||
// Reconnect
|
||||
let isTryingToReconnect = BleManager.shared.reconnecToPeripherals(withIdentifiers: [identifier], withServices: StartupViewController.kServicesToReconnect, timeout: StartupViewController.kReconnectTimeout)
|
||||
|
||||
|
|
@ -156,22 +145,23 @@ class StartupViewController: UIViewController {
|
|||
}
|
||||
|
||||
private func didDisconnectFromPeripheral() {
|
||||
/*
|
||||
// Clear selected peripheral
|
||||
self.selectedPeripheral = nil
|
||||
*/
|
||||
|
||||
|
||||
// Autoconnect failed
|
||||
connected(peripheral: nil)
|
||||
}
|
||||
|
||||
private func connected(peripheral: BlePeripheral?) {
|
||||
if let peripheral = peripheral {
|
||||
// Show restoring connection label
|
||||
UIView.animate(withDuration: 0.2) {
|
||||
self.restoreConnectionLabel.alpha = 1
|
||||
}
|
||||
|
||||
// Setup peripheral
|
||||
CPBBle.shared.setupPeripheral(blePeripheral: peripheral) { [weak self] result in
|
||||
switch result {
|
||||
case .success():
|
||||
ScreenFlowManager.restoreAndGoToCPBModules()
|
||||
ScreenFlowManager.restoreAndGoToHome()
|
||||
|
||||
case .failure(let error):
|
||||
DLog("Failed setup peripheral: \(error.localizedDescription)")
|
||||
|
|
@ -190,15 +180,12 @@ class StartupViewController: UIViewController {
|
|||
|
||||
// MARK: - Screen Flow
|
||||
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)")
|
||||
|
||||
// Change splash screen to main screen
|
||||
if let rootViewController = self.storyboard?.instantiateViewController(withIdentifier: viewControllerIdentifier) {
|
||||
ScreenFlowManager.changeRootViewController(rootViewController: rootViewController) {
|
||||
// Start scannning
|
||||
//Config.bleManager.startScan()
|
||||
}
|
||||
ScreenFlowManager.changeRootViewController(rootViewController: rootViewController, animated: false)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class TemperaturePanelViewController: ModulePanelViewController {
|
|||
// MARK: - Line Chart
|
||||
private func setupChart() {
|
||||
//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.isUserInteractionEnabled = false
|
||||
|
|
|
|||
|
|
@ -86,15 +86,6 @@ class TipViewController: UIViewController {
|
|||
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) {
|
||||
ScreenFlowManager.gotoAutoconnect()
|
||||
ScreenFlowManager.gotoScanner()
|
||||
}
|
||||
|
||||
@objc private func pageControlTapHandler(sender: UIPageControl) {
|
||||
|
|
|
|||
|
|
@ -46,10 +46,6 @@ class ToneGeneratorViewController: UIViewController {
|
|||
@IBOutlet weak var keysContainerView: UIView!
|
||||
@IBOutlet weak var whiteKeysStackView: UIStackView!
|
||||
@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
|
||||
private var tonesPlaying = Set<Int>()
|
||||
|
|
@ -79,50 +75,19 @@ 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
|
||||
let localizationManager = LocalizationManager.shared
|
||||
self.title = localizationManager.localizedString("tonegenerator_title")
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
// Navigationbar setup
|
||||
if let customNavigationBar = navigationController?.navigationBar as? NavigationBarWithScrollAwareRightButton {
|
||||
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
|
||||
}
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
// Navigationbar setup
|
||||
if let customNavigationBar = navigationController?.navigationBar as? NavigationBarWithScrollAwareRightButton {
|
||||
customNavigationBar.setRightButton(topViewController: self, image: UIImage(named: "help"), target: self, action: #selector(help(_:)))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
@objc private func keyDown(_ sender: UIButton) {
|
||||
|
|
@ -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? {
|
||||
var frequency: Double? = nil
|
||||
if tag >= 100 && tag < 200 { // is white note
|
||||
|
|
@ -199,4 +150,3 @@ class ToneGeneratorViewController: UIViewController {
|
|||
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.
|
||||
}
|
||||
|
||||
func testSnapshots() {
|
||||
func testExample() {
|
||||
/*
|
||||
// UI tests must launch the application that they test.
|
||||
let app = XCUIApplication()
|
||||
|
|
@ -40,7 +40,7 @@ class BluefruitPlaygroundUITests: XCTestCase {
|
|||
let scrollViewsQuery = app.scrollViews
|
||||
let elementsQuery = scrollViewsQuery.otherElements
|
||||
|
||||
sleep(2) // Wait for the intro animation
|
||||
sleep(3) // Wait for the intro animation
|
||||
snapshot("01a_Welcome")
|
||||
|
||||
elementsQuery.buttons["LET'S GET STARTED..."].tap()
|
||||
|
|
@ -51,7 +51,7 @@ class BluefruitPlaygroundUITests: XCTestCase {
|
|||
elementsQuery.buttons["BEGIN PAIRING"].tap()
|
||||
|
||||
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")
|
||||
|
||||
|
|
|
|||
16
Gemfile.lock
|
|
@ -1,7 +1,7 @@
|
|||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.2)
|
||||
CFPropertyList (2.3.6)
|
||||
addressable (2.7.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
atomos (0.1.3)
|
||||
|
|
@ -18,7 +18,7 @@ GEM
|
|||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.5)
|
||||
emoji_regex (1.0.1)
|
||||
excon (0.71.1)
|
||||
excon (0.71.0)
|
||||
faraday (0.17.1)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-cookie_jar (0.0.6)
|
||||
|
|
@ -27,7 +27,7 @@ GEM
|
|||
faraday_middleware (0.13.1)
|
||||
faraday (>= 0.7.4, < 1.0)
|
||||
fastimage (2.1.7)
|
||||
fastlane (2.138.0)
|
||||
fastlane (2.137.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
babosa (>= 1.0.2, < 2.0.0)
|
||||
|
|
@ -36,7 +36,7 @@ GEM
|
|||
commander-fastlane (>= 4.4.6, < 5.0.0)
|
||||
dotenv (>= 2.1.1, < 3.0.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-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 0.13.1)
|
||||
|
|
@ -61,7 +61,7 @@ GEM
|
|||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 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-travis-formatter (>= 0.0.3)
|
||||
gh_inspector (1.1.3)
|
||||
|
|
@ -93,7 +93,7 @@ GEM
|
|||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
json (2.3.0)
|
||||
json (2.1.0)
|
||||
jwt (2.1.0)
|
||||
memoist (0.16.2)
|
||||
mime-types (3.3)
|
||||
|
|
@ -121,7 +121,7 @@ GEM
|
|||
faraday (~> 0.9)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.7)
|
||||
simctl (1.6.6)
|
||||
CFPropertyList
|
||||
naturally
|
||||
slack-notifier (2.3.2)
|
||||
|
|
@ -138,7 +138,7 @@ GEM
|
|||
unf_ext (0.0.7.6)
|
||||
unicode-display_width (1.6.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.14.0)
|
||||
xcodeproj (1.12.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
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 8 Plus",
|
||||
"iPhone 8",
|
||||
"iPhone SE",
|
||||
|
||||
# "iPhone SE",
|
||||
# "iPhone 6",
|
||||
# "iPhone 6 Plus",
|
||||
# "iPhone 5",
|
||||
|
|
@ -29,8 +29,7 @@ languages([
|
|||
# Where should the resulting screenshots be stored?
|
||||
# output_directory "./screenshots"
|
||||
|
||||
# remove the '#' to clear all previously generated screenshots before creating new ones
|
||||
clear_previous_screenshots true
|
||||
# clear_previous_screenshots true # remove the '#' to clear all previously generated screenshots before creating new ones
|
||||
|
||||
# Choose which project/workspace to use
|
||||
# project "./Project.xcodeproj"
|
||||
|
|
|
|||