diff --git a/Assets/Plugins/Android/AndroidManifest.xml b/Assets/Plugins/Android/AndroidManifest.xml index 3e6a69ac..2b65ae5c 100644 --- a/Assets/Plugins/Android/AndroidManifest.xml +++ b/Assets/Plugins/Android/AndroidManifest.xml @@ -1,17 +1,19 @@ - - - + + + + - - - - - + + - \ No newline at end of file + + + + + diff --git a/Assets/Plugins/Android/AndroidManifest.xml.meta b/Assets/Plugins/Android/AndroidManifest.xml.meta index 83e598e6..99cac0a9 100644 --- a/Assets/Plugins/Android/AndroidManifest.xml.meta +++ b/Assets/Plugins/Android/AndroidManifest.xml.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 506e447c8b55f0449ae2b5b5ed37dd99 +guid: 4abd69b18c4ab45ec976851430122248 TextScriptImporter: externalObjects: {} userData: diff --git a/Assets/Plugins/Android/unityandroidbluetoothlelib.jar b/Assets/Plugins/Android/unityandroidbluetoothlelib.jar new file mode 100644 index 00000000..588e1621 Binary files /dev/null and b/Assets/Plugins/Android/unityandroidbluetoothlelib.jar differ diff --git a/Assets/Plugins/Android/unityandroidbluetoothlelib.jar.meta b/Assets/Plugins/Android/unityandroidbluetoothlelib.jar.meta new file mode 100644 index 00000000..003a44eb --- /dev/null +++ b/Assets/Plugins/Android/unityandroidbluetoothlelib.jar.meta @@ -0,0 +1,32 @@ +fileFormatVersion: 2 +guid: f158ceee465c745bc89002ae57bc033e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Android: Android + second: + enabled: 1 + settings: {} + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + DefaultValueInitialized: true + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/BluetoothDeviceScript.cs b/Assets/Plugins/BluetoothDeviceScript.cs new file mode 100644 index 00000000..102148b9 --- /dev/null +++ b/Assets/Plugins/BluetoothDeviceScript.cs @@ -0,0 +1,391 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +public class BluetoothDeviceScript : MonoBehaviour +{ +#if UNITY_IOS + public Dictionary BLEStandardUUIDs = new Dictionary(); +#endif + + public List DiscoveredDeviceList; + + public Action InitializedAction { get; set; } + public Action DeinitializedAction; + public Action ErrorAction; + public Action ServiceAddedAction; + public Action BleStatusChangedAction; + public Action StartedAdvertisingAction; + public Action StoppedAdvertisingAction; + public Action DiscoveredPeripheralAction; + public Action DiscoveredPeripheralWithAdvertisingInfoAction; + public Action DiscoveredBeaconAction; + public Action RetrievedConnectedPeripheralAction; + public Action PeripheralReceivedWriteDataAction; + public Action ConnectedPeripheralAction; + public Action ConnectedDisconnectPeripheralAction; + public Action DisconnectedPeripheralAction; + public Action DiscoveredServiceAction; + public Action DiscoveredCharacteristicAction; + public Action DidWriteCharacteristicAction; + public Dictionary>> DidUpdateNotificationStateForCharacteristicAction; + public Dictionary>> DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction; + public Dictionary>> DidUpdateCharacteristicValueAction; + public Dictionary>> DidUpdateCharacteristicValueWithDeviceAddressAction; + public Action RequestMtuAction; + + // Use this for initialization + void Start () + { + DiscoveredDeviceList = new List (); + DidUpdateNotificationStateForCharacteristicAction = new Dictionary>> (); + DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction = new Dictionary>> (); + DidUpdateCharacteristicValueAction = new Dictionary>> (); + DidUpdateCharacteristicValueWithDeviceAddressAction = new Dictionary>> (); + +#if UNITY_IOS + BLEStandardUUIDs["Heart Rate Measurement"] = "00002A37-0000-1000-8000-00805F9B34FB"; +#endif + } + + // Update is called once per frame + void Update () + { + } + + const string deviceInitializedString = "Initialized"; + const string deviceDeInitializedString = "DeInitialized"; + const string deviceErrorString = "Error"; + const string deviceServiceAdded = "ServiceAdded"; + const string deviceStartedAdvertising = "StartedAdvertising"; + const string deviceStoppedAdvertising = "StoppedAdvertising"; + const string deviceDiscoveredPeripheral = "DiscoveredPeripheral"; + const string deviceDiscoveredBeacon = "DiscoveredBeacon"; + const string deviceRetrievedConnectedPeripheral = "RetrievedConnectedPeripheral"; + const string devicePeripheralReceivedWriteData = "PeripheralReceivedWriteData"; + const string deviceConnectedPeripheral = "ConnectedPeripheral"; + const string deviceDisconnectedPeripheral = "DisconnectedPeripheral"; + const string deviceDiscoveredService = "DiscoveredService"; + const string deviceDiscoveredCharacteristic = "DiscoveredCharacteristic"; + const string deviceDidWriteCharacteristic = "DidWriteCharacteristic"; + const string deviceDidUpdateNotificationStateForCharacteristic = "DidUpdateNotificationStateForCharacteristic"; + const string deviceDidUpdateValueForCharacteristic = "DidUpdateValueForCharacteristic"; + const string deviceLog = "Log"; + const string deviceRequestMtu = "MtuChanged"; + const string bleStatusChanged = "BleStatusChanged"; + + public void OnBluetoothMessage (string message) + { + if (message != null) + { + char[] delim = new char[] { '~' }; + string[] parts = message.Split (delim); + if (parts[0].Equals("DeviceStateChanged")) + { + //throw new Exception("test"); + } + if (parts[0].Equals(bleStatusChanged)) + { + if (BleStatusChangedAction != null) + { + BleStatusChangedAction(int.Parse(parts[1])); + } + } + for (int i = 0; i < parts.Length; ++i) + BluetoothLEHardwareInterface.Log(string.Format("Part: {0} - {1}", i, parts[i])); + + if (message.Length >= deviceInitializedString.Length && message.Substring (0, deviceInitializedString.Length) == deviceInitializedString) + { + if (InitializedAction != null) + InitializedAction (); + } + else if (message.Length >= deviceLog.Length && message.Substring (0, deviceLog.Length) == deviceLog) + { + BluetoothLEHardwareInterface.Log (parts[1]); + } + else if (message.Length >= deviceDeInitializedString.Length && message.Substring (0, deviceDeInitializedString.Length) == deviceDeInitializedString) + { + BluetoothLEHardwareInterface.FinishDeInitialize (); + + if (DeinitializedAction != null) + DeinitializedAction (); + } + else if (message.Length >= deviceErrorString.Length && message.Substring (0, deviceErrorString.Length) == deviceErrorString) + { + string error = ""; + + if (parts.Length >= 2) + error = parts[1]; + + if (ErrorAction != null) + ErrorAction (error); + } + else if (message.Length >= deviceServiceAdded.Length && message.Substring (0, deviceServiceAdded.Length) == deviceServiceAdded) + { + if (parts.Length >= 2) + { + if (ServiceAddedAction != null) + ServiceAddedAction (parts[1]); + } + } + else if (message.Length >= deviceStartedAdvertising.Length && message.Substring (0, deviceStartedAdvertising.Length) == deviceStartedAdvertising) + { + BluetoothLEHardwareInterface.Log ("Started Advertising"); + + if (StartedAdvertisingAction != null) + StartedAdvertisingAction (); + } + else if (message.Length >= deviceStoppedAdvertising.Length && message.Substring (0, deviceStoppedAdvertising.Length) == deviceStoppedAdvertising) + { + BluetoothLEHardwareInterface.Log ("Stopped Advertising"); + + if (StoppedAdvertisingAction != null) + StoppedAdvertisingAction (); + } + else if (message.Length >= deviceDiscoveredPeripheral.Length && message.Substring (0, deviceDiscoveredPeripheral.Length) == deviceDiscoveredPeripheral) + { + if (parts.Length >= 3) + { + // the first callback will only get called the first time this device is seen + // this is because it gets added to the a list in the DiscoveredDeviceList + // after that only the second callback will get called and only if there is + // advertising data available + if (!DiscoveredDeviceList.Contains (parts[1] + "|" + parts[2])) + { + DiscoveredDeviceList.Add (parts[1] + "|" + parts[2]); + + if (DiscoveredPeripheralAction != null) + DiscoveredPeripheralAction (parts[1], parts[2]); + } + + if (parts.Length >= 5 && DiscoveredPeripheralWithAdvertisingInfoAction != null) + { + // get the rssi from the 4th value + int rssi = 0; + if (!int.TryParse (parts[3], out rssi)) + rssi = 0; + List uuidList = new List(); + if (parts[4].Contains(",")) + { + string[] s = parts[4].Split(','); + foreach (var o in s) + { + if (!string.IsNullOrEmpty(o)) + { + uuidList.Add(o); + } + } + } + //// parse the base 64 encoded data that is the 5th value + //byte[] bytes = System.Convert.FromBase64String (parts[4]); + + DiscoveredPeripheralWithAdvertisingInfoAction (parts[1], parts[2], rssi, uuidList.ToArray()); + } + } + } + else if (message.Length >= deviceDiscoveredBeacon.Length && message.Substring (0, deviceDiscoveredBeacon.Length) == deviceDiscoveredBeacon) + { + if (parts.Length >= 7) + { + var iBeaconData = new BluetoothLEHardwareInterface.iBeaconData (); + + iBeaconData.UUID = parts[1]; + if (!int.TryParse (parts[2], out iBeaconData.Major)) + iBeaconData.Major = 0; + if (!int.TryParse (parts[3], out iBeaconData.Minor)) + iBeaconData.Minor = 0; + if (!int.TryParse (parts[4], out iBeaconData.RSSI)) + iBeaconData.RSSI = 0; + if (!int.TryParse (parts[5], out iBeaconData.AndroidSignalPower)) + iBeaconData.AndroidSignalPower = 0; + int iOSProximity = 0; + if (!int.TryParse (parts[6], out iOSProximity)) + iOSProximity = 0; + iBeaconData.iOSProximity = (BluetoothLEHardwareInterface.iOSProximity)iOSProximity; + + if (DiscoveredBeaconAction != null) + DiscoveredBeaconAction (iBeaconData); + } + } + else if (message.Length >= deviceRetrievedConnectedPeripheral.Length && message.Substring (0, deviceRetrievedConnectedPeripheral.Length) == deviceRetrievedConnectedPeripheral) + { + if (parts.Length >= 3) + { + DiscoveredDeviceList.Add (parts[1]); + + if (RetrievedConnectedPeripheralAction != null) + RetrievedConnectedPeripheralAction (parts[1], parts[2]); + } + } + else if (message.Length >= devicePeripheralReceivedWriteData.Length && message.Substring (0, devicePeripheralReceivedWriteData.Length) == devicePeripheralReceivedWriteData) + { + if (parts.Length >= 3) + OnPeripheralData (parts[1], parts[2]); + } + else if (message.Length >= deviceConnectedPeripheral.Length && message.Substring (0, deviceConnectedPeripheral.Length) == deviceConnectedPeripheral) + { + if (parts.Length >= 2 && ConnectedPeripheralAction != null) + ConnectedPeripheralAction (parts[1]); + } + else if (message.Length >= deviceDisconnectedPeripheral.Length && message.Substring (0, deviceDisconnectedPeripheral.Length) == deviceDisconnectedPeripheral) + { + if (parts.Length >= 2) + { + if (ConnectedDisconnectPeripheralAction != null) + ConnectedDisconnectPeripheralAction (parts[1]); + + if (DisconnectedPeripheralAction != null) + DisconnectedPeripheralAction (parts[1]); + } + } + else if (message.Length >= deviceDiscoveredService.Length && message.Substring (0, deviceDiscoveredService.Length) == deviceDiscoveredService) + { + if (parts.Length >= 3 && DiscoveredServiceAction != null) + DiscoveredServiceAction (parts[1], parts[2]); + } + else if (message.Length >= deviceDiscoveredCharacteristic.Length && message.Substring (0, deviceDiscoveredCharacteristic.Length) == deviceDiscoveredCharacteristic) + { + if (parts.Length >= 4 && DiscoveredCharacteristicAction != null) + DiscoveredCharacteristicAction (parts[1], parts[2], parts[3]); + } + else if (message.Length >= deviceDidWriteCharacteristic.Length && message.Substring (0, deviceDidWriteCharacteristic.Length) == deviceDidWriteCharacteristic) + { + if (parts.Length >= 2 && DidWriteCharacteristicAction != null) + DidWriteCharacteristicAction (parts[1]); + } + else if (message.Length >= deviceDidUpdateNotificationStateForCharacteristic.Length && message.Substring (0, deviceDidUpdateNotificationStateForCharacteristic.Length) == deviceDidUpdateNotificationStateForCharacteristic) + { + if (parts.Length >= 3) + { + if (DidUpdateNotificationStateForCharacteristicAction != null && DidUpdateNotificationStateForCharacteristicAction.ContainsKey (parts[1])) + { + var characteristicAction = DidUpdateNotificationStateForCharacteristicAction[parts[1]]; + if (characteristicAction != null && characteristicAction.ContainsKey (parts[2])) + { + var action = characteristicAction[parts[2]]; + if (action != null) + action (parts[2]); + } + } + + if (DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction != null && DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction.ContainsKey (parts[1])) + { + var characteristicAction = DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction[parts[1]]; + if (characteristicAction != null && characteristicAction.ContainsKey (parts[2])) + { + var action = characteristicAction[parts[2]]; + if (action != null) + action (parts[1], parts[2]); + } + } + } + } + else if (message.Length >= deviceDidUpdateValueForCharacteristic.Length && message.Substring (0, deviceDidUpdateValueForCharacteristic.Length) == deviceDidUpdateValueForCharacteristic) + { + if (parts.Length >= 4) + OnBluetoothData (parts[1], parts[2], parts[3]); + } + else if (message.Length >= deviceRequestMtu.Length && message.Substring(0, deviceRequestMtu.Length) == deviceRequestMtu) + { + if (parts.Length >= 3) + { + if (RequestMtuAction != null) + { + int mtu = 0; + if (int.TryParse(parts[2], out mtu)) + RequestMtuAction(parts[1], mtu); + } + } + } + } + } + + public void OnBluetoothData (string base64Data) + { + OnBluetoothData ("", "", base64Data); + } + + public void OnBluetoothData (string deviceAddress, string characteristic, string base64Data) + { + if (base64Data != null) + { + byte[] bytes = System.Convert.FromBase64String (base64Data); + if (bytes.Length > 0) + { + deviceAddress = deviceAddress.ToUpper (); + characteristic = characteristic.ToUpper (); + +#if UNITY_IOS + if (BLEStandardUUIDs.ContainsKey(characteristic)) + characteristic = BLEStandardUUIDs[characteristic]; +#endif + + BluetoothLEHardwareInterface.Log ("Device: " + deviceAddress + " Characteristic Received: " + characteristic); + + string byteString = ""; + foreach (byte b in bytes) + byteString += string.Format ("{0:X2}", b); + + BluetoothLEHardwareInterface.Log (byteString); + + if (DidUpdateCharacteristicValueAction != null && DidUpdateCharacteristicValueAction.ContainsKey (deviceAddress)) + { + var characteristicAction = DidUpdateCharacteristicValueAction[deviceAddress]; +#if UNITY_ANDROID + characteristic = characteristic.ToLower (); +#endif + if (characteristicAction != null && characteristicAction.ContainsKey (characteristic)) + { + var action = characteristicAction[characteristic]; + if (action != null) + action (characteristic, bytes); + } + } + + if (DidUpdateCharacteristicValueWithDeviceAddressAction != null && DidUpdateCharacteristicValueWithDeviceAddressAction.ContainsKey (deviceAddress)) + { + var characteristicAction = DidUpdateCharacteristicValueWithDeviceAddressAction[deviceAddress]; +#if UNITY_ANDROID + characteristic = characteristic.ToLower (); +#endif + if (characteristicAction != null && characteristicAction.ContainsKey (characteristic)) + { + var action = characteristicAction[characteristic]; + if (action != null) + action (deviceAddress, characteristic, bytes); + } + } + } + } + } + + public void OnPeripheralData (string characteristic, string base64Data) + { + if (base64Data != null) + { + byte[] bytes = System.Convert.FromBase64String (base64Data); + if (bytes.Length > 0) + { + BluetoothLEHardwareInterface.Log ("Peripheral Received: " + characteristic); + + string byteString = ""; + foreach (byte b in bytes) + byteString += string.Format ("{0:X2}", b); + + BluetoothLEHardwareInterface.Log (byteString); + + if (PeripheralReceivedWriteDataAction != null) + PeripheralReceivedWriteDataAction (characteristic, bytes); + } + } + } + +#if UNITY_IOS + private void IncludeCoreLocationFramework() + { + // this method is here because Unity now only includes CoreLocation + // if there are methods in the .cs code that access it + Input.location.Stop (); + } +#endif +} diff --git a/Assets/Plugins/BluetoothDeviceScript.cs.meta b/Assets/Plugins/BluetoothDeviceScript.cs.meta new file mode 100644 index 00000000..e31cb904 --- /dev/null +++ b/Assets/Plugins/BluetoothDeviceScript.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b188ba3ac565e48f58fc50dd5db4818d +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Plugins/BluetoothHardwareInterface.cs b/Assets/Plugins/BluetoothHardwareInterface.cs new file mode 100644 index 00000000..4c80d913 --- /dev/null +++ b/Assets/Plugins/BluetoothHardwareInterface.cs @@ -0,0 +1,999 @@ +#define EXPERIMENTAL_MACOS_EDITOR +/* + +This build includes an experimental implementation for the macOS editor of Unity +It is experiemental because of the way that the Unity editor hangs on to plugin +instances after leaving play mode. This causes this plugin to not free up its +resources and therefore can cause crashes in the Unity editor on macOS. + +Since Unity does not give plugins or apps a chance to do anything when the user +hits the play / stop button in the Editor there isn't a chance for the app to +deinitialize this plugin. + +What I have found in my own use of this is that if you put a button on your app +somewhere that you can press before hitting the stop button in the editor and +then in that button handler call this plugin's Deinitialize method it seems to +minimize how often the editor crashes. + +WARNING: using the macOS editor can cause the editor to crash an loose your work +and settings. Save often. You have been warned, so please don't contact me if +you have lost work becausee of this problem. This is experimental only. Use at +your own risk. + +*/ + +using UnityEngine; +using System; +using System.Runtime.InteropServices; +using System.Collections.Generic; + +#if UNITY_2018_3_OR_NEWER +#if UNITY_ANDROID +using UnityEngine.Android; +#endif +#endif + +public class BluetoothLEHardwareInterface +{ + public enum CBCharacteristicProperties + { + CBCharacteristicPropertyBroadcast = 0x01, + CBCharacteristicPropertyRead = 0x02, + CBCharacteristicPropertyWriteWithoutResponse = 0x04, + CBCharacteristicPropertyWrite = 0x08, + CBCharacteristicPropertyNotify = 0x10, + CBCharacteristicPropertyIndicate = 0x20, + CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40, + CBCharacteristicPropertyExtendedProperties = 0x80, + CBCharacteristicPropertyNotifyEncryptionRequired = 0x100, + CBCharacteristicPropertyIndicateEncryptionRequired = 0x200, + }; + + public enum ScanMode + { + LowPower = 0, + Balanced = 1, + LowLatency = 2 + } + + public enum ConnectionPriority + { + LowPower = 0, + Balanced = 1, + High = 2, + } + + public enum iOSProximity + { + Unknown = 0, + Immediate = 1, + Near = 2, + Far = 3, + } + + public struct iBeaconData + { + public string UUID; + public int Major; + public int Minor; + public int RSSI; + public int AndroidSignalPower; + public iOSProximity iOSProximity; + } + +#if UNITY_ANDROID + public enum CBAttributePermissions + { + CBAttributePermissionsReadable = 0x01, + CBAttributePermissionsWriteable = 0x10, + CBAttributePermissionsReadEncryptionRequired = 0x02, + CBAttributePermissionsWriteEncryptionRequired = 0x20, + }; +#else + public enum CBAttributePermissions + { + CBAttributePermissionsReadable = 0x01, + CBAttributePermissionsWriteable = 0x02, + CBAttributePermissionsReadEncryptionRequired = 0x04, + CBAttributePermissionsWriteEncryptionRequired = 0x08, + }; +#endif + +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + + public delegate void UnitySendMessageCallbackDelegate (IntPtr objectName, IntPtr commandName, IntPtr commandData); + + [DllImport ("BluetoothLEOSX")] + private static extern void ConnectUnitySendMessageCallback ([MarshalAs (UnmanagedType.FunctionPtr)]UnitySendMessageCallbackDelegate callbackMethod); + + [DllImport ("BluetoothLEOSX")] + private static extern void OSXBluetoothLELog (string message); + + [DllImport ("BluetoothLEOSX")] + private static extern void OSXBluetoothLEInitialize ([MarshalAs (UnmanagedType.Bool)]bool asCentral, [MarshalAs (UnmanagedType.Bool)]bool asPeripheral); + + [DllImport ("BluetoothLEOSX")] + private static extern void OSXBluetoothLEDeInitialize (); + + [DllImport ("BluetoothLEOSX")] + private static extern void OSXBluetoothLEPauseMessages (bool isPaused); + + [DllImport ("BluetoothLEOSX")] + private static extern void OSXBluetoothLEScanForPeripheralsWithServices (string serviceUUIDsString, bool allowDuplicates, bool rssiOnly, bool clearPeripheralList); + + [DllImport ("BluetoothLEOSX")] + private static extern void OSXBluetoothLERetrieveListOfPeripheralsWithServices (string serviceUUIDsString); + + [DllImport ("BluetoothLEOSX")] + private static extern void OSXBluetoothLEStopScan (); + + [DllImport ("BluetoothLEOSX")] + private static extern void OSXBluetoothLEConnectToPeripheral (string name); + + [DllImport ("BluetoothLEOSX")] + private static extern void OSXBluetoothLEDisconnectAll (); + + [DllImport("BluetoothLEOSX")] + private static extern void OSXBluetoothLERequestMtu (string name, int mtu); + + [DllImport ("BluetoothLEOSX")] + private static extern void OSXBluetoothLEDisconnectPeripheral (string name); + + [DllImport ("BluetoothLEOSX")] + private static extern void OSXBluetoothLEReadCharacteristic (string name, string service, string characteristic); + + [DllImport ("BluetoothLEOSX")] + private static extern void OSXBluetoothLEWriteCharacteristic (string name, string service, string characteristic, byte[] data, int length, bool withResponse); + + [DllImport ("BluetoothLEOSX")] + private static extern void OSXBluetoothLESubscribeCharacteristic (string name, string service, string characteristic); + + [DllImport ("BluetoothLEOSX")] + private static extern void OSXBluetoothLEUnSubscribeCharacteristic (string name, string service, string characteristic); + +#endif + +#if UNITY_IOS || UNITY_TVOS + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLELog (string message); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEInitialize (bool asCentral, bool asPeripheral); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEDeInitialize (); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEPauseMessages (bool isPaused); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEScanForPeripheralsWithServices (string serviceUUIDsString, bool allowDuplicates, bool rssiOnly, bool clearPeripheralList); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLERetrieveListOfPeripheralsWithServices (string serviceUUIDsString); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEStopScan (); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEConnectToPeripheral (string name); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEDisconnectPeripheral (string name); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEReadCharacteristic (string name, string service, string characteristic); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEWriteCharacteristic (string name, string service, string characteristic, byte[] data, int length, bool withResponse); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLESubscribeCharacteristic (string name, string service, string characteristic); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEUnSubscribeCharacteristic (string name, string service, string characteristic); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEDisconnectAll (); + +#if !UNITY_TVOS + [DllImport("__Internal")] + private static extern void _iOSBluetoothLERequestMtu(string name, int mtu); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEScanForBeacons (string proximityUUIDsString); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEStopBeaconScan (); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEPeripheralName (string newName); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLECreateService (string uuid, bool primary); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLERemoveService (string uuid); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLERemoveServices (); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLECreateCharacteristic (string uuid, int properties, int permissions, byte[] data, int length); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLERemoveCharacteristic (string uuid); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLERemoveCharacteristics (); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEStartAdvertising (); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEStopAdvertising (); + + [DllImport ("__Internal")] + private static extern void _iOSBluetoothLEUpdateCharacteristicValue (string uuid, byte[] data, int length); +#endif +#elif UNITY_ANDROID + static AndroidJavaObject _android = null; +#endif + + + private static BluetoothDeviceScript bluetoothDeviceScript; + + public static void Log (string message) + { +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + Debug.Log(message); +#else + if (!Application.isEditor) + { +#if UNITY_IOS || UNITY_TVOS + _iOSBluetoothLELog (message); +#elif UNITY_ANDROID + if (_android == null) + { + AndroidJavaClass javaClass = new AndroidJavaClass ("com.shatalmic.unityandroidbluetoothlelib.UnityBluetoothLE"); + _android = javaClass.CallStatic ("getInstance"); + } + + if (_android != null) + _android.Call ("androidBluetoothLog", message); +#endif + } +#endif + } + + public static BluetoothDeviceScript Initialize (bool asCentral, bool asPeripheral, Action action, Action errorAction, Action statusAction) + { + bluetoothDeviceScript = null; + +#if UNITY_2018_3_OR_NEWER +#if UNITY_ANDROID + if (!Permission.HasUserAuthorizedPermission (Permission.FineLocation)) + Permission.RequestUserPermission (Permission.FineLocation); +#endif +#endif + + GameObject bluetoothLEReceiver = GameObject.Find("BluetoothLEReceiver"); + if (bluetoothLEReceiver == null) + bluetoothLEReceiver = new GameObject ("BluetoothLEReceiver"); + + if (bluetoothLEReceiver != null) + { + bluetoothDeviceScript = bluetoothLEReceiver.GetComponent (); + if (bluetoothDeviceScript == null) + bluetoothDeviceScript = bluetoothLEReceiver.AddComponent (); + + if (bluetoothDeviceScript != null) + { + bluetoothDeviceScript.BleStatusChangedAction = statusAction; + bluetoothDeviceScript.InitializedAction = action; + bluetoothDeviceScript.ErrorAction = errorAction; + } + } + + GameObject.DontDestroyOnLoad (bluetoothLEReceiver); + +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + ConnectUnitySendMessageCallback ((objectName, commandName, commandData) => { + string name = Marshal.PtrToStringAuto (objectName); + string command = Marshal.PtrToStringAuto (commandName); + string data = Marshal.PtrToStringAuto (commandData); + + GameObject foundObject = GameObject.Find (name); + if (foundObject != null) + foundObject.SendMessage (command, data); + }); + + BluetoothLEHardwareInterface.OSXBluetoothLEInitialize (asCentral, asPeripheral); +#else + if (Application.isEditor) + { + if (bluetoothDeviceScript != null) + bluetoothDeviceScript.SendMessage ("OnBluetoothMessage", "Initialized"); + } + else + { +#if UNITY_IOS || UNITY_TVOS + _iOSBluetoothLEInitialize (asCentral, asPeripheral); +#elif UNITY_ANDROID + if (_android == null) + { + AndroidJavaClass javaClass = new AndroidJavaClass ("com.shatalmic.unityandroidbluetoothlelib.UnityBluetoothLE"); + _android = javaClass.CallStatic ("getInstance"); + } + + if (_android != null) + _android.Call ("androidBluetoothInitialize", asCentral, asPeripheral); +#endif + } +#endif + + return bluetoothDeviceScript; + } + + public static void DeInitialize (Action action) + { + if (bluetoothDeviceScript != null) + bluetoothDeviceScript.DeinitializedAction = action; + +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + BluetoothLEHardwareInterface.OSXBluetoothLEDeInitialize (); +#else + if (Application.isEditor) + { + if (bluetoothDeviceScript != null) + bluetoothDeviceScript.SendMessage ("OnBluetoothMessage", "DeInitialized"); + } + else + { +#if UNITY_IOS || UNITY_TVOS + _iOSBluetoothLEDeInitialize (); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidBluetoothDeInitialize"); +#endif + } +#endif + } + + public static void FinishDeInitialize () + { + GameObject bluetoothLEReceiver = GameObject.Find("BluetoothLEReceiver"); + if (bluetoothLEReceiver != null) + GameObject.Destroy(bluetoothLEReceiver); + } + + public static void BluetoothEnable (bool enable) + { + if (!Application.isEditor) + { +#if UNITY_IOS || UNITY_TVOS + //_iOSBluetoothLELog (message); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidBluetoothEnable", enable); +#endif + } + } + + public static void BluetoothScanMode (ScanMode scanMode) + { + if (!Application.isEditor) + { +#if UNITY_IOS || UNITY_TVOS +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidBluetoothScanMode", (int)scanMode); +#endif + } + } + + public static void BluetoothConnectionPriority (ConnectionPriority connectionPriority) + { + if (!Application.isEditor) + { +#if UNITY_IOS || UNITY_TVOS +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidBluetoothConnectionPriority", (int)connectionPriority); +#endif + } + } + + public static void PauseMessages (bool isPaused) + { +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + OSXBluetoothLEPauseMessages (isPaused); +#else + if (!Application.isEditor) + { +#if UNITY_IOS || UNITY_TVOS + _iOSBluetoothLEPauseMessages (isPaused); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidBluetoothPause", isPaused); +#endif + } +#endif + } + + // scanning for beacons requires that you know the Proximity UUID + public static void ScanForBeacons (string[] proximityUUIDs, Action actionBeaconResponse) + { + if (proximityUUIDs != null && proximityUUIDs.Length >= 0) + { + if (!Application.isEditor) + { + if (bluetoothDeviceScript != null) + bluetoothDeviceScript.DiscoveredBeaconAction = actionBeaconResponse; + + string proximityUUIDsString = null; + + if (proximityUUIDs != null && proximityUUIDs.Length > 0) + { + proximityUUIDsString = ""; + + foreach (string proximityUUID in proximityUUIDs) + proximityUUIDsString += proximityUUID + "|"; + + proximityUUIDsString = proximityUUIDsString.Substring (0, proximityUUIDsString.Length - 1); + } + +#if UNITY_IOS + _iOSBluetoothLEScanForBeacons (proximityUUIDsString); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidBluetoothScanForBeacons", proximityUUIDsString); +#endif + } + } + } + + public static void RequestMtu(string name, int mtu, Action action) + { + if (bluetoothDeviceScript != null) + { + bluetoothDeviceScript.RequestMtuAction = action; + } + +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + if (mtu > 184) + mtu = 184; + OSXBluetoothLERequestMtu(name, mtu); +#elif UNITY_IOS || UNITY_TVOS + if (mtu > 180) + mtu = 180; + _iOSBluetoothLERequestMtu (name, mtu); +#elif UNITY_ANDROID + if (_android != null) + { + _android.Call ("androidBluetoothRequestMtu", name, mtu); + } +#endif + } + + public static void ScanForPeripheralsWithServices (string[] serviceUUIDs, Action action = null, Action actionAdvertisingInfo = null, bool rssiOnly = false, bool clearPeripheralList = true, int recordType = 0xFF) + { +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + if (!Application.isEditor) + { +#endif + if (bluetoothDeviceScript != null) + { + bluetoothDeviceScript.DiscoveredPeripheralAction = action; + bluetoothDeviceScript.DiscoveredPeripheralWithAdvertisingInfoAction = actionAdvertisingInfo; + + if (bluetoothDeviceScript.DiscoveredDeviceList != null) + bluetoothDeviceScript.DiscoveredDeviceList.Clear (); + } + + string serviceUUIDsString = null; + + if (serviceUUIDs != null && serviceUUIDs.Length > 0) + { + serviceUUIDsString = ""; + + foreach (string serviceUUID in serviceUUIDs) + serviceUUIDsString += serviceUUID + "|"; + + serviceUUIDsString = serviceUUIDsString.Substring (0, serviceUUIDsString.Length - 1); + } + +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + OSXBluetoothLEScanForPeripheralsWithServices (serviceUUIDsString, (actionAdvertisingInfo != null), rssiOnly, clearPeripheralList); +#elif UNITY_IOS || UNITY_TVOS + _iOSBluetoothLEScanForPeripheralsWithServices (serviceUUIDsString, (actionAdvertisingInfo != null), rssiOnly, clearPeripheralList); +#elif UNITY_ANDROID + if (_android != null) + { + if (serviceUUIDsString == null) + serviceUUIDsString = ""; + + _android.Call ("androidBluetoothScanForPeripheralsWithServices", serviceUUIDsString, rssiOnly, recordType); + } +#endif +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + } +#endif + } + + public static void RetrieveListOfPeripheralsWithServices (string[] serviceUUIDs, Action action) + { +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + if (!Application.isEditor) + { +#endif + if (bluetoothDeviceScript != null) + { + bluetoothDeviceScript.RetrievedConnectedPeripheralAction = action; + + if (bluetoothDeviceScript.DiscoveredDeviceList != null) + bluetoothDeviceScript.DiscoveredDeviceList.Clear (); + } + + string serviceUUIDsString = serviceUUIDs.Length > 0 ? "" : null; + + foreach (string serviceUUID in serviceUUIDs) + serviceUUIDsString += serviceUUID + "|"; + + // strip the last delimeter + serviceUUIDsString = serviceUUIDsString.Substring (0, serviceUUIDsString.Length - 1); + +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + OSXBluetoothLERetrieveListOfPeripheralsWithServices (serviceUUIDsString); +#elif UNITY_IOS || UNITY_TVOS + _iOSBluetoothLERetrieveListOfPeripheralsWithServices (serviceUUIDsString); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidBluetoothRetrieveListOfPeripheralsWithServices", serviceUUIDsString); +#endif +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + } +#endif + } + + public static void StopScan () + { +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + OSXBluetoothLEStopScan (); +#else + if (!Application.isEditor) + { +#if UNITY_IOS || UNITY_TVOS + _iOSBluetoothLEStopScan (); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidBluetoothStopScan"); +#endif + } +#endif + } + + public static void StopBeaconScan () + { + if (!Application.isEditor) + { +#if UNITY_IOS + _iOSBluetoothLEStopBeaconScan (); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidBluetoothStopBeaconScan"); +#endif + } + } + + public static void DisconnectAll () + { +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + OSXBluetoothLEDisconnectAll (); +#else + if (!Application.isEditor) + { +#if UNITY_IOS || UNITY_TVOS + _iOSBluetoothLEDisconnectAll (); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidBluetoothDisconnectAll"); +#endif + } +#endif + } + + public static void ConnectToPeripheral (string name, Action connectAction, Action serviceAction, Action characteristicAction, Action disconnectAction = null) + { +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + if (!Application.isEditor) + { +#endif + if (bluetoothDeviceScript != null) + { + bluetoothDeviceScript.ConnectedPeripheralAction = connectAction; + bluetoothDeviceScript.DiscoveredServiceAction = serviceAction; + bluetoothDeviceScript.DiscoveredCharacteristicAction = characteristicAction; + bluetoothDeviceScript.ConnectedDisconnectPeripheralAction = disconnectAction; + } + +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + OSXBluetoothLEConnectToPeripheral (name); +#elif UNITY_IOS || UNITY_TVOS + _iOSBluetoothLEConnectToPeripheral (name); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidBluetoothConnectToPeripheral", name); +#endif +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + } +#endif + } + + public static void DisconnectPeripheral (string name, Action action) + { +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + if (!Application.isEditor) + { +#endif + if (bluetoothDeviceScript != null) + bluetoothDeviceScript.DisconnectedPeripheralAction = action; + +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + OSXBluetoothLEDisconnectPeripheral (name); +#elif UNITY_IOS || UNITY_TVOS + _iOSBluetoothLEDisconnectPeripheral (name); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidBluetoothDisconnectPeripheral", name); +#endif +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + } +#endif + } + + public static void ReadCharacteristic (string name, string service, string characteristic, Action action) + { +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + if (!Application.isEditor) + { +#endif + if (bluetoothDeviceScript != null) + { + if (!bluetoothDeviceScript.DidUpdateCharacteristicValueAction.ContainsKey (name)) + bluetoothDeviceScript.DidUpdateCharacteristicValueAction[name] = new Dictionary>(); + +#if UNITY_IOS || UNITY_TVOS || (EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX)) + bluetoothDeviceScript.DidUpdateCharacteristicValueAction [name] [characteristic] = action; +#elif UNITY_ANDROID + bluetoothDeviceScript.DidUpdateCharacteristicValueAction [name] [FullUUID (characteristic).ToLower ()] = action; +#endif + } + +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + OSXBluetoothLEReadCharacteristic (name, service, characteristic); +#elif UNITY_IOS || UNITY_TVOS + _iOSBluetoothLEReadCharacteristic (name, service, characteristic); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidReadCharacteristic", name, service, characteristic); +#endif +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + } +#endif + } + + public static void WriteCharacteristic (string name, string service, string characteristic, byte[] data, int length, bool withResponse, Action action) + { +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + if (!Application.isEditor) + { +#endif + if (bluetoothDeviceScript != null) + bluetoothDeviceScript.DidWriteCharacteristicAction = action; + +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + OSXBluetoothLEWriteCharacteristic(name, service, characteristic, data, length, withResponse); +#elif UNITY_IOS || UNITY_TVOS + _iOSBluetoothLEWriteCharacteristic (name, service, characteristic, data, length, withResponse); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidWriteCharacteristic", name, service, characteristic, data, length, withResponse); +#endif +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + } +#endif + } + + public static void SubscribeCharacteristic (string name, string service, string characteristic, Action notificationAction, Action action) + { +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + if (!Application.isEditor) + { +#endif + if (bluetoothDeviceScript != null) + { + name = name.ToUpper (); + service = service.ToUpper (); + characteristic = characteristic.ToUpper (); + +#if UNITY_IOS || UNITY_TVOS || (EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX)) + if (!bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction.ContainsKey (name)) + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction [name] = new Dictionary> (); + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction [name] [characteristic] = notificationAction; + + if (!bluetoothDeviceScript.DidUpdateCharacteristicValueAction.ContainsKey (name)) + bluetoothDeviceScript.DidUpdateCharacteristicValueAction [name] = new Dictionary> (); + bluetoothDeviceScript.DidUpdateCharacteristicValueAction [name] [characteristic] = action; +#elif UNITY_ANDROID + if (!bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction.ContainsKey (name)) + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction [name] = new Dictionary> (); + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction [name] [FullUUID (characteristic).ToLower ()] = notificationAction; + + if (!bluetoothDeviceScript.DidUpdateCharacteristicValueAction.ContainsKey (name)) + bluetoothDeviceScript.DidUpdateCharacteristicValueAction [name] = new Dictionary> (); + bluetoothDeviceScript.DidUpdateCharacteristicValueAction [name] [FullUUID (characteristic).ToLower ()] = action; +#endif + } + +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + OSXBluetoothLESubscribeCharacteristic (name, service, characteristic); +#elif UNITY_IOS || UNITY_TVOS + _iOSBluetoothLESubscribeCharacteristic (name, service, characteristic); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidSubscribeCharacteristic", name, service, characteristic); +#endif +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + } +#endif + } + + public static void SubscribeCharacteristicWithDeviceAddress (string name, string service, string characteristic, Action notificationAction, Action action) + { +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + if (!Application.isEditor) + { +#endif + if (bluetoothDeviceScript != null) + { + name = name.ToUpper (); + service = service.ToUpper (); + characteristic = characteristic.ToUpper (); + +#if UNITY_IOS || UNITY_TVOS || (EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX)) + if (!bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction.ContainsKey (name)) + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction[name] = new Dictionary>(); + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction[name][characteristic] = notificationAction; + + if (!bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction.ContainsKey (name)) + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction[name] = new Dictionary>(); + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction[name][characteristic] = null; + + if (!bluetoothDeviceScript.DidUpdateCharacteristicValueWithDeviceAddressAction.ContainsKey (name)) + bluetoothDeviceScript.DidUpdateCharacteristicValueWithDeviceAddressAction[name] = new Dictionary>(); + bluetoothDeviceScript.DidUpdateCharacteristicValueWithDeviceAddressAction[name][characteristic] = action; +#elif UNITY_ANDROID + if (!bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction.ContainsKey (name)) + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction[name] = new Dictionary>(); + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction[name][FullUUID (characteristic).ToLower ()] = notificationAction; + + if (!bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction.ContainsKey(name)) + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction[name] = new Dictionary>(); + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction[name][FullUUID (characteristic).ToLower ()] = null; + + if (!bluetoothDeviceScript.DidUpdateCharacteristicValueWithDeviceAddressAction.ContainsKey (name)) + bluetoothDeviceScript.DidUpdateCharacteristicValueWithDeviceAddressAction[name] = new Dictionary>(); + bluetoothDeviceScript.DidUpdateCharacteristicValueWithDeviceAddressAction[name][FullUUID (characteristic).ToLower ()] = action; +#endif + } + +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + OSXBluetoothLESubscribeCharacteristic (name, service, characteristic); +#elif UNITY_IOS || UNITY_TVOS + _iOSBluetoothLESubscribeCharacteristic (name, service, characteristic); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidSubscribeCharacteristic", name, service, characteristic); +#endif +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + } +#endif + } + + public static void UnSubscribeCharacteristic (string name, string service, string characteristic, Action action) + { +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + if (!Application.isEditor) + { +#endif + if (bluetoothDeviceScript != null) + { + name = name.ToUpper (); + service = service.ToUpper (); + characteristic = characteristic.ToUpper (); + +#if UNITY_IOS || UNITY_TVOS || (EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX)) + if (!bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction.ContainsKey (name)) + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction[name] = new Dictionary>(); + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction[name][characteristic] = null; + + if (!bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction.ContainsKey (name)) + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction[name] = new Dictionary> (); + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction[name][characteristic] = action; +#elif UNITY_ANDROID + if (!bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction.ContainsKey (name)) + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction[name] = new Dictionary>(); + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction[name][FullUUID (characteristic).ToLower ()] = null; + + if (!bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction.ContainsKey (name)) + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction[name] = new Dictionary> (); + bluetoothDeviceScript.DidUpdateNotificationStateForCharacteristicAction[name][FullUUID (characteristic).ToLower ()] = action; +#endif + } + +#if EXPERIMENTAL_MACOS_EDITOR && (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) + OSXBluetoothLEUnSubscribeCharacteristic (name, service, characteristic); +#elif UNITY_IOS || UNITY_TVOS + _iOSBluetoothLEUnSubscribeCharacteristic (name, service, characteristic); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidUnsubscribeCharacteristic", name, service, characteristic); +#endif +#if !UNITY_EDITOR_OSX || !EXPERIMENTAL_MACOS_EDITOR + } +#endif + } + + public static void PeripheralName (string newName) + { + if (!Application.isEditor) + { +#if UNITY_IOS + _iOSBluetoothLEPeripheralName (newName); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidPeripheralName", newName); +#endif + } + } + + public static void CreateService (string uuid, bool primary, Action action) + { + if (!Application.isEditor) + { + if (bluetoothDeviceScript != null) + bluetoothDeviceScript.ServiceAddedAction = action; + +#if UNITY_IOS + _iOSBluetoothLECreateService (uuid, primary); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidCreateService", uuid, primary); +#endif + } + } + + public static void RemoveService (string uuid) + { + if (!Application.isEditor) + { +#if UNITY_IOS + _iOSBluetoothLERemoveService (uuid); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidRemoveService", uuid); +#endif + } + } + + public static void RemoveServices () + { + if (!Application.isEditor) + { +#if UNITY_IOS + _iOSBluetoothLERemoveServices (); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidRemoveServices"); +#endif + } + } + + public static void CreateCharacteristic (string uuid, CBCharacteristicProperties properties, CBAttributePermissions permissions, byte[] data, int length, Action action) + { + if (!Application.isEditor) + { + if (bluetoothDeviceScript != null) + bluetoothDeviceScript.PeripheralReceivedWriteDataAction = action; + +#if UNITY_IOS + _iOSBluetoothLECreateCharacteristic (uuid, (int)properties, (int)permissions, data, length); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidCreateCharacteristic", uuid, (int)properties, (int)permissions, data, length); +#endif + } + } + + public static void RemoveCharacteristic (string uuid) + { + if (!Application.isEditor) + { + if (bluetoothDeviceScript != null) + bluetoothDeviceScript.PeripheralReceivedWriteDataAction = null; + +#if UNITY_IOS + _iOSBluetoothLERemoveCharacteristic (uuid); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidRemoveCharacteristic", uuid); +#endif + } + } + + public static void RemoveCharacteristics () + { + if (!Application.isEditor) + { +#if UNITY_IOS + _iOSBluetoothLERemoveCharacteristics (); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidRemoveCharacteristics"); +#endif + } + } + + public static void StartAdvertising (Action action) + { + if (!Application.isEditor) + { + if (bluetoothDeviceScript != null) + bluetoothDeviceScript.StartedAdvertisingAction = action; + +#if UNITY_IOS + _iOSBluetoothLEStartAdvertising (); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidStartAdvertising"); +#endif + } + } + + public static void StopAdvertising (Action action) + { + if (!Application.isEditor) + { + if (bluetoothDeviceScript != null) + bluetoothDeviceScript.StoppedAdvertisingAction = action; + +#if UNITY_IOS + _iOSBluetoothLEStopAdvertising (); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidStopAdvertising"); +#endif + } + } + + public static void UpdateCharacteristicValue (string uuid, byte[] data, int length) + { + if (!Application.isEditor) + { +#if UNITY_IOS + _iOSBluetoothLEUpdateCharacteristicValue (uuid, data, length); +#elif UNITY_ANDROID + if (_android != null) + _android.Call ("androidUpdateCharacteristicValue", uuid, data, length); +#endif + } + } + + public static string FullUUID (string uuid) + { + if (uuid.Length == 4) + return "0000" + uuid + "-0000-1000-8000-00805F9B34FB"; + return uuid; + } +} diff --git a/Assets/Plugins/BluetoothHardwareInterface.cs.meta b/Assets/Plugins/BluetoothHardwareInterface.cs.meta new file mode 100644 index 00000000..f3b5a0a6 --- /dev/null +++ b/Assets/Plugins/BluetoothHardwareInterface.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b8496a9b1a1df40af9ada2311d1d6d09 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Plugins/BluetoothLEOSX.bundle.meta b/Assets/Plugins/BluetoothLEOSX.bundle.meta new file mode 100644 index 00000000..f7075f6e --- /dev/null +++ b/Assets/Plugins/BluetoothLEOSX.bundle.meta @@ -0,0 +1,43 @@ +fileFormatVersion: 2 +guid: 5383f7d08256547f6b36ee834b840062 +folderAsset: yes +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + DefaultValueInitialized: true + - first: + Standalone: OSXIntel + second: + enabled: 1 + settings: {} + - first: + Standalone: OSXIntel64 + second: + enabled: 1 + settings: {} + - first: + Standalone: OSXUniversal + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/BluetoothLEOSX.bundle/Contents.meta b/Assets/Plugins/BluetoothLEOSX.bundle/Contents.meta new file mode 100644 index 00000000..93308905 --- /dev/null +++ b/Assets/Plugins/BluetoothLEOSX.bundle/Contents.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 34c7c97d38c834839be439bcf96a0267 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/BluetoothLEOSX.bundle/Contents/Info.plist b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/Info.plist new file mode 100644 index 00000000..f1fbd9b3 --- /dev/null +++ b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/Info.plist @@ -0,0 +1,48 @@ + + + + + BuildMachineOSBuild + 19F101 + CFBundleDevelopmentRegion + en + CFBundleExecutable + BluetoothLEOSX + CFBundleIdentifier + com.shatalmic.BluetoothLEOSX + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + BluetoothLEOSX + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleSupportedPlatforms + + MacOSX + + CFBundleVersion + 1 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 11E608c + DTPlatformVersion + GM + DTSDKBuild + 19E258 + DTSDKName + macosx10.15 + DTXcode + 1150 + DTXcodeBuild + 11E608c + LSMinimumSystemVersion + 10.11 + NSHumanReadableCopyright + Copyright © 2016 Shatalmic. All rights reserved. + + diff --git a/Assets/Plugins/BluetoothLEOSX.bundle/Contents/Info.plist.meta b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/Info.plist.meta new file mode 100644 index 00000000..50ad560e --- /dev/null +++ b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/Info.plist.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8e7811284d1734fc2ba4b29fccd3ccf7 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/BluetoothLEOSX.bundle/Contents/MacOS.meta b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/MacOS.meta new file mode 100644 index 00000000..bea35c1b --- /dev/null +++ b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/MacOS.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4e9d68945c9234df3b7855f4e1006f0f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/BluetoothLEOSX.bundle/Contents/MacOS/BluetoothLEOSX b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/MacOS/BluetoothLEOSX new file mode 100644 index 00000000..e15d6a5b Binary files /dev/null and b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/MacOS/BluetoothLEOSX differ diff --git a/Assets/Plugins/BluetoothLEOSX.bundle/Contents/MacOS/BluetoothLEOSX.meta b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/MacOS/BluetoothLEOSX.meta new file mode 100644 index 00000000..34e31722 --- /dev/null +++ b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/MacOS/BluetoothLEOSX.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1a83baeb98e0141f587d978b89ecd37d +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/BluetoothLEOSX.bundle/Contents/_CodeSignature.meta b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/_CodeSignature.meta new file mode 100644 index 00000000..719d57c0 --- /dev/null +++ b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/_CodeSignature.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 673293518de2c4fbf913749ebdb36dd1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/BluetoothLEOSX.bundle/Contents/_CodeSignature/CodeResources b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/_CodeSignature/CodeResources new file mode 100644 index 00000000..d5d0fd74 --- /dev/null +++ b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/_CodeSignature/CodeResources @@ -0,0 +1,115 @@ + + + + + files + + files2 + + rules + + ^Resources/ + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/ + + nested + + weight + 10 + + ^.* + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^Resources/ + + weight + 20 + + ^Resources/.*\.lproj/ + + optional + + weight + 1000 + + ^Resources/.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Resources/Base\.lproj/ + + weight + 1010 + + ^[^/]+$ + + nested + + weight + 10 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/Assets/Plugins/BluetoothLEOSX.bundle/Contents/_CodeSignature/CodeResources.meta b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/_CodeSignature/CodeResources.meta new file mode 100644 index 00000000..44cf6eb6 --- /dev/null +++ b/Assets/Plugins/BluetoothLEOSX.bundle/Contents/_CodeSignature/CodeResources.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2b362acd4e35f4bb380341cd3df2b9c0 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS.meta b/Assets/Plugins/iOS.meta index 104ce4a9..b120dcb2 100644 --- a/Assets/Plugins/iOS.meta +++ b/Assets/Plugins/iOS.meta @@ -1,8 +1,5 @@ fileFormatVersion: 2 -guid: 7cce98d172bfd2f4fa235200a8c37ffd +guid: 8d3303514acc04853a1fbd8393630e00 folderAsset: yes DefaultImporter: - externalObjects: {} userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/Plugins/iOS/UnityBluetoothLE.h b/Assets/Plugins/iOS/UnityBluetoothLE.h new file mode 100644 index 00000000..ce91cfc0 --- /dev/null +++ b/Assets/Plugins/iOS/UnityBluetoothLE.h @@ -0,0 +1,107 @@ +// +// UnityBluetoothLE.h +// Unity-iPhone +// +// Created by Tony Pitman on 03/05/2014. +// +// + +#import +#import + +#if !TARGET_OS_TV +#import + +@interface UnityBluetoothLE : NSObject +#else +@interface UnityBluetoothLE : NSObject +#endif + +{ + CBCentralManager *_centralManager; +#if !TARGET_OS_TV + CLLocationManager *_locationManager; +#endif + NSMutableDictionary *_peripherals; + +#if !TARGET_OS_TV + CBPeripheralManager *_peripheralManager; + + NSString *_peripheralName; + + NSMutableDictionary *_services; + NSMutableDictionary *_characteristics; +#endif + + NSMutableArray *_backgroundMessages; + BOOL _isPaused; + BOOL _alreadyNotified; + BOOL _isInitializing; + BOOL _rssiOnly; + int _recordType; + + long _mtu; + + unsigned char *_writeCharacteristicBytes; + long _writeCharacteristicLength; + long _writeCharacteristicPosition; + long _writeCharacteristicBytesToWrite; + CBCharacteristicWriteType _writeCharacteristicWithResponse; + int _writeCharacteristicRetries; +} + +@property (atomic, strong) NSMutableDictionary *_peripherals; +@property (atomic) BOOL _rssiOnly; + +- (void)initialize:(BOOL)asCentral asPeripheral:(BOOL)asPeripheral; +- (void)deInitialize; +- (void)scanForPeripheralsWithServices:(NSArray *)serviceUUIDs options:(NSDictionary *)options clearPeripheralList:(BOOL)clearPeripheralList recordType:(int)recordType; +- (void)stopScan; +- (void)retrieveListOfPeripheralsWithServices:(NSArray *)serviceUUIDs; +- (void)connectToPeripheral:(NSString *)name; +- (void)disconnectPeripheral:(NSString *)name; +- (CBCharacteristic *)getCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString; +- (void)readCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString; +- (void)writeCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString data:(NSData *)data withResponse:(BOOL)withResponse; +- (void)subscribeCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString; +- (void)unsubscribeCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString; +- (void)writeCharactersticBytesReset; +- (void)writeCharactersticBytes:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic data:(NSData *)data withResponse:(CBCharacteristicWriteType)withResponse; +- (void)writeNextPacket:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic; + +#if !TARGET_OS_TV +- (void)requestMtu:(NSString *)name mtu:(int)mtu; +- (void)scanForBeacons:(NSArray *)beaconRegions; +- (void)stopBeaconScan; + +- (void)peripheralName:(NSString *)newName; +- (void)createService:(NSString *)uuid primary:(BOOL)primary; +- (void)removeService:(NSString *)uuid; +- (void)removeServices; +- (void)createCharacteristic:(NSString *)uuid properties:(CBCharacteristicProperties)properties permissions:(CBAttributePermissions)permissions value:(NSData *)value; +- (void)removeCharacteristic:(NSString *)uuid; +- (void)removeCharacteristics; +- (void)startAdvertising; +- (void)stopAdvertising; +- (void)updateCharacteristicValue:(NSString *)uuid value:(NSData *)value; +#endif + +- (void)pauseMessages:(BOOL)isPaused; +- (void)sendUnityMessage:(BOOL)isString message:(NSString *)message; + ++ (NSString *) base64StringFromData:(NSData *)data length:(int)length; + +@end + +@interface UnityMessage : NSObject + +{ + BOOL _isString; + NSString *_message; +} + +- (void)initialize:(BOOL)isString message:(NSString *)message; +- (void)deInitialize; +- (void)sendUnityMessage; + +@end diff --git a/Assets/Plugins/iOS/UnityBluetoothLE.h.meta b/Assets/Plugins/iOS/UnityBluetoothLE.h.meta new file mode 100644 index 00000000..4bab6c82 --- /dev/null +++ b/Assets/Plugins/iOS/UnityBluetoothLE.h.meta @@ -0,0 +1,95 @@ +fileFormatVersion: 2 +guid: 89fad22a839074ac08f9f7ffc9dbce4e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + '': Linux + second: + enabled: 0 + settings: + CPU: x86 + - first: + '': OSXIntel + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + '': OSXIntel64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Facebook: Win + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Facebook: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 1 + settings: + CompileFlags: + FrameworkDependencies: + - first: + tvOS: tvOS + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/iOS/UnityBluetoothLE.mm b/Assets/Plugins/iOS/UnityBluetoothLE.mm new file mode 100644 index 00000000..7691553a --- /dev/null +++ b/Assets/Plugins/iOS/UnityBluetoothLE.mm @@ -0,0 +1,1316 @@ +// +// UnityBluetoothLE.h +// Unity-iPhone +// +// Created by Tony Pitman on 03/05/2014. +// +// + +#import "UnityBluetoothLE.h" + +const char _messageDelimeter = '~'; + +extern "C" { + + UnityBluetoothLE *_unityBluetoothLE = nil; + + void _iOSBluetoothLELogString (NSString *message) { + + NSLog (message); + } + + void _iOSBluetoothLELog (char *message) { + + _iOSBluetoothLELogString ([NSString stringWithFormat:@"%s", message]); + } + + void _iOSBluetoothLEInitialize (BOOL asCentral, BOOL asPeripheral) { + + _unityBluetoothLE = [UnityBluetoothLE new]; + [_unityBluetoothLE initialize:asCentral asPeripheral:asPeripheral]; + } + + void _iOSBluetoothLEDeInitialize () { + + if (_unityBluetoothLE != nil) { + + [_unityBluetoothLE deInitialize]; + [_unityBluetoothLE release]; + _unityBluetoothLE = nil; + + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", "DeInitialized"); + } + } + + void _iOSBluetoothLEPauseMessages (BOOL pause) { + + if (_unityBluetoothLE != nil) + [_unityBluetoothLE pauseMessages:pause]; + } + + void _iOSBluetoothLEScanForPeripheralsWithServices (char *serviceUUIDsStringRaw, bool allowDuplicates, bool rssiOnly, bool clearPeripheralList, int recordType) { + + if (_unityBluetoothLE != nil) + { + _unityBluetoothLE._rssiOnly = rssiOnly; + + NSMutableArray *actualUUIDs = nil; + + if (serviceUUIDsStringRaw != nil) + { + NSString *serviceUUIDsString = [NSString stringWithFormat:@"%s", serviceUUIDsStringRaw]; + NSArray *serviceUUIDs = [serviceUUIDsString componentsSeparatedByString:@"|"]; + + if (serviceUUIDs.count > 0) + { + actualUUIDs = [[NSMutableArray alloc] init]; + + for (NSString* sUUID in serviceUUIDs) + [actualUUIDs addObject:[CBUUID UUIDWithString:sUUID]]; + } + } + + NSDictionary *options = nil; + if (allowDuplicates) + options = @{ CBCentralManagerScanOptionAllowDuplicatesKey: @YES }; + + [_unityBluetoothLE scanForPeripheralsWithServices:actualUUIDs options:options clearPeripheralList:clearPeripheralList recordType:recordType]; + } + } + + void _iOSBluetoothLEStopScan () { + + if (_unityBluetoothLE != nil) + [_unityBluetoothLE stopScan]; + } + + void _iOSBluetoothLERetrieveListOfPeripheralsWithServices (char *serviceUUIDsStringRaw) { + + if (_unityBluetoothLE != nil) + { + NSMutableArray *actualUUIDs = nil; + + if (serviceUUIDsStringRaw != nil) + { + NSString *serviceUUIDsString = [NSString stringWithFormat:@"%s", serviceUUIDsStringRaw]; + NSArray *serviceUUIDs = [serviceUUIDsString componentsSeparatedByString:@"|"]; + + if (serviceUUIDs.count > 0) + { + actualUUIDs = [[NSMutableArray alloc] init]; + + for (NSString* sUUID in serviceUUIDs) + [actualUUIDs addObject:[CBUUID UUIDWithString:sUUID]]; + } + } + + [_unityBluetoothLE retrieveListOfPeripheralsWithServices:actualUUIDs]; + } + } + + void _iOSBluetoothLEConnectToPeripheral (char *name) { + + if (_unityBluetoothLE && name != nil) + [_unityBluetoothLE connectToPeripheral:[NSString stringWithFormat:@"%s", name]]; + } + + void _iOSBluetoothLEDisconnectPeripheral (char *name) { + + if (_unityBluetoothLE && name != nil) + [_unityBluetoothLE disconnectPeripheral:[NSString stringWithFormat:@"%s", name]]; + } + + void _iOSBluetoothLEReadCharacteristic (char *name, char *service, char *characteristic) { + + if (_unityBluetoothLE && name != nil && service != nil && characteristic != nil) + [_unityBluetoothLE readCharacteristic:[NSString stringWithFormat:@"%s", name] service:[NSString stringWithFormat:@"%s", service] characteristic:[NSString stringWithFormat:@"%s", characteristic]]; + } + + void _iOSBluetoothLEWriteCharacteristic (char *name, char *service, char *characteristic, unsigned char *data, int length, BOOL withResponse) { + + if (_unityBluetoothLE && name != nil && service != nil && characteristic != nil && data != nil && length > 0) + [_unityBluetoothLE writeCharacteristic:[NSString stringWithFormat:@"%s", name] service:[NSString stringWithFormat:@"%s", service] characteristic:[NSString stringWithFormat:@"%s", characteristic] data:[NSData dataWithBytes:data length:length] withResponse:withResponse]; + } + + void _iOSBluetoothLESubscribeCharacteristic (char *name, char *service, char *characteristic) { + + if (_unityBluetoothLE && name != nil && service != nil && characteristic != nil) + [_unityBluetoothLE subscribeCharacteristic:[NSString stringWithFormat:@"%s", name] service:[NSString stringWithFormat:@"%s", service] characteristic:[NSString stringWithFormat:@"%s", characteristic]]; + } + + void _iOSBluetoothLEUnSubscribeCharacteristic (char *name, char *service, char *characteristic) { + + if (_unityBluetoothLE && name != nil && service != nil && characteristic != nil) + [_unityBluetoothLE unsubscribeCharacteristic:[NSString stringWithFormat:@"%s", name] service:[NSString stringWithFormat:@"%s", service] characteristic:[NSString stringWithFormat:@"%s", characteristic]]; + } + + void _iOSBluetoothLEDisconnectAll () { + + if (_unityBluetoothLE != nil) + [_unityBluetoothLE disconnectAll]; + } + +#if !TARGET_OS_TV + void _iOSBluetoothLERequestMtu (char *name, int mtu) { + + if (_unityBluetoothLE != nil) + [_unityBluetoothLE requestMtu:[NSString stringWithFormat:@"%s", name] mtu:mtu]; + } + + void _iOSBluetoothLEScanForBeacons (char *proximityUUIDsStringRaw) { + + if (_unityBluetoothLE != nil) + { + NSMutableArray *actualUUIDs = nil; + + if (proximityUUIDsStringRaw != nil) + { + NSString *proximityUUIDsString = [NSString stringWithFormat:@"%s", proximityUUIDsStringRaw]; + NSArray *proximityUUIDs = [proximityUUIDsString componentsSeparatedByString:@"|"]; + + if (proximityUUIDs.count > 0) + { + NSMutableArray *beaconRegions = [[NSMutableArray alloc] init]; + + for (NSString* sUUID in proximityUUIDs) + { + NSArray *parts = [sUUID componentsSeparatedByString:@":"]; + if (parts.count == 2) + { + CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:[[NSUUID alloc] initWithUUIDString:parts[0]] identifier:parts[1]]; + [beaconRegions addObject:beaconRegion]; + + [_unityBluetoothLE scanForBeacons:beaconRegions]; + } + else + { + NSString *message = [NSString stringWithFormat:@"Error~iBeacon Scanning missing identifiers"]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } + } + } + else + { + NSString *message = [NSString stringWithFormat:@"Error~iBeacon Scanning requires proximity UUIDs"]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } + } + else + { + NSString *message = [NSString stringWithFormat:@"Error~iBeacon Scanning requires proximity UUIDs"]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } + } + } + + void _iOSBluetoothLEStopBeaconScan () { + + if (_unityBluetoothLE != nil) + [_unityBluetoothLE stopBeaconScan]; + } + + void _iOSBluetoothLEPeripheralName (char *newName) { + + if (_unityBluetoothLE != nil && newName != nil) + [_unityBluetoothLE peripheralName:[[NSString alloc] initWithUTF8String:newName]]; + } + + void _iOSBluetoothLECreateService (char *uuid, BOOL primary) { + + if (_unityBluetoothLE != nil) + [_unityBluetoothLE createService:[NSString stringWithFormat:@"%s", uuid] primary:primary]; + } + + void _iOSBluetoothLERemoveService (char *uuid) { + + if (_unityBluetoothLE != nil) + [_unityBluetoothLE removeService:[NSString stringWithFormat:@"%s", uuid]]; + } + + void _iOSBluetoothLERemoveServices () { + + if (_unityBluetoothLE != nil) + [_unityBluetoothLE removeServices]; + } + + void _iOSBluetoothLECreateCharacteristic (char *uuid, int properties, int permissions, unsigned char *data, int length) { + + if (_unityBluetoothLE != nil) { + + NSData *value = nil; + if (data != nil) + value = [[NSData alloc] initWithBytes:data length:length]; + + [_unityBluetoothLE createCharacteristic:[NSString stringWithFormat:@"%s", uuid] properties:properties permissions:permissions value:value]; + } + } + + void _iOSBluetoothLERemoveCharacteristic (char *uuid) { + + if (_unityBluetoothLE != nil) + [_unityBluetoothLE removeCharacteristic:[NSString stringWithFormat:@"%s", uuid]]; + } + + void _iOSBluetoothLERemoveCharacteristics () { + + if (_unityBluetoothLE != nil) + [_unityBluetoothLE removeCharacteristics]; + } + + void _iOSBluetoothLEStartAdvertising () { + + if (_unityBluetoothLE != nil) + [_unityBluetoothLE startAdvertising]; + } + + void _iOSBluetoothLEStopAdvertising () { + + if (_unityBluetoothLE != nil) + [_unityBluetoothLE stopAdvertising]; + } + + void _iOSBluetoothLEUpdateCharacteristicValue (char *uuid, unsigned char *data, int length) { + + if (_unityBluetoothLE != nil) { + + NSData *value = nil; + if (data != nil) + value = [[NSData alloc] initWithBytes:data length:length]; + + [_unityBluetoothLE updateCharacteristicValue:[NSString stringWithFormat:@"%s", uuid] value:value]; + } + } +#endif +} + +@implementation UnityBluetoothLE + +@synthesize _peripherals; +@synthesize _rssiOnly; + +- (void)initialize:(BOOL)asCentral asPeripheral:(BOOL)asPeripheral +{ + _mtu = 20; + + _isPaused = FALSE; + _isInitializing = TRUE; + + _centralManager = nil; +#if !TARGET_OS_TV + _peripheralManager = nil; + _services = nil; + _characteristics = nil; +#endif + if (asCentral) + _centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; + +#if !TARGET_OS_TV + if (asPeripheral) + _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil]; + + _services = [[NSMutableDictionary alloc] init]; + _characteristics = [[NSMutableDictionary alloc] init]; +#endif + + _peripherals = [[NSMutableDictionary alloc] init]; +} + +- (void)deInitialize +{ + if (_backgroundMessages != nil) + { + for (UnityMessage *message in _backgroundMessages) + { + if (message != nil) + { + [message deInitialize]; + [message release]; + } + } + + [_backgroundMessages release]; + _backgroundMessages = nil; + } + +#if !TARGET_OS_TV + if (_peripheralManager != nil) + [self stopAdvertising]; + + [self removeCharacteristics]; + [self removeServices]; +#endif + + if (_centralManager != nil) + [self stopScan]; + + [_peripherals removeAllObjects]; +} + +- (void)pauseMessages:(BOOL)isPaused +{ + if (isPaused != _isPaused) { + + if (_backgroundMessages == nil) + _backgroundMessages = [[NSMutableArray alloc] init]; + + _isPaused = isPaused; + + // if we are not paused now since we know we changed state + // that means we were paused so we need to pump the saved + // messages to Unity + if (isPaused) { + + if (_backgroundMessages != nil) { + + for (UnityMessage *message in _backgroundMessages) { + + if (message != nil) { + + [message sendUnityMessage]; + [message deInitialize]; + [message release]; + } + } + + [_backgroundMessages removeAllObjects]; + } + } + } +} + +#if !TARGET_OS_TV +- (void)createService:(NSString *)uuid primary:(BOOL)primary +{ + CBUUID *cbuuid = [CBUUID UUIDWithString:uuid]; + CBMutableService *service = [[CBMutableService alloc] initWithType:cbuuid primary:primary]; + + NSMutableArray *characteristics = [[NSMutableArray alloc] init]; + + NSEnumerator *enumerator = [_characteristics keyEnumerator]; + id key; + while ((key = [enumerator nextObject])) + [characteristics addObject:[_characteristics objectForKey:key]]; + + service.characteristics = characteristics; + + [_services setObject:service forKey:cbuuid]; + + if (_peripheralManager != nil) + { + [_peripheralManager addService:service]; + } +} + +- (void)removeService:(NSString *)uuid +{ + if (_services != nil) + { + if (_peripheralManager != nil) + { + CBMutableService *service = [_services objectForKey:uuid]; + if (service != nil) + [_peripheralManager removeService:service]; + } + + [_services removeObjectForKey:uuid]; + } +} + +- (void)removeServices +{ + if (_services != nil) + { + [_services removeAllObjects]; + + if (_peripheralManager != nil) + [_peripheralManager removeAllServices]; + } +} + +- (void)peripheralName:(NSString *)newName +{ + _peripheralName = newName; +} + +- (void)createCharacteristic:(NSString *)uuid properties:(CBCharacteristicProperties)properties permissions:(CBAttributePermissions)permissions value:(NSData *)value +{ + CBUUID *cbuuid = [CBUUID UUIDWithString:uuid]; + CBCharacteristic *characteristic = [[CBMutableCharacteristic alloc] initWithType:cbuuid properties:properties value:value permissions:permissions]; + + [_characteristics setObject:characteristic forKey:cbuuid]; +} + +- (void)removeCharacteristic:(NSString *)uuid +{ + if (_characteristics != nil) + [_characteristics removeObjectForKey:uuid]; +} + +- (void)removeCharacteristics +{ + if (_characteristics != nil) + [_characteristics removeAllObjects]; +} + +- (void)startAdvertising +{ + if (_peripheralManager != nil && _services != nil) + { + NSMutableArray *services = [[NSMutableArray alloc] init]; + + NSEnumerator *enumerator = [_services keyEnumerator]; + id key; + while ((key = [enumerator nextObject])) + { + CBMutableService *service = [_services objectForKey:key]; + [services addObject:service.UUID]; + } + + if (_peripheralName == nil) + _peripheralName = @""; + + [_peripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey : services, CBAdvertisementDataLocalNameKey : _peripheralName }]; + } +} + +- (void)stopAdvertising +{ + if (_peripheralManager != nil) + { + [_peripheralManager stopAdvertising]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", "StoppedAdvertising"); + } +} + +- (void)updateCharacteristicValue:(NSString *)uuid value:(NSData *)value +{ + if (_characteristics != nil) + { + CBUUID *cbuuid = [CBUUID UUIDWithString:uuid]; + CBMutableCharacteristic *characteristic = [_characteristics objectForKey:cbuuid]; + if (characteristic != nil) + { + characteristic.value = value; + if (_peripheralManager != nil) + [_peripheralManager updateValue:value forCharacteristic:characteristic onSubscribedCentrals:nil]; + } + } +} + +- (void)requestMtu:(NSString *)name mtu:(int)mtu +{ + _mtu = mtu; + + NSString *message = [NSString stringWithFormat:@"MtuChanged~%@~%d", name, _mtu]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]); +} + +- (void)scanForBeacons:(NSArray*)beaconRegions +{ + if ([CLLocationManager isRangingAvailable]) + { + _locationManager = [CLLocationManager new]; + _locationManager.delegate = self; + + [_locationManager requestWhenInUseAuthorization]; + + for (CLBeaconRegion *region in beaconRegions) + [_locationManager startRangingBeaconsInRegion:region]; + } + else + { + NSString *message = [NSString stringWithFormat:@"Error~iBeacon Ranging is not available"]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } +} + +- (void) stopBeaconScan +{ + if (_locationManager != nil) + { + for (CLBeaconRegion *region in _locationManager.rangedRegions) + [_locationManager stopRangingBeaconsInRegion:region]; + + [_locationManager release]; + _locationManager = nil; + } +} + +// beacon delegate implementation +- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region +{ + NSString *message; + for (CLBeacon *beacon in beacons) + { + message = [NSString stringWithFormat:@"DiscoveredBeacon~%@~%ld~%ld~%ld~%ld~%ld", [beacon.proximityUUID.UUIDString stringByReplacingOccurrencesOfString:@"-" withString:@""], beacon.major.longValue, beacon.minor.longValue, (long)beacon.rssi, (long)0, (long)beacon.proximity]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]); + } +} +#endif + +// central delegate implementation +- (void)scanForPeripheralsWithServices:(NSArray *)serviceUUIDs options:(NSDictionary *)options clearPeripheralList:(BOOL)clearPeripheralList recordType:(int)recordType +{ + if (_centralManager != nil) + { + recordType = recordType; + if (clearPeripheralList && _peripherals != nil) + [_peripherals removeAllObjects]; + + [_centralManager scanForPeripheralsWithServices:serviceUUIDs options:options]; + } +} + +- (void) stopScan +{ + if (_centralManager != nil) + [_centralManager stopScan]; +} + +- (void)retrieveListOfPeripheralsWithServices:(NSArray *)serviceUUIDs +{ + if (_centralManager != nil) + { + if (_peripherals != nil) + [_peripherals removeAllObjects]; + + NSArray * list = [_centralManager retrieveConnectedPeripheralsWithServices:serviceUUIDs]; + if (list != nil) + { + for (int i = 0; i < list.count; ++i) + { + CBPeripheral *peripheral = [list objectAtIndex:i]; + if (peripheral != nil) + { + NSString *identifier = [[peripheral identifier] UUIDString]; + NSString *name = [peripheral name]; + + NSString *message = [NSString stringWithFormat:@"RetrievedConnectedPeripheral~%@~%@", identifier, name]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]); + + [_peripherals setObject:peripheral forKey:identifier]; + } + } + } + } +} + +- (void)connectToPeripheral:(NSString *)name +{ + if (_peripherals != nil && name != nil) + { + CBPeripheral *peripheral = [_peripherals objectForKey:name]; + if (peripheral != nil) + [_centralManager connectPeripheral:peripheral options:nil]; + } +} + +- (void)disconnectAll +{ + if (_peripherals != nil && [_peripherals count] > 0) + { + NSArray* keys = [_peripherals allKeys]; + for(NSString* key in keys) + { + CBPeripheral *peripheral = [_peripherals objectForKey:key]; + if (peripheral != nil) + [_centralManager cancelPeripheralConnection:peripheral]; + } + } +} + +- (void)disconnectPeripheral:(NSString *)name +{ + if (_peripherals != nil && name != nil) + { + CBPeripheral *peripheral = [_peripherals objectForKey:name]; + if (peripheral != nil) + { + [_centralManager cancelPeripheralConnection:peripheral]; + } + } +} + +- (CBCharacteristic *)getCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString +{ + CBCharacteristic *returnCharacteristic = nil; + + if (name != nil && serviceString != nil && characteristicString != nil && _peripherals != nil) + { + CBPeripheral *peripheral = [_peripherals objectForKey:name]; + if (peripheral != nil) + { + CBUUID *serviceUUID = [CBUUID UUIDWithString:serviceString]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:characteristicString]; + + for (CBService *service in peripheral.services) + { + if ([service.UUID isEqual:serviceUUID]) + { + for (CBCharacteristic *characteristic in service.characteristics) + { + if ([characteristic.UUID isEqual:characteristicUUID]) + { + returnCharacteristic = characteristic; + } + } + } + } + } + } + + return returnCharacteristic; +} + +- (void)readCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString +{ + if (name != nil && serviceString != nil && characteristicString != nil && _peripherals != nil) + { + CBPeripheral *peripheral = [_peripherals objectForKey:name]; + if (peripheral != nil) + { + CBCharacteristic *characteristic = [_unityBluetoothLE getCharacteristic:name service:serviceString characteristic:characteristicString]; + if (characteristic != nil) + [peripheral readValueForCharacteristic:characteristic]; + } + } +} + +- (void)writeCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString data:(NSData *)data withResponse:(BOOL)withResponse +{ + if (name != nil && serviceString != nil && characteristicString != nil && _peripherals != nil && data != nil) + { + CBPeripheral *peripheral = [_peripherals objectForKey:name]; + if (peripheral != nil) + { + CBCharacteristic *characteristic = [_unityBluetoothLE getCharacteristic:name service:serviceString characteristic:characteristicString]; + if (characteristic != nil) + { + CBCharacteristicWriteType type = CBCharacteristicWriteWithoutResponse; + if (withResponse) + type = CBCharacteristicWriteWithResponse; + + [self writeCharactersticBytes:peripheral characteristic:characteristic data:data withResponse:type]; + } + } + } +} + +- (void)subscribeCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString +{ + if (name != nil && serviceString != nil && characteristicString != nil && _peripherals != nil) + { + CBPeripheral *peripheral = [_peripherals objectForKey:name]; + if (peripheral != nil) + { + CBCharacteristic *characteristic = [_unityBluetoothLE getCharacteristic:name service:serviceString characteristic:characteristicString]; + if (characteristic != nil) + [peripheral setNotifyValue:YES forCharacteristic:characteristic]; + } + } +} + +- (void)unsubscribeCharacteristic:(NSString *)name service:(NSString *)serviceString characteristic:(NSString *)characteristicString +{ + if (name != nil && serviceString != nil && characteristicString != nil && _peripherals != nil) + { + CBPeripheral *peripheral = [_peripherals objectForKey:name]; + if (peripheral != nil) + { + CBCharacteristic *characteristic = [_unityBluetoothLE getCharacteristic:name service:serviceString characteristic:characteristicString]; + if (characteristic != nil) + [peripheral setNotifyValue:NO forCharacteristic:characteristic]; + } + } +} + +- (void)centralManagerDidUpdateState:(CBCentralManager *)central +{ + switch (central.state) + { + case CBCentralManagerStateUnsupported: + { + NSLog(@"Central State: Unsupported"); + + NSString *message = [NSString stringWithFormat:@"Error~Bluetooth LE Not Supported"]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } break; + + case CBCentralManagerStateUnauthorized: + { + NSLog(@"Central State: Unauthorized"); + + NSString *message = [NSString stringWithFormat:@"Error~Bluetooth LE Not Authorized"]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } break; + + case CBCentralManagerStatePoweredOff: + { + NSLog(@"Central State: Powered Off"); + + NSString *message = [NSString stringWithFormat:@"Error~Bluetooth LE Powered Off"]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } break; + + case CBCentralManagerStatePoweredOn: + { + NSLog(@"Central State: Powered On"); + if (_isInitializing) + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", "Initialized"); + _isInitializing = FALSE; + } break; + + case CBCentralManagerStateUnknown: + { + NSLog(@"Central State: Unknown"); + + NSString *message = [NSString stringWithFormat:@"Error~Bluetooth LE Unknown"]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } break; + + default: + { + } + + } +} + +- (void)centralManager:(CBCentralManager *)central didRetrievePeripherals:(NSArray *)peripherals +{ + +} + +- (void)centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray *)peripherals +{ + +} + +- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error +{ + if (error) + { + NSString *message = [NSString stringWithFormat:@"Error~%@", error.description]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } +} + +- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI +{ + if (_peripherals != nil && peripheral != nil) + { + NSString *name = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey]; + if (name == nil) + name = peripheral.name; + if (name == nil) + name = @"No Name"; + + if (name != nil) + { + NSString *identifier = nil; + + NSString *foundPeripheral = [self findPeripheralName:peripheral]; + if (foundPeripheral == nil) + identifier = [[peripheral identifier] UUIDString]; + else + identifier = foundPeripheral; + + NSString *message = nil; + + if (advertisementData != nil && [advertisementData objectForKey:CBAdvertisementDataManufacturerDataKey] != nil) + { + NSData* bytes = [advertisementData objectForKey:CBAdvertisementDataManufacturerDataKey]; + message = [NSString stringWithFormat:@"DiscoveredPeripheral~%@~%@~%@~%@", identifier, name, RSSI, [UnityBluetoothLE base64StringFromData:bytes length:bytes.length]]; + } + else if (RSSI != 0 && _rssiOnly) + { + message = [NSString stringWithFormat:@"DiscoveredPeripheral~%@~%@~%@~", identifier, name, RSSI]; + } + else + { + message = [NSString stringWithFormat:@"DiscoveredPeripheral~%@~%@", identifier, name]; + } + + if (message != nil) + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]); + + [_peripherals setObject:peripheral forKey:identifier]; + } + } +} + +- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error +{ + if (_peripherals != nil) + { + NSString *foundPeripheral = [self findPeripheralName:peripheral]; + if (foundPeripheral != nil) + { + NSString *message = [NSString stringWithFormat:@"DisconnectedPeripheral~%@", foundPeripheral]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]); + } + } +} + +- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral +{ + NSString *foundPeripheral = [self findPeripheralName:peripheral]; + if (foundPeripheral != nil) + { + NSString *message = [NSString stringWithFormat:@"ConnectedPeripheral~%@", foundPeripheral]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]); + peripheral.delegate = self; + [peripheral discoverServices:nil]; + } +} + +- (CBPeripheral *) findPeripheralInList:(CBPeripheral*)peripheral +{ + CBPeripheral *foundPeripheral = nil; + + NSEnumerator *enumerator = [_peripherals keyEnumerator]; + id key; + while ((key = [enumerator nextObject])) + { + CBPeripheral *tempPeripheral = [_peripherals objectForKey:key]; + if ([tempPeripheral isEqual:peripheral]) + { + foundPeripheral = tempPeripheral; + break; + } + } + + return foundPeripheral; +} + +- (NSString *) findPeripheralName:(CBPeripheral*)peripheral +{ + NSString *foundPeripheral = nil; + + NSEnumerator *enumerator = [_peripherals keyEnumerator]; + id key; + while ((key = [enumerator nextObject])) + { + CBPeripheral *tempPeripheral = [_peripherals objectForKey:key]; + if ([tempPeripheral isEqual:peripheral]) + { + foundPeripheral = key; + break; + } + } + + return foundPeripheral; +} + +// central peripheral delegate implementation +- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error +{ + if (error) + { + NSString *message = [NSString stringWithFormat:@"Error~%@", error.description]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } + else + { + NSString *foundPeripheral = [self findPeripheralName:peripheral]; + if (foundPeripheral != nil) + { + for (CBService *service in peripheral.services) + { + NSString *message = [NSString stringWithFormat:@"DiscoveredService~%@~%@", foundPeripheral, [service UUID]]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]); + + [peripheral discoverCharacteristics:nil forService:service]; + } + } + } +} + +- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error +{ + if (error) + { + NSString *message = [NSString stringWithFormat:@"Error~%@", error.description]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } + else + { + NSString *foundPeripheral = [self findPeripheralName:peripheral]; + if (foundPeripheral != nil) + { + for (CBCharacteristic *characteristic in service.characteristics) + { + NSString *message = [NSString stringWithFormat:@"DiscoveredCharacteristic~%@~%@~%@", foundPeripheral, [service UUID], [characteristic UUID]]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]); + } + } + } +} + +- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error +{ + if (error) + { + NSString *message = [NSString stringWithFormat:@"Error~%@", error.description]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } + else + { + NSString *foundPeripheral = [self findPeripheralName:peripheral]; + if (foundPeripheral != nil) + { + if (characteristic.value != nil) + { + NSString *message = [NSString stringWithFormat:@"DidUpdateValueForCharacteristic~%@~%@~%@", foundPeripheral, [characteristic UUID], [UnityBluetoothLE base64StringFromData:characteristic.value length:characteristic.value.length]]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]); + //NSString *message = [UnityBluetoothLE base64StringFromData:characteristic.value length:characteristic.value.length]; + //UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothData", [message UTF8String] ); + } + } + } +} + +- (void)writeCharactersticBytesReset +{ + _writeCharacteristicBytes = nil; + _writeCharacteristicLength = 0; + _writeCharacteristicPosition = 0; + _writeCharacteristicBytesToWrite = 0; + _writeCharacteristicWithResponse = CBCharacteristicWriteWithResponse; + _writeCharacteristicRetries = 3; +} + +- (void)writeCharactersticBytes:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic data:(NSData *)data withResponse:(CBCharacteristicWriteType)withResponse +{ + if (_writeCharacteristicBytes == nil && (_mtu == 0 || data.length > _mtu)) + { + _writeCharacteristicLength = data.length; + _writeCharacteristicBytes = (unsigned char*)malloc(_writeCharacteristicLength); + memcpy(_writeCharacteristicBytes, [data bytes], _writeCharacteristicLength); + _writeCharacteristicPosition = 0; + _writeCharacteristicWithResponse = withResponse; + if (_mtu == 0) + _writeCharacteristicBytesToWrite = _writeCharacteristicLength; + else + _writeCharacteristicBytesToWrite = _mtu; + } + + NSLog(@"write characteristic block"); + + if (_writeCharacteristicBytes != nil) + { + NSMutableData *newData = [NSMutableData dataWithCapacity:_writeCharacteristicBytesToWrite]; + [newData appendBytes:&_writeCharacteristicBytes[_writeCharacteristicPosition] length:_writeCharacteristicBytesToWrite]; + data = newData; + NSLog(@"data: %@", data); + } + + NSLog(@"writing %ld bytes, %ld with response", data.length, withResponse); + [peripheral writeValue:data forCharacteristic:characteristic type:withResponse]; + + if (withResponse == CBCharacteristicWriteWithoutResponse) + { + double delayInSeconds = 0.01; + dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); + dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ + [self writeNextPacket:peripheral characteristic:characteristic]; + }); + } +} + +- (void)writeNextPacket:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic +{ + if (_writeCharacteristicLength > _writeCharacteristicPosition + _writeCharacteristicBytesToWrite) + { + _writeCharacteristicPosition += _writeCharacteristicBytesToWrite; + if (_writeCharacteristicPosition + _writeCharacteristicBytesToWrite > _writeCharacteristicLength) + _writeCharacteristicBytesToWrite = _writeCharacteristicLength - _writeCharacteristicPosition; + + NSMutableData *data = [NSMutableData dataWithCapacity:_writeCharacteristicLength]; + [data appendBytes:_writeCharacteristicBytes length:_writeCharacteristicLength]; + [self writeCharactersticBytes:peripheral characteristic:characteristic data:data withResponse:_writeCharacteristicWithResponse]; + } + else + { + [self writeCharactersticBytesReset]; + NSString *message = [NSString stringWithFormat:@"DidWriteCharacteristic~%@", characteristic.UUID]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } +} + +- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error +{ + if (error) + { + NSString *message = [NSString stringWithFormat:@"Error~%@", error.description]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } + else + { + NSLog(@"%ld bytes written, %ld position, %ld length, %ld with response", _writeCharacteristicBytesToWrite, _writeCharacteristicPosition, _writeCharacteristicLength, _writeCharacteristicWithResponse); + + if (_writeCharacteristicBytesToWrite > 0) + { + [self writeNextPacket:peripheral characteristic:characteristic]; + } + else + { + NSString *message = [NSString stringWithFormat:@"DidWriteCharacteristic~%@", characteristic.UUID]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } + } +} + +- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error +{ + if (error) + { + NSString *message = [NSString stringWithFormat:@"Error~%@", error.description]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } + else + { + NSString *foundPeripheral = [self findPeripheralName:peripheral]; + if (foundPeripheral != nil) + { + NSString *message = [NSString stringWithFormat:@"DidUpdateNotificationStateForCharacteristic~%@~%@", foundPeripheral, characteristic.UUID]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } + } +} + +#if !TARGET_OS_TV +// peripheral manager delegate implementation +- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral +{ + _iOSBluetoothLELogString ([NSString stringWithFormat:@"Peripheral State Update: %d", (int)peripheral.state]); + if (_isInitializing && peripheral.state == CBPeripheralManagerStatePoweredOn) + { + _isInitializing = FALSE; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", "Initialized"); + } +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error +{ + if (error) + { + NSString *message = [NSString stringWithFormat:@"Error~%@", error.description]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } + else + { + NSString *message = [NSString stringWithFormat:@"ServiceAdded~%@", service.UUID]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } +} + +- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error +{ + if (error) + { + NSString *message = [NSString stringWithFormat:@"Error~%@", error.description]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } + else + { + NSString *message = [NSString stringWithFormat:@"StartedAdvertising"]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic +{ + +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic +{ + +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request +{ + BOOL success = FALSE; + + if (_peripheralManager != nil) + { + CBMutableCharacteristic *characteristic = [_characteristics objectForKey:request.characteristic.UUID]; + + if (characteristic != nil) + { + request.value = [characteristic.value subdataWithRange:NSMakeRange(request.offset, characteristic.value.length - request.offset)]; + [_peripheralManager respondToRequest:request withResult:CBATTErrorSuccess]; + + success = TRUE; + } + } + + if (!success) + [_peripheralManager respondToRequest:request withResult:CBATTErrorAttributeNotFound]; +} + +- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests +{ + BOOL success = FALSE; + + if (_peripheralManager != nil) + { + for (int i = 0; i < requests.count; ++i) + { + CBATTRequest *request = [requests objectAtIndex:i]; + if (request != nil) + { + CBMutableCharacteristic *characteristic = [_characteristics objectForKey:request.characteristic.UUID]; + + if (characteristic != nil) + { + characteristic.value = request.value; + + NSString *message = [NSString stringWithFormat:@"PeripheralReceivedWriteData~%@~%@", [characteristic UUID], [UnityBluetoothLE base64StringFromData:characteristic.value length:characteristic.value.length]]; + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String]); + + success = TRUE; + } + else + { + success = FALSE; + break; + } + } + else + { + success = FALSE; + break; + } + } + } + + if (success) + [_peripheralManager respondToRequest:[requests objectAtIndex:0] withResult:CBATTErrorSuccess]; + else + [_peripheralManager respondToRequest:[requests objectAtIndex:0] withResult:CBATTErrorAttributeNotFound]; +} +#endif + +- (void)sendUnityMessage:(BOOL)isString message:(NSString *)message +{ + if (_isPaused) { + + if (_backgroundMessages != nil) { + + UnityMessage *unitymessage = [[UnityMessage alloc] init]; + if (unitymessage != nil) { + + [unitymessage initialize:isString message:message]; + [_backgroundMessages addObject:unitymessage]; + } + } + } + else { + + if (isString) + { + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [message UTF8String] ); + } + else + { + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothData", [message UTF8String] ); + } + } +} + +static char base64EncodingTable[64] = +{ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' +}; + ++ (NSString *) base64StringFromData: (NSData *)data length: (int)length +{ + unsigned long ixtext, lentext; + long ctremaining; + unsigned char input[3], output[4]; + short i, charsonline = 0, ctcopy; + const unsigned char *raw; + NSMutableString *result; + + lentext = [data length]; + if (lentext < 1) + return @""; + result = [NSMutableString stringWithCapacity: lentext]; + raw = (const unsigned char *)[data bytes]; + ixtext = 0; + + while (true) { + ctremaining = lentext - ixtext; + if (ctremaining <= 0) + break; + for (i = 0; i < 3; i++) { + unsigned long ix = ixtext + i; + if (ix < lentext) + input[i] = raw[ix]; + else + input[i] = 0; + } + output[0] = (input[0] & 0xFC) >> 2; + output[1] = ((input[0] & 0x03) << 4) | ((input[1] & 0xF0) >> 4); + output[2] = ((input[1] & 0x0F) << 2) | ((input[2] & 0xC0) >> 6); + output[3] = input[2] & 0x3F; + ctcopy = 4; + switch (ctremaining) { + case 1: + ctcopy = 2; + break; + case 2: + ctcopy = 3; + break; + } + + for (i = 0; i < ctcopy; i++) + [result appendString: [NSString stringWithFormat: @"%c", base64EncodingTable[output[i]]]]; + + for (i = ctcopy; i < 4; i++) + [result appendString: @"="]; + + ixtext += 3; + charsonline += 4; + + if ((length > 0) && (charsonline >= length)) + charsonline = 0; + } + return result; +} + +#pragma mark Internal + +@end + +@implementation UnityMessage + +- (void)initialize:(BOOL)isString message:(NSString *)message +{ + _isString = isString; + _message = [message copy]; +} + +- (void)deInitialize +{ + if (_message != nil) + [_message release]; + _message = nil; +} + +- (void)sendUnityMessage +{ + if (_message != nil) { + + if (_isString) + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothMessage", [_message UTF8String] ); + else + UnitySendMessage ("BluetoothLEReceiver", "OnBluetoothData", [_message UTF8String] ); + } +} + +@end diff --git a/Assets/Plugins/iOS/UnityBluetoothLE.mm.meta b/Assets/Plugins/iOS/UnityBluetoothLE.mm.meta new file mode 100644 index 00000000..33ce6211 --- /dev/null +++ b/Assets/Plugins/iOS/UnityBluetoothLE.mm.meta @@ -0,0 +1,124 @@ +fileFormatVersion: 2 +guid: 6a40ca1fe2b7a48e5b257178dfdca41e +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Android: 1 + Exclude Editor: 1 + Exclude Linux: 1 + Exclude Linux64: 1 + Exclude LinuxUniversal: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 0 + - first: + : Linux + second: + enabled: 0 + settings: + CPU: x86 + - first: + : OSXIntel + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + : OSXIntel64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Android: Android + second: + enabled: 0 + settings: + CPU: ARMv7 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Facebook: Win + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Facebook: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Linux + second: + enabled: 0 + settings: + CPU: x86 + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: AnyCPU + - first: + iPhone: iOS + second: + enabled: 1 + settings: + AddToEmbeddedBinaries: false + CompileFlags: -fno-objc-arc + FrameworkDependencies: CoreBluetooth; + - first: + tvOS: tvOS + second: + enabled: 1 + settings: + CompileFlags: -fno-objc-arc + FrameworkDependencies: CoreBluetooth; + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/App.cs b/Assets/Scripts/App.cs index f649e2d5..956369e4 100644 --- a/Assets/Scripts/App.cs +++ b/Assets/Scripts/App.cs @@ -11,7 +11,7 @@ using Assets.Scripts.Devices; public static class App { - public static string Host = "http://192.168.0.101:5084/"; + public static string Host = "http://192.168.0.101:5083/"; public static string AppVersion = Application.version; diff --git a/Assets/Scripts/Devices/Ble/BleDevice.cs b/Assets/Scripts/Devices/Ble/BleDevice.cs index 6b634c20..beadb367 100644 --- a/Assets/Scripts/Devices/Ble/BleDevice.cs +++ b/Assets/Scripts/Devices/Ble/BleDevice.cs @@ -17,7 +17,7 @@ namespace Assets.Scripts.Devices.Ble public abstract class BleDevice : AbstractDevice { - protected BleWinHwInterface hwInterface; + protected IBleWinHwInterface hwInterface; protected BlePeripheralInfo peripheralInfo; private readonly HashSet services = new HashSet(); @@ -32,7 +32,7 @@ namespace Assets.Scripts.Devices.Ble } public List Characteristics { get; protected set; } = new List(); - public BleDevice(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface, SensorType sensor) : base(peripheralInfo.Address, NetworkType.BLE) + public BleDevice(BlePeripheralInfo peripheralInfo, IBleWinHwInterface bleWinHwInterface, SensorType sensor) : base(peripheralInfo.Address, NetworkType.BLE) { this.hwInterface = bleWinHwInterface; this.hwInterface.BluetoothStateChangedEvent += BluetoothStateChangedEvent; @@ -50,7 +50,7 @@ namespace Assets.Scripts.Devices.Ble Characteristics.Add(new ModelNumberCharacteristic()); } - private void HwInterface_PeripheralDisconnectedEvent(BleWinHwInterface hwInterface, BlePeripheralInfo peripheral, BleResponse response, bool manualDisconnect) + private void HwInterface_PeripheralDisconnectedEvent(IBleWinHwInterface hwInterface, BlePeripheralInfo peripheral, BleResponse response, bool manualDisconnect) { if (!peripheral.MatchAddress(this.peripheralInfo.Address)) { @@ -93,7 +93,7 @@ namespace Assets.Scripts.Devices.Ble } } - private void PeripheralConnectedAction(BleWinHwInterface hwInterface, BlePeripheralInfo sender, BleResponse response) + private void PeripheralConnectedAction(IBleWinHwInterface hwInterface, BlePeripheralInfo sender, BleResponse response) { Debug.Log($"连接{ this.Name }"+response.IsSuccess); if (response.IsSuccess) @@ -123,7 +123,7 @@ namespace Assets.Scripts.Devices.Ble Debug.Log("断开设备" + this.Name); //App.MainDeviceAdapter.PrintStatus(); - + this.State = DeviceState.Disconnected; this.hwInterface.DisconnectPeripheral(this.peripheralInfo, () => { //App.MainDeviceAdapter.PrintStatus(); @@ -136,7 +136,7 @@ namespace Assets.Scripts.Devices.Ble - private void ServicesDiscoveredAction(BleWinHwInterface hwInterface, BlePeripheralInfo sender, BleResponse> response) + private void ServicesDiscoveredAction(IBleWinHwInterface hwInterface, BlePeripheralInfo sender, BleResponse> response) { //Debug.Log("搜索service"); if(!response.IsSuccess || !response.Data.Any()) @@ -211,7 +211,7 @@ namespace Assets.Scripts.Devices.Ble { } - private void BluetoothStateChangedEvent(BleWinHwInterface hwInterface, BleState state) + private void BluetoothStateChangedEvent(IBleWinHwInterface hwInterface, BleState state) { switch (state) { diff --git a/Assets/Scripts/Devices/Ble/BleDeviceAdapter.cs b/Assets/Scripts/Devices/Ble/BleDeviceAdapter.cs index 823b439b..b32faaae 100644 --- a/Assets/Scripts/Devices/Ble/BleDeviceAdapter.cs +++ b/Assets/Scripts/Devices/Ble/BleDeviceAdapter.cs @@ -1,6 +1,7 @@ using Assets.Scripts.Ble; using Assets.Scripts.Ble.Service; using Assets.Scripts.Devices.Ble.Devices; +using Assets.Scripts.Devices.Ble.Interfaces; using Assets.Scripts.UI.Prefab.Device; using System; using System.Collections.Generic; @@ -17,15 +18,16 @@ namespace Assets.Scripts.Devices.Ble private IDictionary discoveredDevices = new Dictionary(); - private BleWinHwInterface hwInterface; - public BleDeviceAdapter() + private IBleWinHwInterface hwInterface { get; set; } + public BleDeviceAdapter(IBleWinHwInterface bleWinHwInterface) { - hwInterface = BleWinHwInterface.GetInterface(); + + hwInterface = bleWinHwInterface;// BleWinHwInterface.GetInterface(); hwInterface.BluetoothStateChangedEvent += HwInterface_BluetoothStateChangedEvent; } - private void HwInterface_BluetoothStateChangedEvent(BleWinHwInterface hwInterface, BleState bleState) + private void HwInterface_BluetoothStateChangedEvent(IBleWinHwInterface hwInterface, BleState bleState) { //Debug.Log("22222222222222" + bleState); if(bleState == BleState.Off) diff --git a/Assets/Scripts/Devices/Ble/BleResponse.cs b/Assets/Scripts/Devices/Ble/BleResponse.cs index 0c15559d..707278a9 100644 --- a/Assets/Scripts/Devices/Ble/BleResponse.cs +++ b/Assets/Scripts/Devices/Ble/BleResponse.cs @@ -37,5 +37,10 @@ namespace Assets.Scripts.Devices.Ble { return base.ToString() + "\n" + this.Data; } - } + + public static implicit operator BleResponse(BleResponse> v) + { + throw new NotImplementedException(); + } + } } diff --git a/Assets/Scripts/Devices/Ble/BluetoothStateChangedCallback.cs b/Assets/Scripts/Devices/Ble/BluetoothStateChangedCallback.cs index c8fd1484..7a038776 100644 --- a/Assets/Scripts/Devices/Ble/BluetoothStateChangedCallback.cs +++ b/Assets/Scripts/Devices/Ble/BluetoothStateChangedCallback.cs @@ -1,4 +1,5 @@ -using System; +using Assets.Scripts.Devices.Ble.Interfaces; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -6,6 +7,6 @@ using System.Threading.Tasks; namespace Assets.Scripts.Ble { - public delegate void BluetoothStateChangedCallback(BleWinHwInterface hwInterface, BleState bleState); + public delegate void BluetoothStateChangedCallback(IBleWinHwInterface hwInterface, BleState bleState); } diff --git a/Assets/Scripts/Devices/Ble/CharacteristicReadCallback.cs b/Assets/Scripts/Devices/Ble/CharacteristicReadCallback.cs index 9328c12a..b81a34fe 100644 --- a/Assets/Scripts/Devices/Ble/CharacteristicReadCallback.cs +++ b/Assets/Scripts/Devices/Ble/CharacteristicReadCallback.cs @@ -1,4 +1,5 @@ using Assets.Scripts.Devices.Ble; +using Assets.Scripts.Devices.Ble.Interfaces; using System; using System.Collections.Generic; using System.Linq; @@ -7,5 +8,5 @@ using System.Threading.Tasks; namespace Assets.Scripts.Ble { - public delegate void CharacteristicReadCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse response); + public delegate void CharacteristicReadCallback(IBleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse response); } diff --git a/Assets/Scripts/Devices/Ble/CharacteristicsDiscoveredCallback.cs b/Assets/Scripts/Devices/Ble/CharacteristicsDiscoveredCallback.cs index d3fab0e7..d32563d2 100644 --- a/Assets/Scripts/Devices/Ble/CharacteristicsDiscoveredCallback.cs +++ b/Assets/Scripts/Devices/Ble/CharacteristicsDiscoveredCallback.cs @@ -1,4 +1,5 @@ using Assets.Scripts.Devices.Ble; +using Assets.Scripts.Devices.Ble.Interfaces; using System; using System.Collections.Generic; using System.Linq; @@ -7,5 +8,5 @@ using System.Threading.Tasks; namespace Assets.Scripts.Ble { - public delegate void CharacteristicsDiscoveredCallback(BleWinHwInterface hwInterface, BleServiceInfo service, BleResponse> response); + public delegate void CharacteristicsDiscoveredCallback(IBleWinHwInterface hwInterface, BleServiceInfo service, BleResponse> response); } diff --git a/Assets/Scripts/Devices/Ble/Devices/CyclingPower.cs b/Assets/Scripts/Devices/Ble/Devices/CyclingPower.cs index 23bbca0b..097080b8 100644 --- a/Assets/Scripts/Devices/Ble/Devices/CyclingPower.cs +++ b/Assets/Scripts/Devices/Ble/Devices/CyclingPower.cs @@ -1,6 +1,7 @@ using Assets.Scripts.Ble; using Assets.Scripts.Devices.Ant.Interfaces; using Assets.Scripts.Devices.Ble.Characteristic; +using Assets.Scripts.Devices.Ble.Interfaces; using System; using System.Collections.Generic; using System.Linq; @@ -14,7 +15,7 @@ namespace Assets.Scripts.Devices.Ble.Devices { private List Services; private CyclingPowerMeasurement cyclingPowerMeasurement; - public CyclingPower(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.Power) + public CyclingPower(BlePeripheralInfo peripheralInfo, IBleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.Power) { Priority = 2; cyclingPowerMeasurement = new CyclingPowerMeasurement(1920); @@ -49,7 +50,7 @@ namespace Assets.Scripts.Devices.Ble.Devices } - private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse response) + private void CharacteristicReadMainCallback(IBleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse response) { //Debug.Log("power main call" + string.Join(",", response.Data)); diff --git a/Assets/Scripts/Devices/Ble/Devices/Ftms.cs b/Assets/Scripts/Devices/Ble/Devices/Ftms.cs index 5cc33535..1fb5db56 100644 --- a/Assets/Scripts/Devices/Ble/Devices/Ftms.cs +++ b/Assets/Scripts/Devices/Ble/Devices/Ftms.cs @@ -1,6 +1,7 @@ using Assets.Scripts.Ble; using Assets.Scripts.Devices.Ant.Interfaces; using Assets.Scripts.Devices.Ble.Characteristic; +using Assets.Scripts.Devices.Ble.Interfaces; using System; using System.Collections.Generic; using System.Linq; @@ -21,7 +22,7 @@ namespace Assets.Scripts.Devices.Ble.Devices private List Services; private BleCharacteristicInfo controlPointCharacteristic; - public Ftms(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) :base(peripheralInfo, bleWinHwInterface, Ant.SensorType.Trainer) + public Ftms(BlePeripheralInfo peripheralInfo, IBleWinHwInterface bleWinHwInterface) :base(peripheralInfo, bleWinHwInterface, Ant.SensorType.Trainer) { this._ftmsIndoorBikeData = new FtmsIndoorBikeData(); base.Characteristics.Add(this._ftmsIndoorBikeData); @@ -60,7 +61,7 @@ namespace Assets.Scripts.Devices.Ble.Devices } } - private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse response) + private void CharacteristicReadMainCallback(IBleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse response) { foreach (var item in base.Characteristics) { diff --git a/Assets/Scripts/Devices/Ble/Devices/HeartRate.cs b/Assets/Scripts/Devices/Ble/Devices/HeartRate.cs index c84781d1..5ce8abf3 100644 --- a/Assets/Scripts/Devices/Ble/Devices/HeartRate.cs +++ b/Assets/Scripts/Devices/Ble/Devices/HeartRate.cs @@ -1,6 +1,7 @@ using Assets.Scripts.Ble; using Assets.Scripts.Devices.Ant.Interfaces; using Assets.Scripts.Devices.Ble.Characteristic; +using Assets.Scripts.Devices.Ble.Interfaces; using System; using System.Collections.Generic; using System.Linq; @@ -16,7 +17,7 @@ namespace Assets.Scripts.Devices.Ble.Devices private List Services; private HeartRateMeasurement heartRateMeasurement; - public HeartRate(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.HeartRate) + public HeartRate(BlePeripheralInfo peripheralInfo, IBleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.HeartRate) { Priority = 2; heartRateMeasurement = new HeartRateMeasurement(); @@ -73,7 +74,7 @@ namespace Assets.Scripts.Devices.Ble.Devices }); } - private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse response) + private void CharacteristicReadMainCallback(IBleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse response) { //Debug.Log("heart rate main call" + string.Join(",", response.Data)); diff --git a/Assets/Scripts/Devices/Ble/Devices/SpeedCadence.cs b/Assets/Scripts/Devices/Ble/Devices/SpeedCadence.cs index 504a4fd0..281d4546 100644 --- a/Assets/Scripts/Devices/Ble/Devices/SpeedCadence.cs +++ b/Assets/Scripts/Devices/Ble/Devices/SpeedCadence.cs @@ -1,6 +1,7 @@ using Assets.Scripts.Ble; using Assets.Scripts.Devices.Ant.Interfaces; using Assets.Scripts.Devices.Ble.Characteristic; +using Assets.Scripts.Devices.Ble.Interfaces; using System; using System.Collections.Generic; using System.Linq; @@ -18,7 +19,7 @@ namespace Assets.Scripts.Devices.Ble.Devices private double _wheelCircumference; private List Services; - public SpeedCadence(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.SpeedCadence) + public SpeedCadence(BlePeripheralInfo peripheralInfo, IBleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.SpeedCadence) { Debug.Log("创建速度踏频设备"); Priority = 1; @@ -81,7 +82,7 @@ namespace Assets.Scripts.Devices.Ble.Devices } - private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse response) + private void CharacteristicReadMainCallback(IBleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse response) { //Debug.Log("power main call" + string.Join(",", response.Data)); diff --git a/Assets/Scripts/Devices/Ble/Devices/Tacx.cs b/Assets/Scripts/Devices/Ble/Devices/Tacx.cs index 39333b15..5c6c1e94 100644 --- a/Assets/Scripts/Devices/Ble/Devices/Tacx.cs +++ b/Assets/Scripts/Devices/Ble/Devices/Tacx.cs @@ -2,6 +2,7 @@ using Assets.Scripts.Devices.Ant; using Assets.Scripts.Devices.Ant.Interfaces; using Assets.Scripts.Devices.Ble.Characteristic; +using Assets.Scripts.Devices.Ble.Interfaces; using System; using System.Collections.Generic; using System.Linq; @@ -30,7 +31,7 @@ namespace Assets.Scripts.Devices.Ble.Devices /// private double _grade = 0; - public Tacx(BlePeripheralInfo peripheralInfo, BleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.Trainer) + public Tacx(BlePeripheralInfo peripheralInfo, IBleWinHwInterface bleWinHwInterface) : base(peripheralInfo, bleWinHwInterface, Ant.SensorType.Trainer) { tacxFecNotify = new TacxFecNotify(); base.Characteristics.Add(tacxFecNotify); @@ -75,7 +76,7 @@ namespace Assets.Scripts.Devices.Ble.Devices } } - private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse response) + private void CharacteristicReadMainCallback(IBleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse response) { foreach (var item in base.Characteristics) { diff --git a/Assets/Scripts/Devices/Ble/HeartRate/HeartRateService.cs b/Assets/Scripts/Devices/Ble/HeartRate/HeartRateService.cs index dc9f5fef..00bb791b 100644 --- a/Assets/Scripts/Devices/Ble/HeartRate/HeartRateService.cs +++ b/Assets/Scripts/Devices/Ble/HeartRate/HeartRateService.cs @@ -2,6 +2,7 @@ using Assets.Scripts.Ble.Service; using Assets.Scripts.Commands; using Assets.Scripts.Devices.Ble; +using Assets.Scripts.Devices.Ble.Interfaces; using System; using System.Collections.Generic; using System.Linq; @@ -36,12 +37,12 @@ namespace Assets.Scripts.Ble.HeartRate } } - private void HrmCharacteristicSubscribed(BleWinHwInterface winHwInterface, BleCharacteristicInfo characteristic, BleResponse response) + private void HrmCharacteristicSubscribed(IBleWinHwInterface winHwInterface, BleCharacteristicInfo characteristic, BleResponse response) { } - private void HrmCharacteristicRead(BleWinHwInterface winHwInterface, BleCharacteristicInfo characteristic, BleResponse response) + private void HrmCharacteristicRead(IBleWinHwInterface winHwInterface, BleCharacteristicInfo characteristic, BleResponse response) { HeartRateMeasurementValue heartRateMeasurementValue; if(base.CheckCharacteristicResponse(characteristic, response, (BleCharacteristicInfo info, BleResponse bleResponse) => new HeartRateMeasurementValue(bleResponse.Data), out heartRateMeasurementValue) != CommandResponseCode.Success) diff --git a/Assets/Scripts/Devices/Ble/Interfaces/IBleWinHwInterface.cs b/Assets/Scripts/Devices/Ble/Interfaces/IBleWinHwInterface.cs new file mode 100644 index 00000000..dc500079 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Interfaces/IBleWinHwInterface.cs @@ -0,0 +1,28 @@ +using Assets.Scripts.Ble; +using Assets.Scripts.Ble.Scan; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Assets.Scripts.Devices.Ble.Interfaces +{ + public interface IBleWinHwInterface + { + void StartScan(Action discoveredCallBack); + void ConnectPeripheral(BlePeripheralInfo info, Action callback); + void DisconnectPeripheral(BlePeripheralInfo peripheral, Action callback); + void DiscoverServices(BlePeripheralInfo peripheral, Action>> callback); + void DiscoverCharacteristic(BleServiceInfo service, CharacteristicsDiscoveredCallback callback); + void SubscribeCharacteristic(BleCharacteristicInfo characteristic, Action callback); + void WriteCharacteristic(BleCharacteristicInfo characteristic, byte[] data); + void ReadCharacteristic(BleCharacteristicInfo characteristic, CharacteristicReadCallback callback); + void Dispose(); + BleState BleState { get; set; } + Dictionary pCache { get; set; } + event BluetoothStateChangedCallback BluetoothStateChangedEvent; + event CharacteristicReadCallback CharacteristicReadEvent; + event PeripheralDisconnectedCallback PeripheralDisconnectedEvent; + } +} diff --git a/Assets/Scripts/Devices/Ble/Interfaces/IBleWinHwInterface.cs.meta b/Assets/Scripts/Devices/Ble/Interfaces/IBleWinHwInterface.cs.meta new file mode 100644 index 00000000..77926020 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/Interfaces/IBleWinHwInterface.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 86216427171ac784296ca577b921b2e7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/PeripheralDisconnectedCallback.cs b/Assets/Scripts/Devices/Ble/PeripheralDisconnectedCallback.cs index 3b24a05b..e6211017 100644 --- a/Assets/Scripts/Devices/Ble/PeripheralDisconnectedCallback.cs +++ b/Assets/Scripts/Devices/Ble/PeripheralDisconnectedCallback.cs @@ -1,4 +1,5 @@ using Assets.Scripts.Ble; +using Assets.Scripts.Devices.Ble.Interfaces; using System; using System.Collections.Generic; using System.Linq; @@ -7,5 +8,5 @@ using System.Threading.Tasks; namespace Assets.Scripts.Devices.Ble { - public delegate void PeripheralDisconnectedCallback(BleWinHwInterface hwInterface, BlePeripheralInfo peripheral, BleResponse response, bool manualDisconnect); + public delegate void PeripheralDisconnectedCallback(IBleWinHwInterface hwInterface, BlePeripheralInfo peripheral, BleResponse response, bool manualDisconnect); } diff --git a/Assets/Scripts/Devices/Ble/Service/BleService.cs b/Assets/Scripts/Devices/Ble/Service/BleService.cs index 40a0f123..b8679d82 100644 --- a/Assets/Scripts/Devices/Ble/Service/BleService.cs +++ b/Assets/Scripts/Devices/Ble/Service/BleService.cs @@ -8,6 +8,7 @@ using Assets.Scripts.Ble.Commands; using Assets.Scripts.Commands; using Assets.Scripts.Devices.Ble; using Assets.Scripts.Devices.Ble.Extension; +using Assets.Scripts.Devices.Ble.Interfaces; using UnityEngine; namespace Assets.Scripts.Ble.Service @@ -18,7 +19,7 @@ namespace Assets.Scripts.Ble.Service private BleServiceInfo serviceInfo; private List characteristics; - private readonly Dictionary>> registeredCharacteristicReadEvents = new Dictionary>>(); + private readonly Dictionary>> registeredCharacteristicReadEvents = new Dictionary>>(); public IReadOnlyList Characteristics { @@ -58,7 +59,7 @@ namespace Assets.Scripts.Ble.Service this.hwInterface.CharacteristicReadEvent += CharacteristicReadMainCallback; } - private void CharacteristicReadMainCallback(BleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse response) + private void CharacteristicReadMainCallback(IBleWinHwInterface hwInterface, BleCharacteristicInfo characteristic, BleResponse response) { //Debug.Log("read main callback"); if (this.registeredCharacteristicReadEvents.ContainsKey(characteristic.Id) && characteristic.Service.Equals(this.serviceInfo)) @@ -79,7 +80,7 @@ namespace Assets.Scripts.Ble.Service this.CheckCharacteristicDiscoveredResponse(this.hwInterface, this.serviceInfo, scanCharacteristicsResponse); } - private void CheckCharacteristicDiscoveredResponse(BleWinHwInterface hwInterface, BleServiceInfo service, BleResponse> response) + private void CheckCharacteristicDiscoveredResponse(IBleWinHwInterface hwInterface, BleServiceInfo service, BleResponse> response) { if (!service.Equals(this.serviceInfo)) { @@ -98,8 +99,8 @@ namespace Assets.Scripts.Ble.Service protected abstract void CharacteristicsDiscovered(BleResponse> response); - protected void SubscribeCharacteristic(BleCharacteristicInfo characteristic, Action notificationCallback, - Action> readCallback) + protected void SubscribeCharacteristic(BleCharacteristicInfo characteristic, Action notificationCallback, + Action> readCallback) { this.hwInterface.SubscribeCharacteristic(characteristic, (hw, info, response) => { diff --git a/Assets/Scripts/Devices/Ble/Win/BleWinHwInterface.cs b/Assets/Scripts/Devices/Ble/Win/BleWinHwInterface.cs index 96576511..794db60f 100644 --- a/Assets/Scripts/Devices/Ble/Win/BleWinHwInterface.cs +++ b/Assets/Scripts/Devices/Ble/Win/BleWinHwInterface.cs @@ -2,6 +2,7 @@ using Assets.Scripts.Ble.Win; using Assets.Scripts.Devices.Ant; using Assets.Scripts.Devices.Ble; +using Assets.Scripts.Devices.Ble.Interfaces; using Assets.Scripts.Devices.Ble.Win; using System; using System.Collections.Generic; @@ -12,13 +13,24 @@ using UnityEngine; namespace Assets.Scripts.Ble { - public sealed class BleWinHwInterface + public sealed class BleWinHwInterface: IBleWinHwInterface { private static BleWinHwInterface hwInterface; private WclBleMainThread wclBleMainThread; - public readonly Dictionary pCache = new Dictionary(); - + private Dictionary _pCache; + public Dictionary pCache + { + get + { + if (_pCache == null) + { + _pCache = new Dictionary(); + } + return _pCache; + } + set { _pCache = value; } + } private Action _discoveredCallback; private Dictionary> callbacks = new Dictionary>(); @@ -43,6 +55,7 @@ namespace Assets.Scripts.Ble } } + private BluetoothStateChangedCallback bluetoothStateChanged; public event BluetoothStateChangedCallback BluetoothStateChangedEvent { @@ -55,7 +68,7 @@ namespace Assets.Scripts.Ble this.bluetoothStateChanged -= value; } } - + private BleState nativeState; public BleState BleState { @@ -63,7 +76,7 @@ namespace Assets.Scripts.Ble { return this.nativeState; } - private set + set { if(this.nativeState != value) { @@ -201,7 +214,7 @@ namespace Assets.Scripts.Ble return wclBleGattThread; } - internal void ConnectPeripheral(BlePeripheralInfo info, Action callback) + public void ConnectPeripheral(BlePeripheralInfo info, Action callback) { this.callbacks.Add(info, callback); WclBleGattThread wclBleGattThread = this.wclBleMainThread.GetGattThread(info); @@ -229,8 +242,8 @@ namespace Assets.Scripts.Ble //}); } - - internal void DisconnectPeripheral(BlePeripheralInfo peripheral, Action callback) + + public void DisconnectPeripheral(BlePeripheralInfo peripheral, Action callback) { var gattThread = this.wclBleMainThread.GetGattThread(peripheral); if(gattThread != null && gattThread.CanLoadWork) @@ -372,7 +385,7 @@ namespace Assets.Scripts.Ble } } - public void DiscoverServices(BlePeripheralInfo peripheral, Action>> callback) + public void DiscoverServices(BlePeripheralInfo peripheral, Action>> callback) { WclBleGattThread gattThread = this.wclBleMainThread.GetGattThread(peripheral); if(gattThread == null) @@ -420,7 +433,7 @@ namespace Assets.Scripts.Ble this.GattCharacteristicsDiscovered(gattThread, service, response); } - public void SubscribeCharacteristic(BleCharacteristicInfo characteristic, Action callback) + public void SubscribeCharacteristic(BleCharacteristicInfo characteristic, Action callback) { WclBleGattThread gattThread = this.wclBleMainThread.GetGattThread(characteristic.Peripheral); diff --git a/Assets/Scripts/Devices/Ble/mobile.meta b/Assets/Scripts/Devices/Ble/mobile.meta new file mode 100644 index 00000000..95152633 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/mobile.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9fa2f8906f5360c47b807eaef8d42263 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/mobile/BleMobileInterface.cs b/Assets/Scripts/Devices/Ble/mobile/BleMobileInterface.cs new file mode 100644 index 00000000..b18fa19f --- /dev/null +++ b/Assets/Scripts/Devices/Ble/mobile/BleMobileInterface.cs @@ -0,0 +1,399 @@ +using Assets.Scripts.Ble.Scan; +using Assets.Scripts.Ble.Win; +using Assets.Scripts.Devices.Ant; +using Assets.Scripts.Devices.Ble; +using Assets.Scripts.Devices.Ble.Interfaces; +using Assets.Scripts.Devices.Ble.Win; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace Assets.Scripts.Ble +{ + public sealed class BleMobileInterface : IBleWinHwInterface + { + private static BleMobileInterface hwInterface; + private BleMobileThread bleMobileThread; + + private Dictionary _pCache; + public Dictionary pCache + { + get + { + if (_pCache == null) + { + _pCache = new Dictionary(); + } + return _pCache; + } + set { _pCache = value; } + } + private Action _discoveredCallback; + + private Dictionary> callbacks = new Dictionary>(); + private Dictionary>>> servicesCallbacks = new Dictionary>>>(); + //private Dictionary>>> characteristicsCallbacks = new Dictionary>>>(); + private Dictionary> characteristicNotificationCallbacks = new Dictionary>(); + private Dictionary characteristicReadCallbacks = new Dictionary(); + private Dictionary characteristicsDiscoveredCallbacks = new Dictionary(); + + private Dictionary disconnectedCallback = new Dictionary(); + + private CharacteristicReadCallback characteristicReadEvent; + public event CharacteristicReadCallback CharacteristicReadEvent + { + add + { + this.characteristicReadEvent += value; + } + remove + { + this.characteristicReadEvent -= value; + } + } + + + private BluetoothStateChangedCallback bluetoothStateChanged; + public event BluetoothStateChangedCallback BluetoothStateChangedEvent + { + add + { + this.bluetoothStateChanged += value; + } + remove + { + this.bluetoothStateChanged -= value; + } + } + + private BleState nativeState; + public BleState BleState + { + get + { + return this.nativeState; + } + set + { + if (this.nativeState != value) + { + this.nativeState = value; + this.bluetoothStateChanged?.Invoke(this, this.nativeState); + } + } + } + + private PeripheralDisconnectedCallback peripheralDisconnectedEvent; + public event PeripheralDisconnectedCallback PeripheralDisconnectedEvent + { + add + { + this.peripheralDisconnectedEvent += value; + } + remove + { + this.peripheralDisconnectedEvent -= value; + } + } + + private BleMobileInterface() + { + bleMobileThread = new BleMobileThread(); + bleMobileThread.ManagerInitialized += BleMobileThread_ManagerInitialized; + bleMobileThread.ManagerStatusChanged += ManagerStatusChanged; + bleMobileThread.ScanInfoReceived += WatcherScanInfoReceived; + } + + private void BleMobileThread_ManagerInitialized(BleMobileThread thread) + { + this.BleState = BleState.On; + } + + private void ManagerStatusChanged(BleMobileThread sender, WclBleManagerStatus status) + { + this.BleState = BleMobileInterface.StateFromNativeState(status); + } + + private void WatcherScanInfoReceived(BleMobileThread sender, string address, string name, int rssi,string[] uuids) + { + SensorType sensor = SensorType.None; + List services = new List(); + + if (uuids != null && uuids.Length > 0) + { + foreach (var item in uuids) + { + services.Add(new Guid(item)); + } + foreach (var item in ServiceUuids.Services) + { + if (!uuids.Contains(item.IdGuid.ToString())) + { + continue; + } + if (item.IdByteArray == ServiceUuids.Ftms) + { + sensor = SensorType.Trainer; + } + else if (item.IdByteArray == ServiceUuids.HeartRate) + { + sensor = SensorType.HeartRate; + } + else if (item.IdByteArray == ServiceUuids.CyclingPower) + { + sensor = SensorType.Power; + //sensor = SensorType.Trainer; + } + else if (item.IdByteArray == ServiceUuids.CyclingSpeedCadence) + { + sensor = SensorType.SpeedCadence; + } + else if (item.IdByteArray == ServiceUuids.TacxBle) + { + sensor = SensorType.Trainer; + } + } + }; + + var addressStr = address; + if (!pCache.ContainsKey(addressStr)) + { + var device = new BleAdvertisementInfo(new WinBlePeripheralInfo(addressStr, name), rssi, true, services, null, sensor); + pCache.Add(addressStr, device); + } + if (!string.IsNullOrWhiteSpace(name)) + { + (pCache[addressStr].Peripheral as WinBlePeripheralInfo).SetName(name); + } + if (sensor == SensorType.None) + { + return; + } + pCache[addressStr].SensorType = sensor; + pCache[addressStr].Rssi = rssi; + //Debug.Log(sensor); + //pCache[address.ToString()].SensorType = sensor; + + foreach (var item in uuids) + { + pCache[addressStr].TryAddService(new Guid(item)); + } + + pCache[addressStr].Index++; + if (pCache[addressStr].SensorType == SensorType.Power && pCache[addressStr].Services.Any(s => s.Equals(ServiceUuids.Get(ServiceUuids.TacxBle).IdGuid))) + { + pCache[addressStr].SensorType = SensorType.Trainer; + //Debug.Log("纠正为trainer, "+ pCache[address.ToString()].Index); + } + + + if (pCache[addressStr].SensorType == SensorType.Power) + { + if (pCache[addressStr].Index > 4) + { + _discoveredCallback?.Invoke(pCache[addressStr]); + } + } + else if (pCache[addressStr].SensorType != SensorType.None) + { + _discoveredCallback?.Invoke(pCache[addressStr]); + } + } + + //private WclBleGattThread SetUpGattClient(BlePeripheralInfo peripheral) + //{ + // WclBleGattThread wclBleGattThread = this.wclBleMainThread.CreateGattThread(peripheral); + // wclBleGattThread.GattConnected += this.GattConnected; + // wclBleGattThread.GattDisconnected += this.GattDisconnected; + // wclBleGattThread.GattServicesDiscovered += this.GattServicesDiscovered; + // wclBleGattThread.GattCharacteristicsDiscovered += this.GattCharacteristicsDiscovered; + // wclBleGattThread.GattCharacteristicSubscribed += this.GattCharacteristicSubscribed; + // wclBleGattThread.GattCharacteristicRead += this.GattCharacteristicRead; + // wclBleGattThread.GattCharacteristicWrote += this.GattCharacteristicWrote; + // wclBleGattThread.GattCharacteristicChanged += this.GattCharacteristicChanged; + // wclBleGattThread.Start(); + // return wclBleGattThread; + //} + + List servicelist = new List(); + List characteristilist = new List(); + public void ConnectPeripheral(BlePeripheralInfo info, Action callback) + { + BleResponse s = new BleResponse(); + s.IsSuccess = true; + s.Error = null; + var self = this; + BluetoothLEHardwareInterface.ConnectToPeripheral(info.Address, (address) => + { + callback?.Invoke(self, info, s); + }, (address, service) => { + if (servicesCallbacks.Where(c => c.Key.Address == address).Any()) + { + + var serviceCallback = servicesCallbacks.Where(c => c.Key.Address == address).FirstOrDefault(); + List servicelist = new List(); + servicelist.Add(new WinBleServiceInfo(serviceCallback.Key,new Guid(service))); + BleResponse> response = new BleResponse> + { + IsSuccess = true, + Error = null, + Data = servicelist, + }; + serviceCallback.Value?.Invoke(this, serviceCallback.Key, response); + } + + }, (address, service, characteristic) => + { + if (characteristicsDiscoveredCallbacks.Where(c => c.Key.Peripheral.Address == address).Any()) + { + var characteristicCallback = characteristicsDiscoveredCallbacks.Where(c => c.Key.Peripheral.Address == address).FirstOrDefault(); + List characteristiclist = new List(); + characteristiclist.Add(new WinBleCharacteristicInfo(new Guid(characteristic), new WinBleServiceInfo(characteristicCallback.Key.Peripheral, new Guid(service)),0)); + BleResponse> response = new BleResponse> + { + IsSuccess = true, + Error = null, + Data = characteristiclist, + }; + characteristicCallback.Value?.Invoke(this, characteristicCallback.Key, response); + } + }, null); + } + + //设备断开连接 + public void DisconnectPeripheral(BlePeripheralInfo peripheral, Action callback) + { + BluetoothLEHardwareInterface.DisconnectPeripheral(peripheral.Address, (address) => + { + //peripheralDisconnectedEvent(this, peripheral, null, true); + callback?.Invoke(); + }); + } + + public static BleMobileInterface GetInterface() + { + if(hwInterface == null) + { + hwInterface = new BleMobileInterface(); + } + + return hwInterface; + } + //扫描设备 + public void StartScan(Action discoveredCallBack) + { + pCache.Clear(); + _discoveredCallback = discoveredCallBack; + bleMobileThread.StartWatcher(); + } + //发现服务列表 + public void DiscoverServices(BlePeripheralInfo peripheral, Action>> callback) + { + this.servicesCallbacks.Add(peripheral, callback); + } + //发现特征值 + public void DiscoverCharacteristic(BleServiceInfo service, CharacteristicsDiscoveredCallback callback) + { + this.characteristicsDiscoveredCallbacks.Add(service, callback); + } + //订阅特征值 + public void SubscribeCharacteristic(BleCharacteristicInfo characteristic, Action callback) + { + this.characteristicNotificationCallbacks.Add(characteristic.Peripheral, callback); + BluetoothLEHardwareInterface.SubscribeCharacteristicWithDeviceAddress(characteristic.Peripheral.Address, characteristic.Service.ToString(), characteristic.Id.ToString(), null, (deviceAddress, characteristric, bytes) => + { + if (characteristicReadEvent != null) + { + BleResponse response = new BleResponse + { + IsSuccess = true, + Error = null, + Data = bytes, + }; + characteristicReadEvent.Invoke(this, characteristic, response); + } + }); + } + //写入特征值 + public void WriteCharacteristic(BleCharacteristicInfo characteristic, byte[] data) + { + BluetoothLEHardwareInterface.WriteCharacteristic(characteristic.Peripheral.Address, characteristic.Service.Id.ToString(), characteristic.Id.ToString(), data, data.Length, false, (characteristicUUID) => { + + BluetoothLEHardwareInterface.Log("Write Succeeded"); + }); + } + + + public void Dispose() + { + bleMobileThread.ManagerInitialized -= BleMobileThread_ManagerInitialized; + bleMobileThread.ManagerStatusChanged -= ManagerStatusChanged; + bleMobileThread.ScanInfoReceived -= WatcherScanInfoReceived; + bleMobileThread.Stop(); + bleMobileThread = null; + hwInterface = null; + pCache.Clear(); + } + + public void ReadCharacteristic(BleCharacteristicInfo characteristic, CharacteristicReadCallback callback) + { + BluetoothLEHardwareInterface.ReadCharacteristic(characteristic.Peripheral.Address, characteristic.Service.Id.ToString(), characteristic.Id.ToString(), (c, bytes) => { + BleResponse response = new BleResponse + { + IsSuccess = true, + Error = null, + Data = bytes, + }; + callback?.Invoke(this, characteristic, response); + }); + } + + private static BleState StateFromNativeState(WclBleManagerStatus status) + { + switch (status) + { + case WclBleManagerStatus.RadioOff: + return BleState.Off; + case WclBleManagerStatus.RadioOn: + return BleState.On; + case WclBleManagerStatus.Unknown: + return BleState.Unknown; + default: + return BleState.Unavailable; + } + } + + private class WinBlePeripheralInfo : BlePeripheralInfo + { + // Token: 0x06003F35 RID: 16181 RVA: 0x000E9FBF File Offset: 0x000E81BF + public WinBlePeripheralInfo(string address, string name) : base(address, name) + { + } + + // Token: 0x06003F36 RID: 16182 RVA: 0x000E9FC9 File Offset: 0x000E81C9 + public void SetName(string name) + { + base.Name = name; + } + } + + private class WinBleServiceInfo : BleServiceInfo + { + // Token: 0x06003F86 RID: 16262 RVA: 0x000EA27F File Offset: 0x000E847F + public WinBleServiceInfo(BlePeripheralInfo peripheral, Guid id) : base(id, peripheral) + { + } + } + private class WinBleCharacteristicInfo : BleCharacteristicInfo + { + // Token: 0x06003F86 RID: 16262 RVA: 0x000EA27F File Offset: 0x000E847F + public WinBleCharacteristicInfo(Guid id, BleServiceInfo service, BleCharacteristicProperties properties) : base(id,service, properties) + { + + } + } + } +} diff --git a/Assets/Scripts/Devices/Ble/mobile/BleMobileInterface.cs.meta b/Assets/Scripts/Devices/Ble/mobile/BleMobileInterface.cs.meta new file mode 100644 index 00000000..f9174596 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/mobile/BleMobileInterface.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e5257ea169e69a498a3e4d935a51306 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/Ble/mobile/BleMobileThread.cs b/Assets/Scripts/Devices/Ble/mobile/BleMobileThread.cs new file mode 100644 index 00000000..a9db13d0 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/mobile/BleMobileThread.cs @@ -0,0 +1,80 @@ +using Assets.Scripts.Ble; +using System.Timers; + +namespace Assets.Scripts.Devices.Ble +{ + internal class BleMobileThread + { + public delegate void WclInitializedDelegate(BleMobileThread sender); + private BleMobileThread.WclInitializedDelegate managerInitialized; + public event BleMobileThread.WclInitializedDelegate ManagerInitialized + { + add + { + this.managerInitialized += value; + } + remove + { + this.managerInitialized -= value; + } + } + + public delegate void WclAdvertisementPacketDelegate(BleMobileThread sender, string address, string name, int rssi, string[] uuids); + public BleMobileThread.WclAdvertisementPacketDelegate ScanInfoReceived; + + internal delegate void ManagerStatusChangedCallback(BleMobileThread thread, WclBleManagerStatus status); + private BleMobileThread.ManagerStatusChangedCallback managerStatusChanged; + public event BleMobileThread.ManagerStatusChangedCallback ManagerStatusChanged + { + add + { + this.managerStatusChanged += value; + } + remove + { + this.managerStatusChanged -= value; + } + } + + internal BleMobileThread() { + var self = this; + //初始蓝牙 + BluetoothLEHardwareInterface.Initialize(true, false, () => { + managerInitialized?.Invoke(self); + }, + (error) => { + BluetoothLEHardwareInterface.Log("Error: " + error); + if (error.Contains("Bluetooth LE Not Enabled")) + BluetoothLEHardwareInterface.BluetoothEnable(true); + }, (status) => { + + var statusEnum = WclBleManagerStatus.RadioOn; + switch (status) + { + case 13: + statusEnum = WclBleManagerStatus.RadioOff; + break; + case 11: + statusEnum = WclBleManagerStatus.RadioOn; + break; + } + if (managerStatusChanged != null) + { + managerStatusChanged.Invoke(self, statusEnum); + } + }); + } + + public void StartWatcher() { + BluetoothLEHardwareInterface.ScanForPeripheralsWithServices(null, null, (address, name, rssi, bytes) => + { + ScanInfoReceived?.Invoke(this, address, name, rssi, bytes); + }, true); + } + + public void Stop() + { + BluetoothLEHardwareInterface.StopScan(); + } + } +} diff --git a/Assets/Scripts/Devices/Ble/mobile/BleMobileThread.cs.meta b/Assets/Scripts/Devices/Ble/mobile/BleMobileThread.cs.meta new file mode 100644 index 00000000..aaa1a572 --- /dev/null +++ b/Assets/Scripts/Devices/Ble/mobile/BleMobileThread.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cec5e50ac67fde54196ea58f5b1b790b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/Devices/MainDeviceAdapter.cs b/Assets/Scripts/Devices/MainDeviceAdapter.cs index 3e50f026..a5f84d70 100644 --- a/Assets/Scripts/Devices/MainDeviceAdapter.cs +++ b/Assets/Scripts/Devices/MainDeviceAdapter.cs @@ -1,4 +1,5 @@ -using Assets.Scripts.Devices.Ant; +using Assets.Scripts.Ble; +using Assets.Scripts.Devices.Ant; using Assets.Scripts.Devices.Ble; using System; using System.Collections.Generic; @@ -14,10 +15,12 @@ namespace Assets.Scripts.Devices private List adapters = new List(); public MainDeviceAdapter() { - #if !(UNITY_IOS || UNITY_ANDROID) - this.CreateAntAdapter(); +#if UNITY_IOS || UNITY_TVOS || UNITY_ANDROID this.CreateBleAdapter(); - #endif +#else + this.CreateBleAdapter(); + this.CreateAntAdapter(); +#endif } private void CreateAntAdapter() @@ -27,7 +30,11 @@ namespace Assets.Scripts.Devices private void CreateBleAdapter() { - adapters.Add(new BleDeviceAdapter()); +#if UNITY_IOS || UNITY_TVOS || UNITY_ANDROID + adapters.Add(new BleDeviceAdapter(BleMobileInterface.GetInterface())); +#else + adapters.Add(new BleDeviceAdapter(BleWinHwInterface.GetInterface())); +#endif } public void StartScan() diff --git a/Assets/Scripts/Scenes/LoginController.cs b/Assets/Scripts/Scenes/LoginController.cs index 6b7db09d..1294ceb8 100644 --- a/Assets/Scripts/Scenes/LoginController.cs +++ b/Assets/Scripts/Scenes/LoginController.cs @@ -330,6 +330,7 @@ public class LoginController : MonoBehaviour { wxReturnLogin(); }); + #if !(UNITY_IOS || UNITY_ANDROID) wxBrowser = wxLogin1.Find("Browser (GUI)").GetComponent(); if (wxBrowser != null) { @@ -941,7 +942,9 @@ public class LoginController : MonoBehaviour void wxReturnLogin() { wxLock = true; +#if !(UNITY_IOS || UNITY_ANDROID) wxBrowser.LoadHTML("
"); +#endif //wxBrowser.Url = "chrome://version/"; wxLogin1.DOLocalMoveY(-573, 0.3f).onComplete = () => { diff --git a/Assets/Scripts/Scenes/Ride/Scripts/PlayerController.cs b/Assets/Scripts/Scenes/Ride/Scripts/PlayerController.cs index 94388d22..d342a727 100644 --- a/Assets/Scripts/Scenes/Ride/Scripts/PlayerController.cs +++ b/Assets/Scripts/Scenes/Ride/Scripts/PlayerController.cs @@ -56,7 +56,7 @@ namespace Assets.Scenes.Ride.Scripts weight = App.CurrentUser.Weight; bicycleWeight = App.CurrentUser.BicycleWeight; //#if UNITY_EDITOR - power = 500; + //power = 500; //#endif mainController.TrackResistance(currentSlope * App.RideSetting.Sensitivity / 100); } diff --git a/Assets/Shatalmic.meta b/Assets/Shatalmic.meta new file mode 100644 index 00000000..22b01e25 --- /dev/null +++ b/Assets/Shatalmic.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fcef0f33b95c6fd4d9b2f5a0dcd0d564 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shatalmic/Demo.unity b/Assets/Shatalmic/Demo.unity new file mode 100644 index 00000000..87ccf60d --- /dev/null +++ b/Assets/Shatalmic/Demo.unity @@ -0,0 +1,439 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 3 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 0 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 10 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringMode: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ShowResolutionOverlay: 1 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &53270293 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 53270296} + - component: {fileID: 53270295} + - component: {fileID: 53270294} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &53270294 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 53270293} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1077351063, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &53270295 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 53270293} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -619905303, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &53270296 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 53270293} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &344612123 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 344612127} + - component: {fileID: 344612126} + - component: {fileID: 344612125} + - component: {fileID: 344612124} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &344612124 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 344612123} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &344612125 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 344612123} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!223 &344612126 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 344612123} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &344612127 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 344612123} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 601672577} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &601672576 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 601672577} + - component: {fileID: 601672579} + - component: {fileID: 601672578} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &601672577 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 601672576} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 344612127} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &601672578 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 601672576} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 72 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 7 + m_MaxSize: 72 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Due to the new Unity asset publishing requirements you must follow the instructions + in the README file to unpack this plugin. +--- !u!222 &601672579 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 601672576} + m_CullTransparentMesh: 0 +--- !u!1 &1655223073 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1655223076} + - component: {fileID: 1655223075} + - component: {fileID: 1655223074} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1655223074 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1655223073} + m_Enabled: 1 +--- !u!20 &1655223075 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1655223073} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 1, g: 1, b: 1, a: 0} + m_projectionMatrixMode: 1 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_GateFitMode: 2 + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 1 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1655223076 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1655223073} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/Shatalmic/Demo.unity.meta b/Assets/Shatalmic/Demo.unity.meta new file mode 100644 index 00000000..4f135db5 --- /dev/null +++ b/Assets/Shatalmic/Demo.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0149d806ed996492787b9cb44993fd5b +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shatalmic/Editor.meta b/Assets/Shatalmic/Editor.meta new file mode 100644 index 00000000..6b9cdfe0 --- /dev/null +++ b/Assets/Shatalmic/Editor.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 87247c18c42564aceaf454206a3ef873 +folderAsset: yes +DefaultImporter: + userData: diff --git a/Assets/Shatalmic/Editor/BluetoothPostProcessBuild.cs b/Assets/Shatalmic/Editor/BluetoothPostProcessBuild.cs new file mode 100644 index 00000000..847ca187 --- /dev/null +++ b/Assets/Shatalmic/Editor/BluetoothPostProcessBuild.cs @@ -0,0 +1,30 @@ +#if UNITY_IOS +using UnityEditor.Callbacks; +using UnityEditor; +using UnityEditor.iOS.Xcode; +using System.IO; + +public class BluetoothPostProcessBuild +{ + [PostProcessBuild] + public static void ChangeXcodePlist(BuildTarget buildTarget, string pathToBuiltProject) + { + if (buildTarget == BuildTarget.iOS) + { + // Get plist + string plistPath = pathToBuiltProject + "/Info.plist"; + PlistDocument plist = new PlistDocument(); + plist.ReadFromString(File.ReadAllText(plistPath)); + + // Get root + PlistElementDict rootDict = plist.root; + + rootDict.SetString("NSBluetoothPeripheralUsageDescription", "Uses BLE to communicate with devices."); + rootDict.SetString("NSBluetoothAlwaysUsageDescription", "Uses BLE to communicate with devices."); + + // Write to file + File.WriteAllText(plistPath, plist.WriteToString()); + } + } +} +#endif diff --git a/Assets/Shatalmic/Editor/BluetoothPostProcessBuild.cs.meta b/Assets/Shatalmic/Editor/BluetoothPostProcessBuild.cs.meta new file mode 100644 index 00000000..99d5856a --- /dev/null +++ b/Assets/Shatalmic/Editor/BluetoothPostProcessBuild.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1695b640ed1354053a8cdd9e0186f57f +timeCreated: 1493664761 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shatalmic/Editor/PostprocessBuildPlayer b/Assets/Shatalmic/Editor/PostprocessBuildPlayer new file mode 100644 index 00000000..3c2e8ece --- /dev/null +++ b/Assets/Shatalmic/Editor/PostprocessBuildPlayer @@ -0,0 +1,93 @@ +#!/usr/bin/perl +use File::Copy; + +print ("\n*** Starting PostprocessBuildPlayer ***\n"); + +my $installPath = $ARGV[0]; + +#values to watch for already existing +my $requiredDeviceCapsString = "UIRequiredDeviceCapabilities"; +my $backgroundModesString = "UIBackgroundModes"; +my $bluetoothLEString = "bluetooth-le"; + +#go through the info.plist file line by line until you find this one: +my $endOfPlist = ""; + +# The type of player built: +# "dashboard", "standaloneWin32", "standaloneOSXIntel", "standaloneOSXPPC", "standaloneOSXUniversal", "webplayer", "iPhone" +my $target = $ARGV[1]; + +print ("\n*** PostprocessBuildPlayer - Building at '$installPath' with target: $target ***\n"); + +################################################################ +# This modifies info.plist to add the external accessory # +# entries # +################################################################ + +#open this file + +$oplistPath = $installPath."/Info.plist"; +$nplistPath = $installPath."/Info.plist.tmp"; + +open OLDPLIST, "<", $oplistPath or die("Cannot open Info.plist"); +open NEWPLIST, ">", $nplistPath or die("Cannot create new Info.plist"); + +my $nextLine = 0; +my $requiredDeviceCapsFound = 0; +my $backgroundModesFound = 0; + +while() +{ + if ($nextLine == 1) + { + if ($_ =~ m/$bluetoothLEString/) + { + } + else + { + $_ =~ s||\n\tbluetooth-le| + } + + $nextLine = 0; + } + + ################################################################ + # Check for required device caps string already exists # + ################################################################ + if ($_ =~ m/$requiredDeviceCapsString/ ) + { + $nextLine = 1; + } + + ################################################################ + # Check for background modes string already exists # + ################################################################ + if ($_ =~ m/$backgroundModesString/ ) + { + $backgroundModesFound = 1; + } + + ################################################################ + # Add any key/value pairs you want at the end of Info.plist # + ################################################################ + + if ($_ =~ m/$endOfPlist/ && $backgrounModesFound == 0) + { + my $keys = ""; + + $keys .= " UIBackgroundModes\n"; + $keys .= " \n"; + $keys .= " bluetooth-central\n"; + $keys .= " bluetooth-peripheral\n"; + $keys .= " \n"; + + $_ = $keys . $_; + } + + print NEWPLIST $_; +} + +close OLDPLIST; +close NEWPLIST; + +`mv \'$nplistPath\' \'$oplistPath\'`; diff --git a/Assets/Shatalmic/Editor/PostprocessBuildPlayer.meta b/Assets/Shatalmic/Editor/PostprocessBuildPlayer.meta new file mode 100644 index 00000000..7fceff06 --- /dev/null +++ b/Assets/Shatalmic/Editor/PostprocessBuildPlayer.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 3c726eecf85fe4a88a7e7d57812ff455 +timeCreated: 1515198229 +licenseType: Pro +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shatalmic/README.txt b/Assets/Shatalmic/README.txt new file mode 100644 index 00000000..ea8d3e7b --- /dev/null +++ b/Assets/Shatalmic/README.txt @@ -0,0 +1 @@ +You must double click on the unity package file called Plugin.unitypackage in order to unpack the plugin files. \ No newline at end of file diff --git a/Assets/Shatalmic/README.txt.meta b/Assets/Shatalmic/README.txt.meta new file mode 100644 index 00000000..56fc2dbd --- /dev/null +++ b/Assets/Shatalmic/README.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3465e0116537642f78e4b288b23c461c +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shatalmic/plugin.unitypackage.meta b/Assets/Shatalmic/plugin.unitypackage.meta new file mode 100644 index 00000000..c11cbc4e --- /dev/null +++ b/Assets/Shatalmic/plugin.unitypackage.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1a70f37e2f13e4df0ac477f3c3eb451b +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: