diff --git a/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_Central.cs b/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_Central.cs index 3d72df8..57becd8 100644 --- a/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_Central.cs +++ b/Assets/CoreBluetooth/Samples/12_Debug/SampleDebug_Central.cs @@ -18,7 +18,8 @@ public class SampleDebug_Central : MonoBehaviour, ICBCentralManagerDelegate, ICB void Start() { - _centralManager = new CBCentralManager(this); + var initOptions = new CBCentralInitOptions() { ShowPowerAlert = true }; + _centralManager = new CBCentralManager(this, initOptions); } public void DidDiscoverPeripheral(CBCentralManager central, CBPeripheral peripheral, int rssi) diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Plugins/iOS/CoreBluetoothForUnity.framework/CoreBluetoothForUnity b/Packages/com.teach310.core-bluetooth-for-unity/Plugins/iOS/CoreBluetoothForUnity.framework/CoreBluetoothForUnity index 8eb805a..948bd64 100755 Binary files a/Packages/com.teach310.core-bluetooth-for-unity/Plugins/iOS/CoreBluetoothForUnity.framework/CoreBluetoothForUnity and b/Packages/com.teach310.core-bluetooth-for-unity/Plugins/iOS/CoreBluetoothForUnity.framework/CoreBluetoothForUnity differ diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Plugins/macOS/libCoreBluetoothForUnity.dylib b/Packages/com.teach310.core-bluetooth-for-unity/Plugins/macOS/libCoreBluetoothForUnity.dylib index e37f7eb..68168c8 100755 Binary files a/Packages/com.teach310.core-bluetooth-for-unity/Plugins/macOS/libCoreBluetoothForUnity.dylib and b/Packages/com.teach310.core-bluetooth-for-unity/Plugins/macOS/libCoreBluetoothForUnity.dylib differ diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBCentralInitOptions.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBCentralInitOptions.cs new file mode 100644 index 0000000..d4c013e --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBCentralInitOptions.cs @@ -0,0 +1,25 @@ +using CoreBluetooth.Foundation; + +namespace CoreBluetooth +{ + public class CBCentralInitOptions + { + public static readonly string ShowPowerAlertKey = "kCBInitOptionShowPowerAlert"; + public bool? ShowPowerAlert { get; set; } = null; + + public CBCentralInitOptions() + { + } + + internal NSMutableDictionary ToNativeDictionary() + { + var dict = new NSMutableDictionary(); + if (ShowPowerAlert.HasValue) + { + using var value = new NSNumber(ShowPowerAlert.Value); + dict.SetValue(ShowPowerAlertKey, value.Handle); + } + return dict; + } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBCentralInitOptions.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBCentralInitOptions.cs.meta new file mode 100644 index 0000000..b30d0d7 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBCentralInitOptions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4c8e3e6332ec44f878dac785a26dabe0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBCentralManager.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBCentralManager.cs index d8ed04e..cedb0ba 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBCentralManager.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/CBCentralManager.cs @@ -42,9 +42,17 @@ public ICBCentralManagerDelegate Delegate NativeCentralManagerProxy _nativeCentralManagerProxy; - public CBCentralManager(ICBCentralManagerDelegate centralDelegate = null) + public CBCentralManager(ICBCentralManagerDelegate centralDelegate = null, CBCentralInitOptions options = null) { - _handle = SafeNativeCentralManagerHandle.Create(); + if (options == null) + { + _handle = SafeNativeCentralManagerHandle.Create(); + } + else + { + using var optionsDict = options.ToNativeDictionary(); + _handle = SafeNativeCentralManagerHandle.Create(optionsDict.Handle); + } Delegate = centralDelegate; _nativeCentralManagerProxy = new NativeCentralManagerProxy(_handle, this); } diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation.meta b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation.meta new file mode 100644 index 0000000..ec11650 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0d01d4c153ea844208e5cb33fd821c18 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSMutableDictionary.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSMutableDictionary.cs new file mode 100644 index 0000000..964761b --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSMutableDictionary.cs @@ -0,0 +1,50 @@ +using System; + +namespace CoreBluetooth.Foundation +{ + internal class NSMutableDictionary : IDisposable + { + public SafeNSMutableDictionaryHandle Handle { get; private set; } + + public NSMutableDictionary() + { + Handle = NativeMethods.ns_mutable_dictionary_new(); + } + + public IntPtr GetValue(SafeNSObjectHandle key) + { + ExceptionUtils.ThrowObjectDisposedExceptionIf(Handle.IsInvalid, this); + if (key.IsInvalid) + throw new ArgumentNullException(nameof(key)); + + return NativeMethods.ns_mutable_dictionary_get_value(Handle, key); + } + + public bool TryGetValue(SafeNSObjectHandle key, out IntPtr value) + { + value = GetValue(key); + return value != IntPtr.Zero; + } + + public void SetValue(SafeNSObjectHandle key, SafeNSObjectHandle value) + { + ExceptionUtils.ThrowObjectDisposedExceptionIf(Handle.IsInvalid, this); + if (key.IsInvalid) + throw new ArgumentNullException(nameof(key)); + + NativeMethods.ns_mutable_dictionary_set_value(Handle, key, value); + } + + public void SetValue(string key, SafeNSObjectHandle value) + { + using var nsString = new NSString(key); + SetValue(nsString.Handle, value); + } + + public void Dispose() + { + if (Handle != null && !Handle.IsInvalid) + Handle.Dispose(); + } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSMutableDictionary.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSMutableDictionary.cs.meta new file mode 100644 index 0000000..9ebbb82 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSMutableDictionary.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 57ce2fa2a35c04ec1aea132e440957dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSNumber.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSNumber.cs new file mode 100644 index 0000000..4bdd251 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSNumber.cs @@ -0,0 +1,47 @@ +using System; + +namespace CoreBluetooth.Foundation +{ + public class NSNumber : IDisposable + { + internal SafeNSNumberHandle Handle { get; private set; } + + internal NSNumber(bool value) + { + Handle = NativeMethods.ns_number_new_bool(value); + } + + internal NSNumber(int value) + { + Handle = NativeMethods.ns_number_new_int(value); + } + + internal NSNumber(SafeNSNumberHandle handle) + { + Handle = handle; + } + + internal NSNumber(IntPtr handle) + { + Handle = new SafeNSNumberHandle(handle); + } + + public bool BoolValue() + { + ExceptionUtils.ThrowObjectDisposedExceptionIf(Handle.IsInvalid, this); + return NativeMethods.ns_number_bool_value(Handle); + } + + public int IntValue() + { + ExceptionUtils.ThrowObjectDisposedExceptionIf(Handle.IsInvalid, this); + return NativeMethods.ns_number_int_value(Handle); + } + + public void Dispose() + { + if (Handle != null && !Handle.IsInvalid) + Handle.Dispose(); + } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSNumber.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSNumber.cs.meta new file mode 100644 index 0000000..90e327a --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSNumber.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6434df3869a1e4a748239147c582f655 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSString.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSString.cs new file mode 100644 index 0000000..9d1f463 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSString.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.InteropServices; + +namespace CoreBluetooth.Foundation +{ + public class NSString : IDisposable + { + internal SafeNSStringHandle Handle { get; private set; } + + public NSString(string str) + { + if (str is null) + throw new ArgumentNullException(nameof(str)); + + Handle = NativeMethods.ns_string_new(str); + } + + internal NSString(SafeNSStringHandle handle) + { + Handle = handle; + } + + internal NSString(IntPtr handle) + { + Handle = new SafeNSStringHandle(handle); + } + + public int LengthOfBytesUtf8() + { + ExceptionUtils.ThrowObjectDisposedExceptionIf(Handle.IsInvalid, this); + return NativeMethods.ns_string_length_of_bytes_utf8(Handle); + } + + public override string ToString() + { + ExceptionUtils.ThrowObjectDisposedExceptionIf(Handle.IsInvalid, this); + return HandleToString(Handle); + } + + public void Dispose() + { + if (Handle != null && !Handle.IsInvalid) + Handle.Dispose(); + } + + internal static string HandleToString(SafeNSStringHandle handle) + { + if (handle.IsInvalid) + return null; + + NativeMethods.ns_string_get_cstring_and_length(handle, out IntPtr ptr, out int length); + if (ptr == IntPtr.Zero) + return null; + + if (length == 0) + return string.Empty; + + return Marshal.PtrToStringUTF8(ptr, length); + } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSString.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSString.cs.meta new file mode 100644 index 0000000..ee517aa --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NSString.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 63bfe215835164c11b748c3372df3b75 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NativeMethods.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NativeMethods.cs new file mode 100644 index 0000000..9934910 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NativeMethods.cs @@ -0,0 +1,47 @@ +using System; +using System.Runtime.InteropServices; + +namespace CoreBluetooth.Foundation +{ + internal static class NativeMethods + { +#if UNITY_IOS && !UNITY_EDITOR + const string DLL_NAME = "__Internal"; +#else + const string DLL_NAME = "libCoreBluetoothForUnity"; +#endif + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern void ns_object_release(IntPtr handle); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern SafeNSNumberHandle ns_number_new_bool([MarshalAs(UnmanagedType.I1)] bool value); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern SafeNSNumberHandle ns_number_new_int(int value); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern bool ns_number_bool_value(SafeNSNumberHandle handle); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern int ns_number_int_value(SafeNSNumberHandle handle); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern SafeNSStringHandle ns_string_new([MarshalAs(UnmanagedType.LPStr)] string str); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern int ns_string_length_of_bytes_utf8(SafeNSStringHandle handle); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern void ns_string_get_cstring_and_length(SafeNSStringHandle handle, out IntPtr ptr, out int length); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern SafeNSMutableDictionaryHandle ns_mutable_dictionary_new(); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr ns_mutable_dictionary_get_value(SafeNSMutableDictionaryHandle handle, SafeNSObjectHandle key); + + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] + internal static extern void ns_mutable_dictionary_set_value(SafeNSMutableDictionaryHandle handle, SafeNSObjectHandle key, SafeNSObjectHandle value); + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NativeMethods.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NativeMethods.cs.meta new file mode 100644 index 0000000..8d54101 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/NativeMethods.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e30aa1a959f834368a7d5082602bd951 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSMutableDictionaryHandle.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSMutableDictionaryHandle.cs new file mode 100644 index 0000000..d20eb8d --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSMutableDictionaryHandle.cs @@ -0,0 +1,10 @@ +using System; + +namespace CoreBluetooth.Foundation +{ + internal class SafeNSMutableDictionaryHandle : SafeNSObjectHandle + { + public SafeNSMutableDictionaryHandle() : base() { } + public SafeNSMutableDictionaryHandle(IntPtr handle) : base(handle) { } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSMutableDictionaryHandle.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSMutableDictionaryHandle.cs.meta new file mode 100644 index 0000000..4e25f04 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSMutableDictionaryHandle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 109e42256379744fcada5c8f5914169b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSNumberHandle.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSNumberHandle.cs new file mode 100644 index 0000000..bfc1588 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSNumberHandle.cs @@ -0,0 +1,10 @@ +using System; + +namespace CoreBluetooth.Foundation +{ + internal class SafeNSNumberHandle : SafeNSObjectHandle + { + public SafeNSNumberHandle() : base() { } + public SafeNSNumberHandle(IntPtr handle) : base(handle) { } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSNumberHandle.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSNumberHandle.cs.meta new file mode 100644 index 0000000..4121d56 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSNumberHandle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d25d9164f6ce5490cb55386db2e22d9b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSObjectHandle.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSObjectHandle.cs new file mode 100644 index 0000000..4a0473c --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSObjectHandle.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.Win32.SafeHandles; + +namespace CoreBluetooth.Foundation +{ + public abstract class SafeNSObjectHandle : SafeHandleZeroOrMinusOneIsInvalid + { + protected SafeNSObjectHandle() : base(true) { } + protected SafeNSObjectHandle(IntPtr handle) : base(true) + { + SetHandle(handle); + } + + protected override bool ReleaseHandle() + { + NativeMethods.ns_object_release(handle); + return true; + } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSObjectHandle.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSObjectHandle.cs.meta new file mode 100644 index 0000000..49e32d9 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSObjectHandle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5f1b5da64673e47abbbace0db0a880c1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSStringHandle.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSStringHandle.cs new file mode 100644 index 0000000..932519c --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSStringHandle.cs @@ -0,0 +1,10 @@ +using System; + +namespace CoreBluetooth.Foundation +{ + internal class SafeNSStringHandle : SafeNSObjectHandle + { + public SafeNSStringHandle() : base() { } + public SafeNSStringHandle(IntPtr handle) : base(handle) { } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSStringHandle.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSStringHandle.cs.meta new file mode 100644 index 0000000..a782128 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/Foundation/SafeNSStringHandle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 66452e05814934a688bb31bf681ce898 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativeMethods.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativeMethods.cs index 6c9210a..3e264d1 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativeMethods.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/NativeMethods.cs @@ -25,7 +25,7 @@ int identifierSize internal static extern int cb4u_central_maximum_update_value_length(SafeNativeCentralHandle handle); [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] - internal static extern SafeNativeCentralManagerHandle cb4u_central_manager_new(); + internal static extern SafeNativeCentralManagerHandle cb4u_central_manager_new(IntPtr options); [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] internal static extern void cb4u_central_manager_release(IntPtr handle); diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/SafeNativeCentralManagerHandle.cs b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/SafeNativeCentralManagerHandle.cs index 4eef3e5..6fab801 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Runtime/SafeNativeCentralManagerHandle.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Runtime/SafeNativeCentralManagerHandle.cs @@ -20,13 +20,23 @@ internal class SafeNativeCentralManagerHandle : SafeHandleZeroOrMinusOneIsInvali SafeNativeCentralManagerHandle() : base(true) { } - internal static SafeNativeCentralManagerHandle Create() + static SafeNativeCentralManagerHandle Create(IntPtr options) { - var instance = NativeMethods.cb4u_central_manager_new(); + var instance = NativeMethods.cb4u_central_manager_new(options); instance.RegisterHandlers(); return instance; } + internal static SafeNativeCentralManagerHandle Create(Foundation.SafeNSMutableDictionaryHandle options) + { + return Create(options.DangerousGetHandle()); + } + + internal static SafeNativeCentralManagerHandle Create() + { + return Create(IntPtr.Zero); + } + void RegisterHandlers() { NativeMethods.cb4u_central_manager_register_handlers( diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/CBCentralManagerTests.cs b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/CBCentralManagerTests.cs index 7754001..3e0fe8f 100644 --- a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/CBCentralManagerTests.cs +++ b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/CBCentralManagerTests.cs @@ -23,6 +23,14 @@ public void Create() Assert.That(centralManager, Is.Not.Null); } + [Test] + public void CreateWithOptions() + { + var options = new CBCentralInitOptions() { ShowPowerAlert = true }; + using var centralManager = new CBCentralManager(null, options); + Assert.That(centralManager, Is.Not.Null); + } + [Test] public void Release() { diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation.meta b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation.meta new file mode 100644 index 0000000..d90eaa3 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ea24704c13038490da0c067e709299bd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSMutableDictionaryTests.cs b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSMutableDictionaryTests.cs new file mode 100644 index 0000000..a8997d0 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSMutableDictionaryTests.cs @@ -0,0 +1,55 @@ +using CoreBluetooth.Foundation; +using NUnit.Framework; + +namespace CoreBluetoothTests.Foundation +{ + public class NSMutableDictionaryTests + { + [Test] + public void New() + { + using var nsDictionary = new NSMutableDictionary(); + Assert.That(nsDictionary.Handle, Is.Not.Null); + Assert.That(nsDictionary.Handle.IsInvalid, Is.False); + } + + [Test] + public void SetAndGetStringValue() + { + using var nsDictionary = new NSMutableDictionary(); + using var key = new NSString("hoge"); + using var value = new NSString("fuga"); + nsDictionary.SetValue(key.Handle, value.Handle); + + using var key2 = new NSString("hoge"); + using var nSString = new NSString(nsDictionary.GetValue(key2.Handle)); + Assert.That(nSString.ToString(), Is.EqualTo("fuga")); + } + + [Test] + public void ReturnZeroPointerIfNotFound() + { + using var nsDictionary = new NSMutableDictionary(); + using var notFoundKey = new NSString("dummy"); + var gotPtr = nsDictionary.GetValue(notFoundKey.Handle); + Assert.That(gotPtr, Is.EqualTo(System.IntPtr.Zero)); + } + + [Test] + public void TryGetValue() + { + using var nsDictionary = new NSMutableDictionary(); + using var key = new NSString("hoge"); + using var value = new NSString("fuga"); + nsDictionary.SetValue(key.Handle, value.Handle); + + using var key2 = new NSString("hoge"); + Assert.That(nsDictionary.TryGetValue(key2.Handle, out var gotPtr), Is.True); + using var nSString = new NSString(gotPtr); + Assert.That(nSString.ToString(), Is.EqualTo("fuga")); + + using var notFoundKey = new NSString("dummy"); + Assert.That(nsDictionary.TryGetValue(notFoundKey.Handle, out gotPtr), Is.False); + } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSMutableDictionaryTests.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSMutableDictionaryTests.cs.meta new file mode 100644 index 0000000..a0dbdaf --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSMutableDictionaryTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f3f95fa6f3878490db759d0b3f3b2bd1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSNumberTests.cs b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSNumberTests.cs new file mode 100644 index 0000000..ce044dd --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSNumberTests.cs @@ -0,0 +1,31 @@ +using CoreBluetooth.Foundation; +using NUnit.Framework; + +namespace CoreBluetoothTests.Foundation +{ + public class NSNumberTests + { + [Test] + public void IntValue() + { + using var nsNumber = new NSNumber(2); + Assert.That(nsNumber.Handle, Is.Not.Null); + Assert.That(nsNumber.Handle.IsInvalid, Is.False); + Assert.That(nsNumber.IntValue, Is.EqualTo(2)); + } + + [Test] + public void BoolValue() + { + using var trueNumber = new NSNumber(true); + Assert.That(trueNumber.Handle, Is.Not.Null); + Assert.That(trueNumber.Handle.IsInvalid, Is.False); + Assert.That(trueNumber.BoolValue, Is.True); + + using var falseNumber = new NSNumber(false); + Assert.That(falseNumber.Handle, Is.Not.Null); + Assert.That(falseNumber.Handle.IsInvalid, Is.False); + Assert.That(falseNumber.BoolValue, Is.False); + } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSNumberTests.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSNumberTests.cs.meta new file mode 100644 index 0000000..f668cdb --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSNumberTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 92d8a88cda7bf4b118dfd49aee13c8b1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSStringTests.cs b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSStringTests.cs new file mode 100644 index 0000000..e1b9068 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSStringTests.cs @@ -0,0 +1,54 @@ +using CoreBluetooth.Foundation; +using NUnit.Framework; + +namespace CoreBluetoothTests.Foundation +{ + public class NSStringTests + { + [Test] + public void New() + { + using var nsString = new NSString("dummy"); + Assert.That(nsString.Handle, Is.Not.Null); + Assert.That(nsString.Handle.IsInvalid, Is.False); + } + + [Test] + public void LengthOfBytesUtf8() + { + using (var nsString = new NSString("a")) + { + Assert.That(nsString.LengthOfBytesUtf8(), Is.EqualTo(1)); + } + + using (var nsString = new NSString("あ")) + { + Assert.That(nsString.LengthOfBytesUtf8(), Is.EqualTo(3)); + } + + using (var nsString = new NSString("𠮷")) + { + Assert.That(nsString.LengthOfBytesUtf8(), Is.EqualTo(4)); + } + } + + [Test] + public void HandleToString() + { + using (var nsString = new NSString("dummy")) + { + Assert.That(NSString.HandleToString(nsString.Handle), Is.EqualTo("dummy")); + } + + using (var nsString = new NSString("あいうえお")) + { + Assert.That(NSString.HandleToString(nsString.Handle), Is.EqualTo("あいうえお")); + } + + using (var nsString = new NSString(string.Empty)) + { + Assert.That(NSString.HandleToString(nsString.Handle), Is.EqualTo(string.Empty)); + } + } + } +} diff --git a/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSStringTests.cs.meta b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSStringTests.cs.meta new file mode 100644 index 0000000..6487f38 --- /dev/null +++ b/Packages/com.teach310.core-bluetooth-for-unity/Tests/Runtime/Foundation/NSStringTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bab5ec5e9e0894e8ea6a61fe9486284d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UCentralManager.swift b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UCentralManager.swift index cedb521..45b2af4 100644 --- a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UCentralManager.swift +++ b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CB4UCentralManager.swift @@ -11,10 +11,10 @@ public class CB4UCentralManager : NSObject { public var didDiscoverPeripheralHandler: CB4UCentralManagerDidDiscoverPeripheralHandler? public var didUpdateStateHandler: CB4UCentralManagerDidUpdateStateHandler? - public override init() { + public init(_ options: [String: Any]? = nil) { super.init() - centralManager = CBCentralManager(delegate: self, queue: nil) + centralManager = CBCentralManager(delegate: self, queue: nil, options: options) } func selfPointer() -> UnsafeMutableRawPointer { diff --git a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CoreBluetoothForUnity.swift b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CoreBluetoothForUnity.swift index 4c6c542..6c5fdcd 100644 --- a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CoreBluetoothForUnity.swift +++ b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/CoreBluetoothForUnity.swift @@ -28,8 +28,14 @@ public func cb4u_central_maximum_update_value_length(_ centralPtr: UnsafeRawPoin } @_cdecl("cb4u_central_manager_new") -public func cb4u_central_manager_new() -> UnsafeMutableRawPointer { - return Unmanaged.passRetained(CB4UCentralManager()).toOpaque() +public func cb4u_central_manager_new(_ optionsPtr: UnsafeRawPointer?) -> UnsafeMutableRawPointer { + var options: [String: Any]? = nil + if let optionsPtr = optionsPtr { + let nsMutableDictionary = Unmanaged.fromOpaque(optionsPtr).takeUnretainedValue() + options = nsMutableDictionary as? [String: Any] + } + + return Unmanaged.passRetained(CB4UCentralManager(options)).toOpaque() } @_cdecl("cb4u_central_manager_release") diff --git a/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/FoundationForUnity/FoundationForUnity.swift b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/FoundationForUnity/FoundationForUnity.swift new file mode 100644 index 0000000..348db59 --- /dev/null +++ b/Plugins/CoreBluetoothForUnity/Sources/CoreBluetoothForUnity/FoundationForUnity/FoundationForUnity.swift @@ -0,0 +1,85 @@ +import Foundation + +@_cdecl("ns_object_release") +public func ns_object_release(_ handle: UnsafeRawPointer) { + Unmanaged.fromOpaque(handle).release() +} + +@_cdecl("ns_number_new_bool") +public func ns_number_new_bool(_ value: Bool) -> UnsafeMutableRawPointer { + let instance = NSNumber(value: value) + return Unmanaged.passRetained(instance).toOpaque() +} + +@_cdecl("ns_number_new_int") +public func ns_number_new_int(_ value: Int32) -> UnsafeMutableRawPointer { + let instance = NSNumber(value: value) + return Unmanaged.passRetained(instance).toOpaque() +} + +@_cdecl("ns_number_bool_value") +public func ns_number_bool_value(_ handle: UnsafeRawPointer) -> Bool { + let instance = Unmanaged.fromOpaque(handle).takeUnretainedValue() + return instance.boolValue +} + +@_cdecl("ns_number_int_value") +public func ns_number_int_value(_ handle: UnsafeRawPointer) -> Int32 { + let instance = Unmanaged.fromOpaque(handle).takeUnretainedValue() + return instance.int32Value +} + +@_cdecl("ns_string_new") +public func ns_string_new(_ str: UnsafePointer) -> UnsafeMutableRawPointer { + let nsstring = NSString(utf8String: str)! + return Unmanaged.passRetained(nsstring).toOpaque() +} + +@_cdecl("ns_string_length_of_bytes_utf8") +public func ns_string_length_of_bytes_utf8(_ handle: UnsafeRawPointer) -> Int32 { + let nsstring = Unmanaged.fromOpaque(handle).takeUnretainedValue() + return Int32(nsstring.lengthOfBytes(using: String.Encoding.utf8.rawValue)) +} + +@_cdecl("ns_string_get_cstring_and_length") +public func ns_string_get_cstring_and_length(_ handle: UnsafeRawPointer, _ ptr: UnsafeMutablePointer?>, _ length: UnsafeMutablePointer) { + let nsstring = Unmanaged.fromOpaque(handle).takeUnretainedValue() + if let cstring = nsstring.utf8String { + ptr.pointee = UnsafePointer(cstring) + length.pointee = Int32(strlen(cstring)) + } else { + ptr.pointee = nil + length.pointee = 0 + } +} + +@_cdecl("ns_mutable_dictionary_new") +public func ns_mutable_dictionary_new() -> UnsafeMutableRawPointer { + let instance = NSMutableDictionary() + return Unmanaged.passRetained(instance).toOpaque() +} + +@_cdecl("ns_mutable_dictionary_get_value") +public func ns_mutable_dictionary_get_value(_ handle: UnsafeRawPointer, _ keyPtr: UnsafeRawPointer) -> UnsafeMutableRawPointer? { + let instance = Unmanaged.fromOpaque(handle).takeUnretainedValue() + + let key = Unmanaged.fromOpaque(keyPtr).takeUnretainedValue() + if let value = instance[key] as? NSObject { + return Unmanaged.passRetained(value).toOpaque() + } + + return nil +} + +@_cdecl("ns_mutable_dictionary_set_value") +public func ns_mutable_dictionary_set_value(_ handle: UnsafeRawPointer, _ keyPtr: UnsafeRawPointer, _ valuePtr: UnsafeRawPointer?) { + let instance = Unmanaged.fromOpaque(handle).takeUnretainedValue() + + let key = Unmanaged.fromOpaque(keyPtr).takeUnretainedValue() + if let valuePtr = valuePtr { + let value = Unmanaged.fromOpaque(valuePtr).takeUnretainedValue() + instance[key] = value + } else { + instance[key] = nil + } +}